Lâu rồi mình cũng không viết writeup, hôm nay có giải inctf 2021 rating 70 với mình thấy challenge cũng hay nên mình viết lại một số bài mà mình giải quyết được. Source code và payload của các bài mình giải được mình bỏ ở đây nhé. SOURCE
Bài này là một bài xss và được viết bằng golang. Mình sẽ tập trung vào file server.go
.
Cụ thể ở function createHandler
.
Mỗi bài mà chúng ta tạo đều có hash riêng và nếu như hash đó bằng với hash_admin thì không có sanitize
.
Function sanitize
Ở func này chỉ có chức năng là EscapeString (htmlencode) chống mình xss.
=> Mình chỉ cần tìm ra admin_hash
thì có thể nhảy qua được sanitize
và xss bình thường.
Sau một hồi stuck vì không biết kiếm cách nào để lấy được admin_hash
thì đọc func save_post
postid được gen bằng ((CONFIG.seed * CONFIG.a) + CONFIG.c) % CONFIG.modulus
Có thêm 1 api là _debug
. Access nó thì ta lấy được {"Admin_bucket":"b5cd7ae0-7b50-7ae0-7ae0-47a03b473015","VAL_A":245,"VAL_B":143}
.
Từ những data này thì ra có thể brute ra postid và dựa vô đó đọc solution của người khác =)) hay còn gọi là chôm flag và đây cũng là unintended của bài này.
Và sau đó mình thấy được 1 solution có chứa web-hook và mình nhảy vô và đọc được flag.
Flag -> inctf{8d739_csrf_is_fun_3d587ec9}
Solution chính thức ở đây như mình đã nói là kiếm admin_hash
, sau đó xss bình thường.
Func này tạo admin_hash nhưng khi gọi getadminhash()
trong khi CONFIG chưa khởi tạo xong => admin_hash blank.
Đây là script của author, mọi người có thể tham khảo.
Link script
Payload solved bot
Được cung cấp một Dockerfile thì chúng ta đọc nó thôi hehe
Thấy được có nơi lưu code để deploy là /code
Ở ô input thì chúng ta phải nhập một url. Ở đây mình thử nhập 1 protocol là file://
với hi vọng tải được file source về.
Lần 1: file:///code/app.py
-> tải được file app.py
Ở trong phần import thư viện thì mình thấy from main import Requests_On_Steroids
, vậy nên thử tải luôn main.py
Lần 2: file:///code/main.py
-> tải được file main.py
File app.py
Đầu tiên ta thấy server sử dụng redis
.
Ở đoạn code này nếu như user_isAdmin = yes
thì mình flag sẽ được set cho cookie có name là flag
, mặc định sẽ là user_isAdmin = false
và cookie flag được đặt là NAAAN
.
File main.py
Ở file này nó mount schema vậy mình chỉ cần gopher vô redis set uid của mình là isAdmin = yes là có flag (SSRF)
inctf://redis:6379/_set taidh_isAdmin yes
Sau đó truy cập lại với cookie uid=taidh sẽ có flag trả về ở cookie
Flag -> inctfi{IDK_WHY_I_EVEN_USED_REDIS_HERE!!!}
Đăng nhập với account bất kì sau đó thấy có chức năng upload và download file về.
F12 thì thấy được có /source
=> được cung cấp source. hehe
/return-files
có thể LFI và từ đó chúng ta có thể đọc các file.
Nó chỉ check nếu trong tham số f
nếu có ..
hay không.
/dev_test
nhận url mà ta cung cấp và có function url_validate
để check url đó.
Để bypass những cái trên thì mình đã urlencode đầu vào. Vì sau khi check hết tất cả các blacklist thì nó sẽ unquote
(decode) và truy cập vào url.
Access http://127%2E0%2E0%2E1
ở dev_test
thì nhận được mã nguồn php.
Ở file này thì có 2 tham số là part1
và part2
.
Để thực hiện được query3
thì chúng ta cần vượt qua if($check || !(strlen($_GET['part2'])<124))
Chỉ cần câu query2
-> lỗi và len của part2
< 124.
Để câu query2
lỗi thì chúng ta chỉ cần thêm '
. Nhưng preg_match đã filter. Chú ý kĩ hơn thì thấy sau khi check qua preg_match thì sẽ $inp=urldecode($inp);
decode. vậy chúng ta chỉ cần encode '
=> vượt qua được preg_match và vừa làm câu query2
lỗi.
Điều kiện còn lại thì chỉ cần len của part2
< 124 là xong.
Sau khi một hồi tìm thì không thấy flag ở trong db và phải inbox author hỏi. Anh ấy bảo chỉ cần tìm path flag ở trong db. Tới đây mình nghĩ vậy ở trong db có path flag sau đó chỉ cần sử dụng LFI để đọc.
Chú ý ở câu query1
được comment
Chúng ta có table adminfo
và có 2 cột name và path. Có lẽ brute path ở đây.
Mình đã sử dụng LFI để đọc file /etc/hosts
và nhận được host local là 192.168.48.2
và mình sử dụng nó luôn hehe
Sau đó sử dụng path flag và brute được vào chỗ. /return-file?f=/path_flag
.
Flag -> inctf{y0u_pr0v3d_th4t_1t_i5_n0t_53cur3_7765626861636b6572}
Ở bài này được cung cấp all source. Server sẽ kill trong 10 phút, nên mình tự deploy local để test.
Đầu tiên, sẽ có phần upload file, nhưng để upload được thì cần phải có pin code. Ở /waf
có vẻ như là sẽ tạo pincode.
Đọc code thôi hehe. file /waf/waf.py
Nhận tham số role
và sẽ được check một số thứ như. Nếu có superuser
sẽ replace thành blank. len của role không được > 30.
Để nhận được pincode thì cần user == admin
và role == superuser
.
data mặc định sẽ là name=user
và role là thứ mình truyền vào. Nhìn vào thì mình có thể escape ở role này và thêm name
.
Sau khi thêm vào thì data sẽ được biến thành cấu trúc json và sử dụng ujson.load(data)
để load dữ liệu.
Vì mình gặp json khá nhiều nên sau khi đọc tới đoạn ujson.loads(data)
thì mình nhớ đến bài viết này
Document JSON
=> sử dụng unicode thể bypass các thứ trên để tạo role=superuser
và add thêm name=admin
.
Tiếp tục qua file app.js
.
Ở đây chỉ có chức năng tải lên 1 file .json
sau đó được copy vào tệp package.json
và cuối cùng được load bằng var config = require('config-handler')();
.
Đầu tiên thì thấy được config-handler
có thể tấn công Prototype_Pollution
.
Ngồi đọc code một hồi thì không thấy có gì lạ và exploit chỗ nào. Bỗng dưng thấy thư viện squirrelly
này khá lạ và bắt đầu tìm hiểu về nó và thấy được có một CVE gần đây và cùng version và server đang sử dụng.
CVE-2021-32819
Để hiểu hơn thì bạn có thể đọc bài phân tích về CVE đó nhá.
Ở đây mình lấy luôn payload của họ và sửa lại và thêm prototype pollution để exploit.
payload genpin -> super\u0075ser","name":"admin
file upload reverse shell
Nhớ thay HOST và PORT của các bạn nhé.
Flag -> inctf{Pr0707yp3_P011u710n5_4r3_D34dly}