เว็บลิงค์ที่ซื่อสัตย์ php. เลเยอร์เค้กและ com_weblinks Joomla ไฟล์อื่นๆ ที่ใช้ในส่วนประกอบ
ในเดือนนี้ นักขุดบั๊กไม่ต้องการที่จะตามใจเราด้วยการหาช่องโหว่ใหม่ๆ ในแอปพลิเคชันยอดนิยม แน่นอนว่า มีการเผยแพร่คำแนะนำมากมายในผลิตภัณฑ์ของบริษัทที่มีชื่อเสียง แต่มีเพียงไม่กี่รายการที่มีรหัส PoC ที่ย่อยได้ ในการตรวจสอบของเรา ฉันพยายามรวบรวมช่องโหว่ที่สำคัญและสมบูรณ์ที่สุดที่อธิบายไว้เมื่อเร็วๆ นี้ ดังนั้นนั่งเอนหลังและสนุกกับการอ่าน
ช่องโหว่ PHP ในการจัดการคำขอ HTTP Head โดยย่อเมื่อวันที่ 3 มีนาคม Adam Ivanyuk ค้นพบคุณสมบัติที่น่าสนใจในตัวแปลภาษา PHP ซึ่งประมวลผลคำขอ HEAD ไม่ถูกต้องนัก ผู้วิจัยตั้งชื่อช่องโหว่นี้ว่า "HTTP HEAD method trick in php scripts"
ผู้เขียนโค้ดหลายคนพัฒนาสคริปต์ PHP ด้วยความหวังว่าคำสั่งทั้งหมดที่เขียนในนั้นจะดำเนินการได้สำเร็จโดยไม่ถูกขัดจังหวะกลางคัน (โดยเฉพาะในสคริปต์สั้นๆ) นี่คือสิ่งที่เกิดขึ้นหากผู้ใช้ปลายทางร้องขอสคริปต์โดยใช้เมธอด GET, POST, PUT
แต่คุณควรทราบว่ามีวิธี HTTP อื่นๆ เช่น HEAD เมื่อประมวลผลวิธีนี้ใน PHP จะทำให้เกิดช่องโหว่ด้านความปลอดภัยได้
เราดูที่หนึ่งในแหล่งที่มาของล่าม: ./main/SAPI.c, บรรทัดที่ 315:
ถ้า (SG(request_info).request_method &&
!strcmp(SG(request_info).request_method, "HEAD"))
{
SG(request_info).headers_only = 1;
...
เมื่อข้อมูลใดๆ มาถึง ฟังก์ชัน php_ub_body_write จะถูกดำเนินการ ต่อไป ดูที่ main/output.c บรรทัดที่ 699:
ถ้า (SG(request_info).headers_only) (
ถ้า(SG(headers_sent))
{
กลับ 0;
}
php_header(TSRMLS_C);
zend_bailout();
}
ที่นี่คุณจะเห็นว่าในเอาต์พุตหน้าจอแรกและเมื่อใช้เมธอด HEAD ฟังก์ชัน zend_bailout จะยกเลิกสคริปต์
เอาเปรียบตอนนี้มาเข้าถึงสคริปต์นี้โดยใช้เมธอด HEAD:
อย่างที่คุณคาดไว้ สมุดเยี่ยมของเราจะหยุดที่บรรทัด "echo $data;" ดังนั้นไฟล์ book.txt จะถูกรีเซ็ตเป็นศูนย์
ตัวอย่างนี้ค่อนข้างทำลายล้าง ในตัวอย่างที่สอง เราสามารถข้ามการให้สิทธิ์ในแผงการดูแลระบบดั้งเดิมได้:
ในสคริปต์นี้ เมื่อคุณเข้าสู่ระบบโดยใช้วิธีการปกติ ตัวแปรการดูแลระบบจะถูกตั้งค่าในเซสชัน จากนั้น หากผู้ใช้ป้อนรหัสผ่านผิด ตัวแปรนี้จะถูกตั้งค่าเป็นศูนย์ และผู้ใช้จะไม่ได้เป็นผู้ดูแลระบบ
หากเราเข้าถึงแผงการดูแลระบบผ่าน HEAD การดำเนินการจะถูกขัดจังหวะด้วยโค้ดที่มี "echo" ดังนั้นตัวแปรการดูแลระบบจะไม่ถูกรีเซ็ตเป็นศูนย์ และเราสามารถเดินไปรอบๆ ส่วนที่ปิดของแอปพลิเคชันได้อย่างปลอดภัย ที่นี่ จำเป็นต้องคำนึงว่าในเว็บเซิร์ฟเวอร์ส่วนใหญ่ ค่าของเอาต์พุตบัฟเฟอร์ถูกกำหนดเป็น 4096 ไบต์ ดังนั้นในตัวอย่างการทำงาน เราอาจต้องใช้บรรทัด 'สตริงยาวประกอบด้วยอักขระประมาณ 4090 ตัว'
เอาเปรียบ- พี.เอช.พี
ที่นี่ อาร์เรย์ $check มีข้อมูล POST ของเรา และตัวแปร $locked เป็นสตริงอนุกรมที่ถูกทำให้ยุ่งเหยิงโดยใช้ฟังก์ชัน str_rot13() ซึ่งอยู่ภายใต้การควบคุมของเราทั้งหมด
ณ จุดนี้ มันคุ้มค่าที่จะพูดนอกเรื่องสำหรับผู้ที่ยังไม่ได้อ่านบทความที่เกี่ยวข้องใน ][ และพูดคุยสั้น ๆ เกี่ยวกับข้อผิดพลาดที่แสดงออกในวิธีการวิเศษของ PHP ดังนั้นใน PHP เวอร์ชัน 5 แนวคิดพื้นฐานของการเขียนโปรแกรม OOP จึงปรากฏขึ้น: ตัวสร้างและตัวทำลาย ตัวสร้างถูกนำไปใช้ด้วยเมธอด "__construct" และตัวทำลายถูกนำไปใช้ด้วยเมธอด "__destruct" เมื่อสิ้นสุดการทำงานและเมื่อเรียกใช้ผ่านฟังก์ชัน unserialize() แต่ละอ็อบเจกต์จะรันเมธอด __ destruct ของตัวเอง ถ้ามันถูกเขียนไว้ในโค้ด
ตอนนี้กลับไปที่เฟรมเวิร์กของเราและดูที่ตัวทำลายของคลาส App จากไฟล์ ./libs/configure.php:
ฟังก์ชัน __destruct()
{
ถ้า ($this->__cache)
{
$core = App::core("เค้ก");
unset($this->__paths);
แคช::write("dir_map", array_filter($this->__paths),
"เค้ก_คอร์");
แคช::write("fi le_map", array_filter($this->__map),
"เค้ก_คอร์");
แคช::write("object_map", $this->__objects,
"เค้ก_คอร์");
}
}จากโค้ดด้านบน คุณสามารถเข้าใจได้ว่าวิธีนี้สามารถถูกบุกรุกได้โดยการเขียนค่าโดยพลการลงในวัตถุแคช คีย์ที่น่าสนใจที่สุดในการถอดรหัสคือ 'file_map' มันจัดการลิงค์ระหว่างคลาสและไฟล์ PHP ที่เกี่ยวข้องและยังใช้เพื่อโหลดคลาสเพิ่มเติมในขณะรันไทม์
รหัสจริงสำหรับการโหลดคลาสดูซับซ้อนกว่าเล็กน้อย แต่ทั้งหมดจะลงเอยที่โค้ดต่อไปนี้จากเมธอด __load ภายในคลาส App:
บิงโก! โดยการแทนที่ตัวแปร $file เราสามารถรวมโค้ด PHP ของเราเองได้! ยิ่งไปกว่านั้น นี่จะเป็นจุดบกพร่องการรวมไฟล์ระยะไกลอย่างแท้จริง ดังนั้น เราจึงไม่ต้องการเทคนิคเพิ่มเติมใดๆ ในการอัปโหลดไฟล์ในเครื่องไปยังเซิร์ฟเวอร์ อย่างไรก็ตาม ผู้เขียนช่องโหว่ที่พบแนะนำให้ LFI ใช้ประโยชน์จากช่องโหว่นี้ เนื่องจาก CakePHP ใช้ไฟล์แคชในเครื่อง ซึ่งอยู่ในรูปแบบซีเรียลไลซ์ในไดเร็กทอรีที่แคร็กเกอร์รู้จัก
เอาเปรียบในฐานะ PoC ขนาดเล็กสำหรับสร้างสตริงซีเรียลไลซ์ที่เป็นพิษ เฟลิกซ์แนะนำโค้ดต่อไปนี้:
แน่นอน คุณต้องรวมคลาสที่จำเป็นจาก CakePHP ก่อน นอกจากนี้ยังมีการใช้ประโยชน์จาก Python ที่มีคุณลักษณะครบถ้วน ซึ่งคุณสามารถดูได้ที่ malloc.im/burnedcake.py
พล็อตนี้ควรใช้งานได้ในทุกแอปพลิเคชันที่สร้างด้วย CakePHP ที่ใช้แบบฟอร์ม POST พร้อมโทเค็นความปลอดภัยและไม่ได้เปลี่ยนตำแหน่งไฟล์แคชเริ่มต้น ตามค่าเริ่มต้นช่องโหว่จะแสดงการกำหนดค่าฐานข้อมูล ประโยชน์อื่น ๆ สามารถเพิ่มได้อย่างง่ายดายโดยการเปลี่ยนเพย์โหลด PHP ในตัว
เป้าหมาย- CakePHP getState("filter_order_dir");
$filter_order = JFilterInput::clean($filter_order, "cmd");
$filter_order_dir =
JFilterInput::clean($filter_order_dir, "คำ");
// เราต้องได้รับรายชื่อทั้งหมด
// เว็บลิงค์ในหมวดหมู่ที่กำหนด
$query = "เลือก *"
" จาก #__เว็บลิงก์" .
" ที่ไหน catid = ". (int) $this->_id.
" และเผยแพร่ = 1" .
"และเก็บถาวร = 0"
" สั่งโดย ". $filter_order "".
$filter_order_dir .", สั่งซื้อ";
ส่งคืน $query;
}ที่นี่ คุณจะเห็นว่าตัวแปร $filter_order และ $filter_order_dir ไม่ได้รับการตรวจสอบความสอดคล้องอย่างเข้มงวดกับคำสั่ง SQL แต่จะตรวจสอบโดยใช้วิธีการล้างมาตรฐานจากคลาส JFilterInput เท่านั้น:
- CakePHP getState("filter_order_dir");