The admin file-upload endpoint POST /admin/Index/upload in ApiAdmin (ThinkPHP 6-based API management platform) takes the uploaded file's extension verbatim — there is no whitelist, blacklist or content check — and move_uploaded_file() drops the file into the web-accessible directory public/upload/Ymd/ . Any logged-in admin user can upload a .php file and reach it directly over HTTP, achieving remote code execution on the server.
ApiAdmin <= 5.0 (ThinkPHP 6.0, config/apiadmin.php APP_VERSION = 5.0; last release 2020)
Any back-end account with admin panel access
Response ( apiAuth is the token carried as Api-Auth header on all later requests):
Step 2 — Upload a PHP shell
Response — the server returns the full URL of the stored file (extension .php preserved, name randomized):
Step 3 — Execute the shell
The uploaded PHP file is executed by the server — RCE confirmed. The attacker can then drop any PHP backdoor (webshell) the same way.
app/controller/admin/Index.php:11-50 — the extension is extracted from the client-supplied filename with no validation, and the file is moved into the web root:
The vulnerability is Index.php:36-38 + :42 :
Index.php:37 — array_pop() keeps whatever extension the attacker sends ( .php , .phtml , .php5 …); there is no in_array() extension check and no getimagesize() content check;
Index.php:42 — move_uploaded_file() stores it under $_SERVER['DOCUMENT_ROOT'] (= public/ ), which is directly web-served, so the file is both readable and executed as PHP.
The only hardening is a random filename ( md5(time().uniqid()) ), which does not prevent access — the fileUrl is returned to the attacker in the same response.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
