Cookie Arena CTF Season 1 – Webs Writeup

Người hàng xóm Cookie Hân Hoan gần đây đã tổ chức một kỳ CTF rất xịn xò mang tên Cookie Arena kéo dài từ ngày 31/10 đến hết ngày 3/11. Cyber Space rất hân hạnh được tham gia và writeup cho một số thử thách về Web trong kỳ CTF này. Ad gà lắm, các bạn đừng ném đá nha!

Web Basic

Sause

Trang web khá là đơn giản:

Và như các bạn đã có thể đoán từ tên thử thách, chúng ta cần kiểm tra mã nguồn của trang:

Ez 1 point phải không?

Flag{Web_Sause_Delicious}

Hân Hoan

Với thử thách này, chúng ta được cho một form đăng nhập:

Đăng nhập bằng tên đăng nhập và mật khẩu bất kỳ, chúng ta nhận được thông báo mình không phải CookieHanHoan:

Kiểm tra cookie của trang, chúng ta có thể thấy mình đang là Guest:

Thử sửa cookie này thành CookieHanHoan và nhấp Refresh, chúng ta đã có flag:

Đổi Cookie
GG EZ
Flag{Cookies_Yummy_Cookies_Yammy!}

Header 401

Hmn… Trang web đề bài không có gì đặc biệt:

Trong mã nguồn của trang, chúng ta thấy có thông tin được dùng để đăng nhập:

Đề bài có nhắc tới tên phương thức GET, và có thể nếu ta đổi phương thức này thành cái khác, flag màu nhiệm sẽ hiện ra. Để đổi phương thức, chúng ta sử dụng Burp Suite. Đầu tiên ta cần vào tab HTTP History và thêm request vào Repeater để chỉnh sửa request:

Sau đó đổi phương thức GET thành POST:

Bấm Send, và server báo lỗi thiếu header Basic Authorization:

Các bạn có thể tham khảo cú pháp của header Authorization tại đây. Vậy header basic authorization của chúng ta có cú pháp như sau:

Authorization: Basic <thong_tin_dang_nhap>

Lưu ý nhỏ ở đây, trường thong_tin_dang_nhap không phải username:password mà là giá trị base64 của nó. Như vậy thong_tin_dang_nhap của chúng ta là giá trị base64 của chuỗi gaconlonton:cookiehanhoan, chính là Z2Fjb25sb250b246Y29va2llaGFuaG9hbg==. Thêm header này vào request và gửi lại:

Noice! Flag đây rồi:

Flag{m4g1c@l_h34d3r_xD}

JS B**p B**p

Đề bài cho chúng ta một form đang nhập gồm 3 trường username, password và userrole:

Trong mã nguồn, chúng ta nhận thấy đoạn code javascript kiểm tra thông tin đăng nhập:

const form = document.getElementById('login'); 
  form.addEventListener('submit', function (event) {
    event.preventDefault();

    let username = form.elements['username'].value;
    let password = form.elements['password'].value;
    let role     = form.elements['role'].value;

    if (verifyUsername(username) && verifyPassword(password) && verifyRole(role)) {
      event.target.submit();
    } else {
      alert('Wrong Credentials!')
      location.reload(true)
    }
  })

Đoạn code lấy thông tin username, password userrole lần lượt lắp vào các hàm verifyUsername(), verifyPassword()verifyRole(). Tuy nhiện đoạn code này lại không khai báo các hàm này. Vậy chắc hẳn các hàm đó đã được khai báo tại các file js được đính kèm phía trên. Kiểm tra xem các file js này có gì nào:

Bruh… Một đống code JSF*ck :)) Đương nhiên mình sẽ không giải mã tất cả chỗ này đâu. Còn một cách khác nhanh hơn để kiểm tra nội dung các hàm verifyUsername(), verifyPassword()verifyRole(). Trong Javascript có tồn tại một hàm rất hay là toString(). Gọi nó và nó sẽ trả lại một chuỗi toàn bộ mã nguồn của hàm bất kỳ. Để mình biểu diễn cho các bạn xem. Để kiểm tra nội dung hàm verifyUsername(), ta sẽ gõ dòng này vào console:

console.log(verifyUsername.toString())

Vậy hàm này kiểm tra xem username có phải “cookiehanhoan” không. Chúng ta kiểm tra nội dung hàm verifyPassword():

Hàm này kiểm tra xem password chúng ta nhập có phải đảo ngược của chuỗi dr0Wss@p3rucreSr3pus hay không, nói cách khác mật khẩu của chúng ta là sup3rSercur3p@ssW0rd. Hàm cuối cùng là verifyRole():

Hàm này thì hơi phức tạp hơn tẹo:

function verifyRole(role) {
    if (role.charCodeAt(0) != 64) {
        return false;
    }
    if ((role.charCodeAt(1) + role.charCodeAt(2) != 209) && (role.charCodeAt(2) - role.charCodeAt(1) != 9)) {
        return false
    }
    if ((role.charCodeAt(3).toString() + role.charCodeAt(4).toString() != "10578") && (role.charCodeAt(3) - role.charCodeAt(4) != 27)) {
        return false
    }
    return true
}

Chữ cái đầu tiên của Role phải có giá trị ascii là 64, và đó chính là ký tự @. Tiếp theo đoạn code kiểm tra tổng và hiệu 2 giá trị Ascii của 2 ký tự tiếp theo. Áp dụng toán lớp 5, chúng ta tìm được ký tự thứ 2 và 3 có giá trị ascii là 100 109, tương ứng với dm. Đoạn tiếp theo ghép giá trị ascii của ký tự thứ 4 và 5 thành chuỗi “10578“, và hiệu 2 giá trị này là 27. Chúng ta có thể dễ dàng tìm thấy giá trị ascii của 2 ký tự này lần lượt là 105 78, tương ứng với iN. Vậy UserRole là @dmiN. Sử dụng cả 3 thông tin đã tìm được để đăng nhập:

Bùm!

Flag{JAV-ascript_F*ck}

Impossible

Có vẻ ta chỉ cần nhập đúng một mật khẩu duy nhất:

Để xem mã nguồn có gì nào:

Đoạn code này được sử dụng để kiểm tra mật khẩu. Mật khẩu chúng ta nhập sau khi được xóa bỏ đoạn “cookiehanhoan“, sẽ được đem đi mã hóa base64 và so sánh với Y29va2llaGFuaG9hbg==, mà đoạn này giải mã ra cũng chính là “cookiehanhoan” luôn. Nói cách khác, mật khẩu chúng ta nhập sau khi lược bỏ đoạn “cookiehanhoan” vẫn phải có giá trị là “cookiehanhoan” thì mới hợp lệ. Hmn… nghe có vẻ impossible nhể? Không đâu nếu chúng ta nhập mật khẩu như thế này:

cookiecookiehanhoanhanhoan

Đoạn cookiehanhoan ở giữa sẽ được lược bỏ, để lại 2 phần đầu và cuối nối với nhau, tạo thành chuỗi cookiehanhoan. Hehe:

Flag{Javascript_is_not_safe???}

Infinite Loop

Lại là một form đăng nhập nữa:

Tuy nhiên khi đăng nhập bằng thông tin bất kỳ, trang web load mãi không dừng và cuối cùng browser báo lỗi:

Kiểm tra trong Burp Suite, chúng ta nhận thấy sau POST request gửi thông tin đăng nhập lên server là hàng loạt các GET request đến các id khác nhau:

Vậy là mỗi GET request này lại chuyển hướng sang GET request khác, tạo thành vòng lặp vô tận. Có thể flag được dấu trong một trong những ID này. Để xem nội dung response của tất cả các request này, mình viết một đoạn script nhỏ bằng Python, request đến tất cả các id và chặn không cho chuyển hướng (redirect):

import requests

for i in range (0,11):
	idd = str(i)
	url = 'http://chal6.web.letspentest.org/check.php?id='
	parsed = url + idd
	r = requests.get(parsed,allow_redirects=False)
	print("ID: {} ----- {}".format(idd, r.status_code))
	if "REDIRECT..." not in r.text:
		print("------------FOUND ID {}".format(idd))
		print(r.text)

Nếu request đến id nào mà response khác tẹo là in ra ngay. Chạy file và mình tìm thấy id 6 chứa flag:

Flag{Y0u_c4ptur3_m3_xD!!!}

I’m not a Robot

Tên đề bài quá rõ ràng, chúng ta nhảy luôn vào file robots.txt thôi:

Robots.txt là một tiêu chuẩn mà rất nhiều trang web sử dụng để tương tác với các web crawler và web robots, bằng cách quy định những khu vực nào của trang web cho phép scan. Như ví dụ ở đây, trang web cho phép scan tại /fl@g1337_d240c789f29416e11a3084a7b50fade5.txt, chúng ta xem có gì ở đây nào:

Flag{N0_B0T_@ll0w}

Web Exploitation

The Maze Runner

Trang web trong đề bài trả về rất nhiều thư mục:

Trong các thư mục này là rất nhiều thư mục nhỏ nữa:

Vậy có thể flag nằm đâu đó trong mạng lưới folder to đùng này. Để đẩy nhanh việc tìm file flag, mình tải toàn bộ trang web với lệnh wget:

wget -r http://chal10.web.letspentest.org/

Option -r tức là recursive, cho phép len lỏi vào các thư mục nhỏ. Để lấy flag trong thư mục này, chúng ta dùng lệnh grep đơn giản:

grep -r ./chal10.web.letspentest.org/ --regexp=FLAG

Vậy là các file có chứa string “FLAG” sẽ được lọc ra:

FLAG{6059e2117ea3eeecdad7faf1e15d16a2}

Ét Quy Eo

Đường link trong đề bài dẫn đến một form đăng nhập:

Để đăng nhập được, có vẻ chúng ta sẽ phải khai thác lỗ hổng SQL Injection. Một đoạn code đơn giản để truy vấn username và mật khẩu sẽ trông như sau:

username = get("username");
password = get("password);

sql_query = "SELECT * FROM Users WHERE username = " + username +"' AND password = '" + password + "'"

Các biến username và password được lấy từ input của người dùng, sau đó được ghép lại để tạo thành SQL Query hoàn chỉnh. Nếu ta nhập một username như thế này

CyberSpace' OR 1=1 --

SQL Query sau khi ghép lại sẽ trở thành:

SELECT * FROM Users WHERE username='CyberSpace' OR 1=1 -- AND password = ''

Mặc dù username này không tồn tại, tuy nhiên phép so sánh 1=1 lại luôn đúng, kết hợp với đằng sau dấu — đều được đánh dấu là comment, đoạn SQL Query trên tương ứng với:

SELECT * FROM Users

Vậy nên chúng ta sẽ có toàn bộ thông tin lưu trong bảng Users:

Giải mã đoạn base64 kia ra là chúng ta có flag thôi:

Flag{Fr33_Styl3}

Misconfiguration

Đề bài nhắc tới các file cấu hình ẩn, cộng thêm tên bài nữa, chúng ta có thể đoán được flag sẽ nằm trong một trong số các file cấu hình đáng lẽ phải không cho người dùng truy cập vào. Đầu tiên phải kể đến file .htaccess. File .htaccess được sử dụng để cấu hình Apache Web Server, dùng để bật/tắt các tính năng mà Apache hỗ trợ:

Ở đây chúng ta lấy được phần 1 của flag. File thứ hai ta cũng cần để ý đến là file web.config. File này được ASP.NET sử dụng để cấu hình ứng dụng web:

Chúng ta có thể thấy phần 2 của flag và một tên file bak. Tải file này về, giải nén là chúng ta sẽ có phần cuối của flag:

Flag{1b283f0725d536a0f217d89caca7b183}

ID’OR1=1

Đề bài có một trường để chúng ta nhập một ID:

Nhập số bất kỳ, chúng ta nhận được một số dữ liệu từ server:

Nếu thay bằng ID khác, chúng ta lại nhận được dữ liệu khác:

Vậy có vẻ ứng dụng web này dính lỗ hổng IDOR, người dùng có thể truy cập tài nguyên tùy ý mà không cần xác thực. Nếu nhập đúng ID, có thể chúng ta sẽ lấy được flag. Để tự động quét các ID, mình sử dụng đoạn code python đơn giản:

import requests


for i in range (1,3000):
	url = "http://chal11.web.letspentest.org/user?id="
	id = str(i)
	passed_url = url + id
	r = requests.get(passed_url)
	if i % 100 == 0:
		print("Checked {}".format(id))
	if r.status_code != 500:
		print("Found ID {} -------------- {}".format(id, r.text))

Đoạn code đơn giản chỉ chạy qua các ID từ 1 đến 3000, nếu phát hiện ID nào trả về dữ liệu thì sẽ in ID đó ra cùng response. Ngồi để code chạy 1 lúc, và chúng ta có thể tìm thấy flag tại ID 1337 (leet):

Flag{61cb4a784e83b6109999af6f036b88bf}

Cảm ơn Cookie Hân Hoan đã mang đến một kỳ CTF rất hay và bổ ích. Ad hóng Season 2 ghê!

1 bình luận về “Cookie Arena CTF Season 1 – Webs Writeup

Trả lời

Please log in using one of these methods to post your comment:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s