"Vẫn như thường lệ, khi có CTF nào của trường khác thì mình luôn cố gắng đi xin challenge để về làm và học hỏi những điều mới mẻ. Lần này có giải KMA CTF lần 1 chọn đội tuyển SVATTT mình lại tiếp tục ngỏ lời với những người bạn để xin đề giải được 4 challenge dưới đây." - Trích văn từ sư phụ TaiDH hê hê :v
Category | Challenge Name |
---|---|
Web | Vào đây! |
Web | Jo`in Le'm |
Web | Flag Holder |
Web | Ninja Shop |
Giả sử mình có một dbs như sau. Với một truy vấn bình thường
Nhưng khi mình sử dụng truy vấn SELECT * from demo where name = 'Test';
thì kết quả vẫn trả về tương tự
Vậy nên ta sẽ dùng user = Admin
để bypass và truy vấn vẫn đúng.
Còn về password như đã nói ở trên và bài blog. Payload của mình như sau:
Đây là một bài php đã obfuscate code. Việc đầu tiên ta cần làm là sửa lại cho nó chỉnh chu dễ hiểu tý :v
Mình sửa lại như sau:
curl_escape($ch, $_GET["url"]) === urlencode($_GET["url"]
và dòng 31 check request vào local hay không.curl_escape
và urlencode
(Follow đúng của code là như vậy, mình build lại code cho đẹp nhưng chưa đúng thứ tự thực thi :v Các bạn xem tạm hehehe)Chú ý ở đây curl_escape
sẽ encode string theo chuẩn RFC 3986 còn để ý trong phần mình khoanh đỏ thì urlencode
sẽ xử lý khác đi một chút về ký tự space.
Một cái sẽ thành + và một cái là %20
Đến đây thì chúng ta đã gần như bypass được bằng việc sử dụng space. Nhưng khi request ?url=http://google.com
. Việc thêm space sẽ làm cho request không hợp lệ. Vậy thì làm sao.
Ý tưởng mình tiếp tục sử dụng thêm một ký tự nữa đó là # (fragment url)
Việc sử dụng # kết hợp space sẽ trở thành http://google.com%23%20
,như vậy chúng ta vừa bypass được vừa loại bỏ đi space để trở thành một url request hợp lệ
Và trong source code:
Thì mình mới nảy ra ý tưởng. Path traversal các file log của Apache để RCE. Trong đó có lưu User-Agent (Cái này mình control được nên có thể viết code php vào).
Trong http request có SESSION cookie nhưng của bài khác :v mình chưa xoá nên nó có lưu lại
Khi nhận một url status code > 300 và <400 (302 chuyển hướng) thì url của mình sẽ curl thẳng.
Mình không có vps để deploy lên một trang web đặt header 302. Nên dùng requestrepo, trang web có hỗ trợ.
Qua phần response sử như sau:
Và send thử:
Vậy là thành công. Vì sao mình lại đặt thêm Location và file:///etc/passwd
Ở trong source code sẽ check xem url mình đưa vào là một url có http hoặc https hay không. Nếu không sẽ bị check và thoát luôn.
Như vậy thì ta không sử dụng được wrapper file:// trực tiếp. Việc sử dụng 302 chuyển hướng
The Location response header indicates the URL to redirect a page to. It only provides a meaning when served with a 3xx (redirection) or 201 (created) status response.
Ta sẽ lợi dụng nó để sử dụng file://
Đến đây mình thử lại xem có đọc được flag hay là file log không. Nhưng mọi thứ đều vô vọng :D
Sau đó tác giả lại hint là không cần RCE khiến cho mình nửa vời. Đoán không được mà RCE cũng không được.
Stuck mất mấy tiếng rồi mới có hint là:
Sau khi thấy hint thì đây là payload của mình:
/render?template=Hello+{variable}&variable={%print(1)%}
thì mình nhận thấy có vẻ được đấy :v nên ngồi lì mãi cái cách để SSTI và không thành công :vif word in string.lower()[:MAX_LENGTH]
string input vào của mình sẽ chuyển về ký tự thường bởi hàm lower() và check 20 ký tự đầu, trong khi input vào cũng chỉ cho max 20. Vậy thì giả sự kiếm được mình sẽ pass qua được phần kiểm tra và không quá 20 ký tựỐ kề vậy là xong rồi, mình sẽ dùng ký tự đó 10 lần (lower sẽ thành 20) và mình còn 10 char để làm gì đó. Nhận thấy biến FLAG được gắn giá trị của FLAG. Chỉ cần gọi nó ra là xong thôi :v Mình viết script để gửi lên chứ Burp xử lý unicode khá tệ.
Bài này mình làm trong thời gian khá lâu vì đọc docs không kỹ và non là chính…
INSERT INTO coins(coin, uid) VALUES (100, %d)', (int)$uid
new_balance
của mình đưa vào sẽ không bị convert về int, hehe vậy chỗ này sẽ rất mlem.SELECT fullname FROM users WHERE username='%s' limit 0,1", $_SESSION["username
, không thể dùng SET để lưu biến, vì như mình đã nói không thể stack query.
Như vậy thì mình sẽ lưu được giá trị 1337 vào @x, trong đó 1337 là fullname mà mình input, và username='cc' là một username đã tồn tại. Thì câu query đúng sẽ thực hiện và lưu giá trị.
Đầu tiên như ý tưởng đã sẽ cần tạo một username sẵn trước đã.
Tiếp đó sẽ tạo account thứ 2 có fullname=1337 và username là db' INTO @x -- -
Nhưng khoan, 1337 sẽ bị check, nếu sử dụng hex thì fullname sẽ là X'31333337'
= 11 ký tự sẽ bị check, vì chỉ được input 10 ký tự ở fullname(tác giả tính cả rồi)
Vậy thì mình sẽ đặt là 1338 rồi mua một quí bửu, 1338-1 coin thì về đúng 1337 rồi :v
Sau khi register acc thứ 2 xong ta sẽ login và reset coin lại theo biến @x là được.
F5 lại
Hehe đi mua 1 thằng quý bửu thôi
Coin = 1337 giờ mua flag
Cảm ơn các tác giả với những thử thách trên, và trường KMA đã tạo ra một sân chơi CTF để bọn mình cơ cơ hội luyện tập.
Cảm ơn ông bạn @Deku đã chia sẻ đề cho mình.