# Leaking File Contents with a Blind File Oracle in Flarum

Flarum passed user-controlled avatar upload data into Intervention Image's make(), enabling file:// reads and blind SSRF; php://filter oracle leaked arbitrary local file contents.

- Payout: Research
- Severity: high
- Program: Flarum
- Credited: Assetnote
- Disclosed: 2025-02-12
- CVE: CVE-2023-40033
- Write-up quality: 8/10
- Original: https://www.assetnote.io/resources/research/leaking-file-contents-with-a-blind-file-oracle-in-flarum
- Library page: https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-leaking-file-contents-with-a-blind-file-oracle-in-flarum
- Tags: domain:web impact:data-leak impact:internal-access kind:research platform:research sev:high tech:php technique:blind technique:source-review vuln:info-disclosure vuln:ssrf

**Target:** PHP forum software avatar upload endpoint /api/users/{id}/avatar
**Root cause:** UploadAvatarHandler passes the uploaded file object (with __toString) into Intervention Image's ImageManager::make(), which treats URL-like strings as remote sources and calls file_get_contents without validation, accepting file:// and php:// wrappers.
**Steps:**
1. Authenticate as any basic user and POST to `/api/users/{id}/avatar` with multipart field `avatar` containing a URL string such as `file:///etc/passwd` or `http://localhost:8100/favicon.ico`.
2. Observe that make() dispatches to initFromUrl and calls `file_get_contents($url)` with attacker-controlled URL.
3. For file leak, supply `php://filter/convert.base64-encode|convert.iconv.latin1.UTF-32|.../resource=/etc/passwd` chained with `dechunk` to build a blind oracle.
4. Use repeated iconv conversions to blow up string size; a 500 response indicates the base64 first char was in 0-9a-fA-F, absence of 500 indicates otherwise.
5. Iterate with character-swapping iconv filters to recover each base64 character and reconstruct the file.
**How found:** Enumerated routes.php for non-admin authenticated endpoints, found the avatar upload route, read UploadAvatarHandler and noticed ImageManager::make() accepts many source types including URLs; reading Intervention Image's AbstractDecoder revealed file_get_contents on user input, prompting the php://filter oracle idea from DownUnderCTF 2022.
**Exploitation:** Any registered user uploads a crafted 'avatar' whose string content is a php://filter chain pointing at a local file. The library reads the file, applies base64-encode and dechunk, then repeated latin1->UTF-32 conversions inflate the data until memory exhaustion yields a 500. The presence/absence of the 500 acts as a bit oracle; iterating iconv character rotations recovers the full file contents. Same primitive yields blind SSRF via http:// URLs.
**Escalation:** Chained the file-read oracle with SSRF to reach internal services; arbitrary file read can expose secrets/config leading to further compromise.
**Insight:** When a library's factory method accepts 'string' inputs, check whether an uploaded file object's __toString() reaches it — PHP stream wrappers (file://, php://filter) turn a benign image loader into a file-read/SSRF primitive; the dechunk+iconv blowup oracle converts blind reads into full file disclosure.
