4 min read

echoCTF Argum E Nvative (ID#10) writeup (zh-TW)

echoCTF Argum E Nvative (ID#10) writeup (zh-TW)

類別:Reverse Engineering / easy / 1,500pts
檔案Argum_E_Nvative.zip(內含 statically-linked ELF x86_64 binary)
題目連結: https://echoctf.red/challenge/10

一、目標

分析 binary,回答題目要求的 4 個問題:

  • Q1: 程式應如何執行?
  • Q2: 預期的 env flag(ETSCTF)為何?
  • Q3: 預期的 begging argument 為何?
  • Q4: 在滿足前置條件後,binary 印出的正確 source answer為何?

二、靜態檢視

  • 先直接執行看看並觀察
└─$ ./Argum_E_Nvative.bin
Checking argv0... incorrect
Checking argc... 1 incorrect
Checking path[] exists... exists, Checking st_size==4096... correct
Checking ets_env... null
Checking home_env... incorrect
Checking argv[2]... incorrect
The ETSCTF source answer is... (drum roll please)
&wX%!*'hmrkOOPiy'')
  • file 顯示:ELF 64-bit LSB executable、static、not stripped(可反編譯)。
  • 使用 Ghidra 對 main 反編譯後可直接讀出程式的檢查流程

三、深入ghidra反編譯後的main

  1. 先讀取兩個環境變數:local_118 = getenv("HOME")local_110 = getenv("ETSCTF")
  2. 檢查 argv[0]strcmp((char *)*argv, "./fdd35ef750404db0be89f31f116c2d23")
    • 若相等 → 標示 argv0 correct,並把 argv0[3]argv0[2] 放入輸出陣列(ETSCTF result buffer)。
    • 否則放兩個隨機字元(rand)。
  3. 檢查 argc:如果 argc == 3 才進一步檢查 argv[1],否則 argc 判定為 incorrect 並填 rand。
  4. 檢查 argv[1]strcmp(argv[1], "-e")。若相等,放 argv1[0](也就是 '-')到輸出陣列,否則放 rand。
  5. 檢查路徑 "/."stat(local_78,&st))——如果存在則呼叫 setFlagFileProperties()
    • 若相等 → ets_env correct,程式從該 env 字串取出 6 個指定位置的字元(index:10, 0x16(=22), 6, 0x11(=17), 0x12(=18), 9)依序 append 到輸出陣列。
    • 否則 append 6 個 rand。
  6. 檢查 HOME 與是否以 root 執行(geteuid()):
    • HOME 非空且 geteuid()==0home_env correct,append HOME[0]HOME[2]
    • 否則 append HOME[1]HOME[3](程式仍會取 HOME 中的字元來組字串)。
  7. 檢查 argv[2]strcmp(argv[2], "PleaseGiveMeTheFlag")
    • 若相等 → append 一系列 argv2 上特定索引的字元(次序:+0x12, +0x0f, +3, +10, +5, +9, +1, +8, +0, +0x12),共 10 個字元。
    • 否則 append 10 個 rand。
  8. 最後印出 "The ETSCTF source answer is... (drum roll please) " 與組好的 ETSCTF 字串(即題目要的 source answer)。

檢查 ETSCTF 環境變數是否存在,並與整段長字串比對:

"Would it save you a lot of time if I just gave up and went mad now? ETSCTF_02ea5de333a4f296b8aa8bf0925a90ae"
因此要得到 deterministic(非隨機)的 source answer,必須讓所有那些判斷都走到「correct」分支(即:argv0argcargv1ETSCTF env、HOME(與 euid==0)及 argv2 都滿足預期值)。

四、Q1–Q4 的說明

Q1(簡短說明)

程式在 main 裡直接用

strcmp(argv[0], "./fdd35ef750404db0be89f31f116c2d23")

檢查執行時的程式名稱(argv[0]),所以 正確的執行方式就是以 ./fdd35ef750404db0be89f31f116c2d23 這個名稱去呼叫它

Q2 — What is the expected env flag? (350 pts)

答案

Would it save you a lot of time if I just gave up and went mad now? ETSCTF_Redacted

說明:程式用 getenv("ETSCTF") 取得該 env,並和上面整段字串用 strcmp 比較;只有完全吻合才走 correct 分支並抽出其中的指定字元。

Q3 — What is the expected begging argument? (350 pts)

答案(精確字串)

PleaseGiveMeTheFlag

說明:程式用 strcmp(argv[2], "PleaseGiveMeTheFlag") 進行判斷,若相等則從 argv[2] 的多個指定索引處抽出字元組成輸出的一部分。

Q4 — What is the correct source answer? (500 pts)

答案

完整執行這個即可~

sudo env HOME=/root ETSCTF='Would it save you a lot of time if I just gave up and went mad now? ETSCTF_02ea5de333a4f296b8aa8bf0925a90ae'   bash -c "exec -a './fdd35ef750404db0be89f31f116c2d23' ./Argum_E_Nvative.bin -e PleaseGiveMeTheFlag"

說明:

  • 當所有檢查都走到 correct(argv0、argc、argv1、ETSCTF env、home_env 與 argv2 都符合程式內部期望)時,程式依序從這些來源抽取特定位元並 append 成最終字串,印出該字串即為 Q4 要求的 source answer。
└─$ sudo env HOME=/root ETSCTF='Would it save you a lot of time if I just gave up and went mad now? ETSCTF_02ea5de333a4f296b8aa8bf0925a90ae'   bash -c "exec -a './fdd35ef750404db0be89f31f116c2d23' ./Argum_E_Nvative.bin -e PleaseGiveMeTheFlag"
Checking argv0... correct
Checking argc... 3 correct
Checking argv[1]... correct
Checking path[] exists... exists, Checking st_size==4096... correct
Checking ets_env... not null, correct
Checking home_env... correct
Checking argv[2]... correct
The ETSCTF source answer is... (drum roll please)
df-%!*'ati as/ogFaMeelvPg

經驗分享

關於三:

這邊是利用ghidra,去抓出整個"main"的反編譯邏輯喔!方法其實可以透過在ghidra先搜尋所有strings,後來去特定找一些關鍵字,好比我是找argv,就找到main了。