Loading HuntDB...

Server-Side Request Forgery (SSRF) via Game Export API

Critical
L
Lichess
Submitted None
Reported by oblivionsage

Vulnerability Details

Technical details and impact analysis

Server-Side Request Forgery (SSRF)
# Summary Hello Lichess Team, I found a Server-Side Request Forgery vulnerability in the game export functionality. An attacker can make the Lichess server send HTTP requests to arbitrary URLs by manipulating the `players` parameter. This works on public endpoints that don't require any authentication # Description: The issue is in the game export API endpoints that accept a `players` parameter. When I looked at the code, I noticed that this parameter gets passed directly to `RealPlayerApi.apply()` without any URL validation Here's the problematic flow I found: In `app/controllers/Game.scala`: ```bash val config = GameApiV2.OneConfig( format = GameApiV2.Format.byRequest, imported = getBool("imported"), flags = requestPgnFlags(extended = true), playerFile = get("players") // User input taken directly ) ``` Then in `modules/api/src/main/GameApiV2.scala`: ```bash realPlayers <- config.playerFile.so(realPlayerApi.apply) ``` And finally in `modules/web/src/main/RealPlayer.scala` ```bash def apply(url: String): Fu[Option[RealPlayers]] = cache.get(url) // This leads to: ws.url(url).withRequestTimeout(3.seconds).get() ``` The `get("players")` method directly reads from HTTP query parameters without any validation. The `so()` method executes the function when the Option contains a value, so any URL I provide gets passed straight to the HTTP client # PoC Note: I discovered this vulnerability through source code analysis and then confirmed it by testing live on the official Lichess website. The following PoC demonstrates the issue as it exists in the production environment 1. Pick any valid game ID from Lichess (like from a recent game) {F4390928} 2. Make a request to: `/game/export/[GAME_ID]?players=http://169.254.169.254/latest/meta-data/ ` 3. The server will make an HTTP request to the provided URL 4. You can also test with: `/api/games/export/_ids?players=[URL]` or `/api/games/user/[USERNAME]?players=[URL] ` 5. Monitor your server logs or use a service like webhook.site to confirm the request {F4390944} {F4390945} # Mitigation: To fix this, you should: + Add URL validation to only allow specific trusted domains for the `players` parameter + Implement a whitelist of allowed URLs or URL patterns + Consider removing the feature entirely if it's not critical + Add authentication requirements for these endpoints + Block requests to private IP ranges (127.x.x.x, 192.168.x.x, 10.x.x.x, 169.254.x.x) (For security and ethical reasons, I did not send any actual requests to these IP addresses. If required, I can provide my own IP address or any additional information for verification purposes) The fix should be in `RealPlayerApi.apply()` method to validate the URL before making any HTTP requests. https://cwe.mitre.org/data/definitions/918.html ## Impact An attacker could use this to: + Access cloud metadata services (AWS, GCP) to steal credentials and configuration + Scan internal networks and discover internal services + Access internal APIs and admin panels that aren't exposed to the internet + Potentially read sensitive internal data or configuration files + Perform port scanning of internal infrastructure This is particularly concerning because the endpoints are public - no authentication needed. Also, since it's on a high-traffic site like Lichess, an attacker could potentially use this to bypass IP-based restrictions on internal services

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted

Weakness

Server-Side Request Forgery (SSRF)