Human
Machine
REPRO-2026-00132 CRITICAL RCE
Verified
ShowDoc Unauthenticated File Upload RCE via deprecated ThinkPHP syntax
showdoc/showdoc (github) Apr 14, 2026
What's the vulnerability?
ShowDoc versions before 2.8.7 contain an unrestricted file upload vulnerability. Due to deprecated ThinkPHP 3.1 syntax ($upload->allowExts instead of $upload->exts), attackers bypass extension checks using angle bracket tricks (e.g., test.<>php) to upload PHP webshells. The endpoint /index.php?s=/home/page/uploadImg is accessible without authentication.
Root Cause Analysis
## Summary
ShowDoc v2.8.2 is vulnerable to unauthenticated file upload leading to code execution. The real endpoint `POST /index.php?s=/home/page/uploadImg` accepts a multipart file named `test.<>php`, stores it under `Public/Uploads`, and the uploaded PHP is executable. The bug is caused by deprecated ThinkPHP syntax (`$upload->allowExts`) being ignored, so extension allowlisting is not enforced in the effective upload path.
## Impact
- **Package/component affected:** `showdoc/showdoc`, `PageController::uploadImg()` and ThinkPHP upload handling (`ThinkPHP Upload->upload()`)
- **Affected versions:** ShowDoc before 2.8.7 (runtime validated on v2.8.2)
- **Risk level:** Critical
- **Consequences:**
- Unauthenticated attacker can upload executable PHP
- Uploaded file is reachable from web path (`/Public/Uploads/...`)
- Remote code execution in application context
## Root Cause
In vulnerable code, `uploadImg()` configures ThinkPHP upload with:
- `$upload->allowExts = array('jpg','gif','png','jpeg');`
- then `$info = $upload->upload();`
On ThinkPHP 3.2+, `allowExts` is deprecated/ignored and the active key is `exts`. Because of this mismatch, intended extension restrictions are ineffective in the vulnerable path. A crafted filename (`test.<>php`) bypasses the simple `.php` substring check and results in a saved `.php` payload.
**Fix commits:**
- `fb77dd4db88dc23f5e570fc95919ee882aca520a`: replaces `allowExts` with `exts` (upload blocked)
- `e1cd02a3f98bb227c0599e7fa6b803ab1097597f`: adds early `return false` in `uploadImg()` (endpoint disabled)
## Reproduction Steps
1. Run `repro/reproduction_steps.sh`.
2. The script:
- clones ShowDoc and checks out three revisions: vulnerable `v2.8.2`, fixed `fb77dd4`, fixed `e1cd02a`
- builds Docker runtime images (`php:7.4-apache`) for each revision
- initializes each instance via `install/non_interactive.php`
- sends real multipart POST to `/index.php?s=/home/page/uploadImg` with filename `test.<>php`
- for vulnerable revision, requests the uploaded PHP and verifies execution marker
- for fixed revisions, verifies upload rejection/disable behavior and absence of uploaded PHP
3. Expected evidence:
- vulnerable upload response contains success JSON + uploaded `.php` URL
- vulnerable execution response contains `RCE_OK_1e08f13e8e4695e97fef6d9de3665be4`
- fb77 shows `上传文件后缀不允许` and no uploaded PHP
- e1cd returns empty response body and no uploaded PHP
## Evidence
- Execution logs:
- `logs/reproduction_run8.log`
- `logs/reproduction_run9.log`
- Vulnerable artifacts:
- `repro/runtime_artifacts/http/vuln/upload_response.txt`
- `repro/runtime_artifacts/http/vuln/uploaded_php_files.txt`
- `repro/runtime_artifacts/http/vuln/webshell_exec_response.txt`
- `repro/runtime_artifacts/http/vuln/container.log`
- `repro/runtime_artifacts/http/vuln/controller_lines.txt`
- `repro/runtime_artifacts/http/vuln/controller_upload_flow.txt`
- Fixed artifacts:
- `repro/runtime_artifacts/http/fb77/upload_response.txt`
- `repro/runtime_artifacts/http/fb77/uploaded_php_files.txt`
- `repro/runtime_artifacts/http/fb77/container.log`
- `repro/runtime_artifacts/http/e1cd/upload_response.txt`
- `repro/runtime_artifacts/http/e1cd/uploaded_php_files.txt`
- `repro/runtime_artifacts/http/e1cd/container.log`
- Revision evidence:
- `repro/runtime_artifacts/http/vuln/revision.txt`
- `repro/runtime_artifacts/http/fb77/revision.txt`
- `repro/runtime_artifacts/http/e1cd/revision.txt`
- Summary artifact:
- `repro/runtime_artifacts/http/summary.txt`
## Recommendations / Next Steps
- Upgrade to a fixed release (>= 2.8.7) or include both fixes (`fb77dd4` and `e1cd02a`).
- Keep upload endpoint authenticated or disabled if not required.
- Enforce strict extension/MIME checks with current framework API (`exts`) and add server-level execution restrictions in upload directories.
- Add regression tests for tricky filenames like `test.<>php` and other extension obfuscation inputs.
## Additional Notes
- **Idempotency:** confirmed. `repro/reproduction_steps.sh` was run twice consecutively (`reproduction_run8.log`, `reproduction_run9.log`) with successful reproduction and consistent vulnerable-vs-fixed behavior.
- **Limitation:** requires Docker availability in the execution environment.
One Command
Verify with pruva-verify
Run the Pruva CLI to automatically fetch and execute the reproduction script.
pruva-verify REPRO-2026-00132 or
pruva-verify CVE-2025-0520 Install:
curl -fsSL https://pruva.dev/install.sh | sh Or Run Manually
1
Download the script
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00132/artifacts/reproduction_steps.sh 2
Make executable
chmod +x reproduction_steps.sh 3
Run the script
./reproduction_steps.sh Run in a VM, container, or disposable environment. This exploits a real vulnerability.
How Pruva Reproduced This
Watch the AI agent's step-by-step process.
Loading session...
Artifacts
No artifacts available