Escalate LFI to RCE Techniques [Thai]

บันทึกเป็นความจำ และหวังว่าคนที่อ่านจะได้ประโยชน์จากเทคนิคต่างๆนี้ด้วย

ก่อนอื่น ถ้าพบว่าในรูปที่ผมแปะอธิบายไว้ มีข้อมูล private หรืออะไรที่ควรปิดไว้ ขอให้มากระซิบบอกนะครับ จะเป็นพระคุณอย่างยิ่ง (ทั้งหมดนี้รันใน VM ส่วนใหญ่ไม่น่าจะมีปัญหาอะไร)

จะไม่พูดถึงว่า Local File Inclusion คืออะไร ลองไปอ่านเองได้ตาม google และจะไม่ได้พูดถึงการ bypass ด้วย truncation หรือการ bypass rules อะไรมาก เน้นไปที่การพยายามใส่ shell เข้าไปเพื่อทำให้ Local File Inclusion read พัฒนาไปเป็น RCE

ความแตกต่างระหว่าง Local File Inclusion อ่านไฟล์ /etc/passwd กับการแสดง RCE คือความรุนแรงที่เกิดขึ้น ความน่าสนใจในการหาช่องโหว่ การดึงดูดความสนใจจากผู้บริหาร (ซึ่งสำคัญมาก บอกว่ามีช่องโหว่เพราะอ่าน /etc/passwd ได้กับบอกว่ามีช่องโหว่ทำให้โดนยึด server ได้ ความรู้สึกต่างกัน) และ bounty ที่เยอะขึ้น XD

File Upload

อันนี้ค่อนข้างตรงตัว แต่ก็ยังมี technique ต่างๆอีกพอสมควร แต่สำคัญมากคือต้องเข้าถึง dir ไฟล์นั้นโดยตรงได้ ถ้า dir/filename มีลักษณะ random และถูกครอบด้วย indirect object reference อีกที วิธีนี้หมดสิทธิและจะไม่พูดถึง พูดถึงกรณีเข้าถึง dir ของ image ที่ upload ได้อย่างเดียว

  • กรณี upload .php เข้าไปได้ ไม่จำเป็นต้องมีช่อง LFI ก็ RCE ได้เลย
  • กรณีที่เช็คแต่นามสกุลไฟล์อย่างเดียว แล้ว upload เป็น .jpg ไว้บน server อันนี้เราจะเรียกตรงๆไม่ได้แบบข้อตะกี้แล้ว ต้องใช้ LFI เข้าช่วย (ซึ่งตรงนี้ สามารถ bypass การตรวจจับนามสกุลต่างๆได้ เช่น Path Truncation)
  • กรณีที่มีการเช็คไฟล์ด้วยว่าเป็น image จริงหรือไม่ เราอาจจะสามารถ bypass การตรวจสอบได้ เช่น ใช้ประโยชน์ของ exif (กรณีเป็นไฟล์ jpeg) โดยยัดโค้ดเข้าไปในส่วน comment
  • กรณีที่ server ตรวจสอบ exif ด้วย ผมจะแสดงให้เห็นว่าจริงๆไม่จำเป็นต้องการ exif เลย ผมคิดว่า php include นั้นเป็นเสมือน include header ในภาษา c (อันนี้ผมสรุปจากการที่ผมทดลองเอง) คือการยกข้อมูลในไฟล์มาแปะตรงๆ ดังนั้นถ้าในข้อมูลในไฟล์นั้นมีข้อมูลที่สามารถแปลเป็น php อยู่ ก็สามารถถูกโจมตีได้ทั้งนั้น ตามตัวอย่างนี้ผมใช้ไฟล์ bmp (ซึ่งแก้ hex ได้ง่าย) จะเห็นว่ารูปนั้นดูดีงามทุกอย่าง แต่ในข้อมูลรูปนั้นมีส่วนที่ถูก interpret เป็น php ได้ด้วย ถ้าเกิดรูปนั้นถูก resize ก่อนเก็บบน server จะทำให้การโจมตีนี้ยากขึ้น แต่ผมคิดว่าถ้าเรารู้ว่า algorithm ว่าการ resize นั้นทำงานยังไง ก็โจมตีได้เช่นกัน

PHP Wrapper ต่างๆ

ที่จริงตรงนี้หลายอันน่าจะเป็น Remote File Inclusion มากกว่า แต่ก็รวมเข้าไปด้วย เพราะจุดที่ต้องค้นหาส่วนใหญ่มันไม่ต่างกับ Local File เท่าไร ผมไม่ได้ทดลองหรือแสดงให้ดู เนื่องจากเห็นว่าต้องไปปรับ config ของ server พอสมควร O_o ผู้ที่สนใจลองเล่นจาก root-me.org แทนได้ ถ้าวันหลังว่างๆจะมาเติมเต็มส่วนนี้อีกที

ถ้าจะต่างกับ LFI ปกติก็ตรง LFI นั้นถูกโจมตีผ่าน include() แต่อันนี้จะผ่าน function อื่นๆ เช่น fopen, file_get_content, etc

อย่างที่บอกยังไม่ได้ทดสอบ อาจจะมีผิดพลาดส่วนนี้ได้ ทักมาให้แก้ไขได้นะครับ

  • expect wrapper ก้ RCE ตรงๆได้เลย -..-

view.php?page=expect://ls

  • file wrapper (default php wrapper) ดูไม่ค่อยมีประโยชน์พิเศษอะไรเท่าไร น่าจะใช้สำหรับ bypass security check บางตัวเท่านั้น

view.php?page=file:///tmp/upload/shell.php

  • php wrapper

view.php?page=php://input&cmd=ls

(in POST param) <?php system($_GET[‘cmd’]);?>

  • zip wrapper สำหรับกรณีมีให้ upload เป็น zip ได้ เราเริ่มด้วยสร้าง zip ที่มีไฟล์ file_in_zip ที่มี php code สำหรับการโจมตี. upload zip ขึ้น server จากนั้นรันมันด้วย wrapper นี้ (ตอน zip ชื่อไฟล์มีนามสกุลอะไร ก็ต้องเรียกด้วยนามสกุลนั้น อาจจะ .php หรืออาจจะไม่มีก็ได้)

view.php?page=zip://path_to_zipfile.zip%23file_in_zip

view.php?page=phar://path_to_phar_file/file_in_phar

  • น่าจะมีตัวอื่นอีก แนะนำผมได้นะครับ กำลังหาอยู่ T T

Shell Injection ด้วยทางอื่นๆ

  • via /proc/self/environ
  • via /proc/self/fd

อันแรกเป็น user-agent header ส่วนอันที่สองเป็น process log ซึ่งลองแล้ว ไม่เห็นจะได้เลย T T คิดว่าเพราะ server ใหม่ตั้งค่า default ไม่ให้เข้าถึงส่วนนี้ได้แล้ว บรัย แต่จดไว้ก่อน ไม่เสียหาย server เก่าๆในโลกก้มีเยอะแยะ O_o

  • ผ่านทาง Email เช่น อนุญาตให้ mail ไปหา www-data ได้
  • ผ่านทาง access log และ error log ของ apache และ nginx อันนี้ให้ inject ผ่านทาง user-agent แทน เพราะบน url มันจะโดน encode เรียบร้อย ทำให้ inject < และ > เข้าไปไม่ได้ วิธีนี้ดีงามที่สุดแล้ว เพราะเป็น default ของ server เลย XD แต่ก็สามารถถูกป้องกันได้ เช่น

http://stackoverflow.com/questions/21395304/nginx-disable-logging-for-certain-user-agents

  • ผ่านทาง log อื่นๆ ซึ่งข้างล่างทั้งหมดนี้ จะสามารถทำได้ เมื่อ admin ของเครื่อง server ชอบรันทุกอย่างเป็น root (อ่านได้ทุกไฟล์) ทำให้ process web server นั้นสามารถเข้าไปอ่าน log ต่างๆได้ เราจึง inject shell เข้าไปในพวกนี้ได้ด้วย (เผื่อกรณีข้างบนไม่ผ่าน)
  • ถ้าตั้ง permission ถูกต้อง ไม่ได้รัน web server ด้วย root วิธีข้างล่างจากนี้ทั้งหมดจะไม่สามารถทำได้ครับ โดยจะ error เพราะติด permission denied แทน
  • /var/log/mail.log จะเห็นว่าผมไม่จำเป็นต้องส่งไปหา www-data ละ แค่พยายามให้มันโผล่ใน log ก็เพียงพอ (อันนี้ขี้เกียจไปตั้งให้มันรันเป็น root เลยแค่ add เข้า group adm แทน)
  • /var/log/auth.log ตรงนี้ สามารถ injection shell ผ่าน sudo, su, ssh ได้ทั้งหมด คือถ้าเปิด port ssh ไว้ ต่อให้ผม ไม่รู้ username, password แค่ inject ให้เข้าไปอยู่ใน log ได้ และมี LFI อีกหนึ่ง แค่นี้ก้พอแล้ว

คงไม่สรุปอะไรมากมาย ก็ให้เห็นวิธี escalate จาก LFI ธรรมดาไปเป็น RCE ได้ด้วยช่องโหว่ในการ upload, การเปิด attack surface มากเกินไป (ใช้ wrapper ต่างๆ) และ system misconfiguration (อย่ารันด้วย root !!)

เปิดรับคำแนะนำหรือคำอธิบายเพิ่มเติมเสมอนะครับ

- Jan 29, 2017 -