GDG Docker 闖關 10 分鐘爆破:用 grep 拿走所有 flag
2026-10-14 · ctf · linux · grep · gdg
2026-09-30 的 GDG 社課是「Docker 踏入初心」,lab 叫社團伺服器搶救行動:用 docker compose 起一個 Ubuntu 容器,進去之後找出散落在系統各處的 6 個 GDG{...} flag,交齊之後社團官網就會活過來。
正規路線是一關一關學 cat、cd、ls -a、nano、sudo apt install tree、mkdir / mv / cp。這篇講的是另一條路:所有 flag 都長得一樣,那就直接搜。
先講結論:一個
grep拿到 5 個 flag,再一個grep拿到第 3 關的解密密碼,最後一個迴圈全部交出去。
觀察
- flag 格式固定:
hint.txt開頭就寫了「全部有 6 個 flag,格式長這樣GDG{...}」。 - 關卡檔案都在家目錄:Dockerfile 把
lab/home/整包COPY到/home/student/,第 4 關的迷宮也是 build 時在同一個地方生出來的。 - 檢查 flag 的程式不存明文:
submit只存 djb2 雜湊,所以 flag 只會出現在各關的檔案裡,不會從submit洩漏。反過來說,關卡檔案裡的就是明文。
既然 flag 都是明文、都在 ~ 底下、格式又固定,那就是 grep 的工作。
第一步:進容器
docker compose up -d --build
docker compose exec gdg-lab bash
第二步:一個 grep 掃整個家目錄
grep -roE 'GDG\{[a-z0-9_]+\}' ~ | sort -u
/home/student/club_server/level1/backup/old_stuff/flag.txt:GDG{cd_1s_th3_f1rst_st3p}
/home/student/club_server/level2/.gdg_secret:GDG{d0tf1l3s_4r3_sn34ky}
/home/student/club_server/level4/archive/2020/winter/code/secret/flag.txt:GDG{n1c3_try_but_n0t_th1s_0n3}
/home/student/club_server/level4/archive/2022/summer/photos/raw/hackathon_0713/.cache/thumbs/backup/final_v3_FINAL/flag.txt:GDG{tr33_s33s_3v3ryth1ng}
/home/student/club_server/level5/broken_project/main.py:GDG{pr0j3ct_r3st0r3d_g00d_j0b}
/home/student/hint.txt:GDG{h3ll0_t3rm1n4l_w0rld}
每個參數在做什麼:
-r:遞迴往下搜。grep不會跳過開頭是.的檔案和資料夾,所以第 2 關的.gdg_secret和第 4 關藏在.cache/裡的 flag 都直接被掃到,ls -a和tree -a那兩招都不用學。-o:只印出符合的那一段,不印整行。-E:用延伸正規表示式,+才能直接用。[a-z0-9_]+:flag 內容只有小寫、數字、底線。這樣會排除官網和說明檔裡那些GDG{...}範例字樣。sort -u:排序並去掉重複。
一次拿到 level 0、1、2、4、5。其中有兩個值得注意:
- 第 4 關有兩個。
2020/winter/code/secret/flag.txt的n1c3_try_but_n0t_th1s_0n3是故意放的假 flag,真的在.cache/那一個。看路徑就分得出來,懶得分也沒關係,submit會幫你判錯。 - 第 5 關根本不用修專案。這關本來要用
mkdir/mv/cp把拆亂的專案排回去,程式自我檢查通過才會印出 flag。但 flag 是寫死在main.py裡的常數FLAG = "GDG{...}",grep直接讀原始碼就拿到了。
第三步:第 3 關,密碼也是 GDG 開頭
第 3 關的 flag 被加密在 level3/secret.enc,所以第二步搜不到。解密程式 decrypt.py 需要一個 key.txt,正規路線是去跑 NPC 腳本問密碼。
但是把搜尋條件放寬,只找 GDG 開頭、後面接大寫和數字的字串:
grep -rnoE 'GDG[A-Z]+[0-9]*' ~
/home/student/todo.txt:9:GDGVIP
/home/student/club_server/npc/senior.sh:43:GDGVIP666
/home/student/club_server/npc/senior.sh:48:GDGVIP
todo.txt 裡寫著「改成 GDGVIP███(這裡有咖啡漬)」,被咖啡漬蓋掉的部分,NPC 腳本 senior.sh 直接把整串印了出來:GDGVIP666。不用跑腳本、不用等對話,密碼就在原始碼裡。
decrypt.py 的作法是讀同一個資料夾裡的 key.txt,所以不用開 nano,一行寫進去就好:
cd ~/club_server/level3 && echo GDGVIP666 > key.txt && python3 decrypt.py
╔══ 解密成功 ══╗
FLAG: GDG{d3crypt3d_th3_l3g4cy}
第四步:全部交出去
把兩個來源接在一起,抽出所有 flag,丟給 submit:
for f in $({ grep -rhoE 'GDG\{[a-z0-9_]+\}' ~; python3 ~/club_server/level3/decrypt.py; } \
| grep -oE 'GDG\{[a-z0-9_]+\}' | sort -u); do
submit "$f"
done
-h讓grep不印檔名,只留下 flag 本身。{ ...; ...; }把兩個指令的輸出合成一條,再一起過濾。decrypt.py用的是自己所在資料夾的key.txt,所以從哪裡執行都可以。- 會抽出 7 個值:6 個真的加 1 個假的。假的那個
submit會回「flag 不正確」,迴圈照樣繼續,最後進度是 6 / 6。
打開 http://localhost:8080,官網復活。
為什麼這招會成功
這個 lab 是設計給第一次碰 terminal 的人,重點是每關教一個指令,不是防作弊。flag 以明文放在家目錄,權限也是學生自己的,所以任何能讀遍整個目錄的工具都能一次掃完。
其實出題的人自己也偷偷講了。第 4 關的便條紙最後寫著:
(進階:其實有更快的方法。裝完 tree 之後可以試試
tree -af | grep flag…… 或者根本不用裝 tree:find . -name "flag.txt"這兩招我大四才學會,你現在就知道了,賺到。)
這篇只是把同樣的想法再推一步:不搜檔名,直接搜內容。
如果想讓這招失效,比較直接的作法是:
- flag 不要以明文出現在任何檔案裡,例如第 5 關改成檢查通過後才用結構本身算出 flag。
- 解密用的密碼不要完整寫在同一台機器的腳本裡。
指令總整理
# 5 個明文 flag
grep -roE 'GDG\{[a-z0-9_]+\}' ~ | sort -u
# 第 3 關的密碼
grep -rnoE 'GDG[A-Z]+[0-9]*' ~
# 解密第 3 關
cd ~/club_server/level3 && echo GDGVIP666 > key.txt && python3 decrypt.py
# 全部交出去
for f in $({ grep -rhoE 'GDG\{[a-z0-9_]+\}' ~; python3 ~/club_server/level3/decrypt.py; } \
| grep -oE 'GDG\{[a-z0-9_]+\}' | sort -u); do
submit "$f"
done
正規路線還是值得走一遍,cd、ls -a、nano、apt、mv 和 cp 的差別,之後每天都會用到。只是當你知道目標長什麼樣子的時候,先 grep 一下永遠是對的第一步。