Flare-On 9 Challenge - Writeup
はじめに
Mandiant社が毎年開催しているリバースエンジニアリング分野のCTF、Flare-On Challengeに参加しました。出題された11問のうち、6問目まで解きました。
Easyな問題しか解けなかったうえに公式からもWriteup出ていますが、せっかくなので書きました。
(公式Writeupは以下のページで公開されています)
01 - Flaredle
設問
Welcome to Flare-On 9!
You probably won't win. Maybe you're like us and spent the year playing Wordle. We made our own version that is too hard to beat without cheating.
Play it live at: http://flare-on.com/flaredle/
7-zip password: flare
htmlファイル, jsファイル, cssファイルが与えられる。htmlファイルを開くとWordleみたいなやつが表示される。
htmlファイルはscript.jsを読み込んでいる。
<script src="script.js" type="module"></script>
script.jsを見る。word.jsを読み込んで、word.jsで定義されているWORDSをimportしている模様。
import { WORDS } from "./words.js";
const NUMBER_OF_GUESSES = 6;
const WORD_LENGTH = 21;
const CORRECT_GUESS = 57;
let guessesRemaining = NUMBER_OF_GUESSES;
let currentGuess = [];
let nextLetter = 0;
let rightGuessString = WORDS[CORRECT_GUESS];
word.jsのWORDSを見る。CORRECT_GUESS = 57とあったので、WORDS[57]を確認すると良さそう。
57番目の要素は"flareonisallaboutcats"となっている。
export const WORDS = ['acetylphenylhydrazine',
'aerobacteriologically',
'alkylbenzenesulfonate',
'aminoacetophenetidine',
'anatomicopathological',
'anemometrographically',
'anthropoclimatologist',
'anthropomorphological',
'anticonstitutionalism',
'anticonstitutionalist',
'antienvironmentalists',
'antiinstitutionalists',
'antimaterialistically',
'antinationalistically',
'antisupernaturalistic',
'appendorontgenography',
'ballistocardiographic',
'benzalphenylhydrazone',
'bioelectrogenetically',
'chemicopharmaceutical',
'chlamydobacteriaceous',
'cholecystogastrostomy',
'cholecystojejunostomy',
'cholecystolithotripsy',
'cholecystonephrostomy',
'choledochoenterostomy',
'choledocholithotripsy',
'chromophotolithograph',
'cineangiocardiography',
'cytospectrophotometry',
'clinicopathologically',
'constitutionalization',
'counterclassification',
'counterdemonstrations',
'counterindoctrination',
'counterinterpretation',
'counterproductiveness',
'counterpronunciamento',
'counterreconnaissance',
'cryptocrystallization',
'crystalloluminescence',
'dacryocystorhinostomy',
'dehydrocorticosterone',
'deintellectualization',
'demarcatordemarcators',
'dendrochronologically',
'disestablishmentarian',
'disproportionableness',
'duodenocholedochotomy',
'duodenopancreatectomy',
'electrodiagnostically',
'electroencephalograms',
'electroencephalograph',
'electromyographically',
'electrotheraputically',
'enterocholecystostomy',
'establishmentarianism',
'flareonisallaboutcats',
'gastroenterocolostomy',
'gastroenterologically',
'glossolabiopharyngeal',
'hepaticoenterostomies',
'heterotransplantation',
'hexachlorocyclohexane',
'hydrodesulphurization',
'hydroxycorticosterone',
'hyperaggressivenesses',
'hyperconservativeness',
'hyperconstitutionally',
'hyperenthusiastically',
'hyperintellectualness',
'hyperpolysyllabically',
'hypsidolichocephalism',
'historicogeographical',
'historicophilosophica',
'humuhumunukunukuapuaa',
'immunoelectrophoresis',
'immunoelectrophoretic',
'indistinguishableness',
'intellectualistically',
'internationalizations',
'intertransformability',
'isopropylideneacetone',
'labioglossopharyngeal',
'magnetofluidmechanics',
'magnetoplasmadynamics',
'mandibulosuspensorial',
'mechanicointellectual',
'mechanotheraputically',
'membranocartilaginous',
'methyltrinitrobenzene',
'microcolorimetrically',
'microminiaturizations',
'microradiographically',
'microseismometrograph',
'nitrotrichloromethane',
'nonautobiographically',
'noncharacteristically',
'nonimpressionableness',
'noninterchangeability',
'nonpsychoanalytically',
'nonrepresentativeness',
'otorhinolaryngologist',
'overargumentativeness',
'overcommercialization',
'overconscientiousness',
'overgesticulativeness',
'overimpressionability',
'overindividualization',
'overindustrialization',
'overintellectualizing',
'oversuperstitiousness',
'palaeodendrologically',
'pancreatoduodenectomy',
'parathyroidectomizing',
'pathologicoanatomical',
'pentamethylenediamine',
'pharyngoepiglottidean',
'pharmacoendocrinology',
'phenylethylmalonylure',
'philosophicoreligious',
'phoneticohieroglyphic',
'phosphoglyceraldehyde',
'photochromolithograph',
'photolithographically',
'photomicrographically',
'phthalylsulfathiazole',
'platydolichocephalous',
'poliencephalomyelitis',
'poluphloisboiotatotic',
'prostatovesiculectomy',
'protransubstantiation',
'pseudoanachronistical',
'pseudoanthropological',
'pseudohermaphroditism',
'pseudolamellibranchia',
'pseudoparthenogenesis',
'pseudophilanthropical',
'psychopharmacological',
'psychophysiologically',
'psychotherapeutically',
'representationalistic',
'scientificohistorical',
'scleroticochoroiditis',
'selectivitysenescence',
'semianthropologically',
'sphygmomanometrically',
'stereomicroscopically',
'stereophotomicrograph',
'stereoroentgenography',
'succinylsulfathiazole',
'superconservativeness',
'superconstitutionally',
'superincomprehensible',
'superincomprehensibly',
'supertranscendentness',
'tessarescaedecahedron',
'tetrabromofluorescein',
'thermophosphorescence',
'transcendentalisation',
'transcendentalization',
'transubstantiationite',
'triacetyloleandomycin',
'trichloroacetaldehyde',
'trichloronitromethane',
'uncontemporaneousness',
'undistinguishableness',
'unstraightforwardness',
'ureteropyelonephritis',
'zygomaticoauricularis',
]
期待されるinputは rightGuessString + '@flare-on.com'
となっている。そのため、Flagはflareonisallaboutcats@flare-on.comだと判断してsubmitしたら正解だった。
if (guessString === rightGuessString) {
let flag = rightGuessString + '@flare-on.com';
toastr.options.timeOut = 0;
toastr.options.onclick = function() {alert(flag);}
toastr.success('You guessed right! The flag is ' + flag);
guessesRemaining = 0
return
} else {
guessesRemaining -= 1;
currentGuess = [];
nextLetter = 0;
if (guessesRemaining === 0) {
toastr.error("You've run out of guesses! Game over!")
toastr.info('Try reverse engineering the code to discover the correct "word"!');
}
}
Flag : flareonisallaboutcats@flare-on.com
02 - Pixel Poker
設問
I said you wouldn't win that last one. I lied. The last challenge was basically a captcha. Now the real work begins. Shall we play another game?
7-zip password: flare
PixelPoker.exeという名前の実行形式ファイルが与えられる。実行すると座標当てゲーム?みたいなものが始まる。1ゲームで10回まで挑戦できるっぽい。正しい座標を当てるとFlagが表示されるものと推測する。
x64dbgでデバッグしつつアセンブリコードを眺めていると、
- ある値と0x2E5のmodを取った結果が0x5F(95)になっているか
- ある値と0x281のmodを取った結果が0x139(313)になっているか
をチェックしている箇所があることが確認できた。95と313はそれぞれ座標の値に対応してそうなので、PixelPokerのGUIで頑張って(95, 313)をクリックした(不器用なので正確にクリックするのに結構苦労した)。
(95, 313)の座標をクリックするとFlagが書かれた下記の画像が得られた。
Flag : w1nN3r_W!NneR_cHick3n_d1nNer@flare-on.com
03 - Magic 8 Ball
設問
You got a question? Ask the 8 ball!
7-zip password: flare
03も01と02同様、ゲーム解析系の問題となっている。与えられた実行ファイル Magic8Ball.exe は実行するとユーザからのキー入力を受け付けて、適当に文字列を入力してEnterキーを押すと変な文言が表示される。
x64dbgとIDAを使用して解析する。sub_402090関数を見ると怪しい文字列がハードコードされていることが確認できた。
gimme flag pls?
と入力してみたが特にFlagは得られなかった。
mov dword ptr [edi+5Ch], 'mmig'
mov dword ptr [edi+60h], 'lf e'
mov dword ptr [edi+64h], 'p ga'
mov dword ptr [edi+68h], '?sl'
更に見ていくとsub_4024E0関数に変なチェック処理があった。何をやっているのか少し悩んだが、 UDLR
はそれぞれ 矢印キーの上下左右の入力
に対応しているようだった。
loc_4026C5: ; CODE XREF: sub_4024E0+1E1↑j
cmp byte ptr [ecx], 4Ch ; 'L'
jnz loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_4026D7
mov ecx, [esi]
loc_4026D7: ; CODE XREF: sub_4024E0+1F3↑j
cmp byte ptr [ecx+1], 4Ch ; 'L'
jnz loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_4026EA
mov ecx, [esi]
loc_4026EA: ; CODE XREF: sub_4024E0+206↑j
cmp byte ptr [ecx+2], 55h ; 'U'
jnz loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_4026FD
mov ecx, [esi]
loc_4026FD: ; CODE XREF: sub_4024E0+219↑j
cmp byte ptr [ecx+3], 52h ; 'R'
jnz loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_402710
mov ecx, [esi]
loc_402710: ; CODE XREF: sub_4024E0+22C↑j
cmp byte ptr [ecx+4], 55h ; 'U'
jnz short loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_40271F
mov ecx, [esi]
loc_40271F: ; CODE XREF: sub_4024E0+23B↑j
cmp byte ptr [ecx+5], 4Ch ; 'L'
jnz short loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_40272E
mov ecx, [esi]
loc_40272E: ; CODE XREF: sub_4024E0+24A↑j
cmp byte ptr [ecx+6], 44h ; 'D'
jnz short loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_40273D
mov ecx, [esi]
loc_40273D: ; CODE XREF: sub_4024E0+259↑j
cmp byte ptr [ecx+7], 55h ; 'U'
jnz short loc_402789
mov ecx, esi
cmp eax, 10h
jb short loc_40274C
mov ecx, [esi]
loc_40274C: ; CODE XREF: sub_4024E0+268↑j
cmp byte ptr [ecx+8], 4Ch ; 'L'
jnz short loc_402789
cmp dword ptr [edi+10Ch], 10h
lea ecx, [edi+0F8h]
jb short loc_402763
mov ecx, [ecx]
loc_402763: ; CODE XREF: sub_4024E0+27F↑j
push 0Fh ; int
lea eax, [edi+5Ch]
push eax ; int
push ecx ; int
call ds:strncmp
add esp, 0Ch
test eax, eax
jnz short loc_402789
sub esp, 18h
mov ecx, esp
push esi ; Src
call sub_401220
mov ecx, edi
call sub_401A10
"gimme flag pls?"という文字列を入力したあとで LLURULDUL
の順に9回矢印キーを入力してボールを揺らしてあげると下記のようにFlagが表示された。
Flag : U_cRackeD_th1$_maG1cBaLL_!!_@flare-on.com
04 - darn_mice
設問
"If it crashes its user error." -Flare Team
7-zip password: flare
darn_mice.exeという実行形式ファイルが与えられる。
コマンドプロンプトから実行する。そのまま実行すると何も表示されないが、引数を付けると謎の文章が表示される。
E:\CTF\Flare-On 9\04 - darn_mice\04_darn_mice>darn_mice.exe
E:\CTF\Flare-On 9\04 - darn_mice\04_darn_mice>darn_mice.exe a
On your plate, you see four olives.
You leave the room, and a mouse EATS one!
x64dbgとIDAを使用して解析する。
main関数の中では、VirtualAllocを使用して確保した領域にデータをコピーし実行している。
| 6A 40 | push 40
| 68 00300000 | push 3000
| 68 00100000 | push 1000
| 6A 00 | push 0
| FF15 0C20F200 | call dword ptr ds:[<&VirtualAlloc>]
| 8945 CC | mov dword ptr ss:[ebp-34],eax
| 8B45 D4 | mov eax,dword ptr ss:[ebp-2C]
| 0FB64C05 D8 | movzx ecx,byte ptr ss:[ebp+eax-28]
| 8B55 08 | mov edx,dword ptr ss:[ebp+8]
| 0355 D4 | add edx,dword ptr ss:[ebp-2C]
| 0FBE02 | movsx eax,byte ptr ds:[edx]
| 03C8 | add ecx,eax
| 8B55 CC | mov edx,dword ptr ss:[ebp-34]
| 880A | mov byte ptr ds:[edx],cl
| FF55 CC | call dword ptr ss:[ebp-34]
| 68 9890F200 | push darn_mice.F29098
| E8 D7000000 | call <darn_mice.sub_F11240>
| 83C4 04 | add esp,4
| EB 97 | jmp darn_mice.F11105
確保した領域にコピーされて実行されることになる値は、引数文字列に下記のStackStringsの値をそれぞれ足した値になっている。
| C645 D8 50 | mov byte ptr ss:[ebp-28],50
| C645 D9 5E | mov byte ptr ss:[ebp-27],5E
| C645 DA 5E | mov byte ptr ss:[ebp-26],5E
| C645 DB A3 | mov byte ptr ss:[ebp-25],A3
| C645 DC 4F | mov byte ptr ss:[ebp-24],4F
| C645 DD 5B | mov byte ptr ss:[ebp-23],5B
| C645 DE 51 | mov byte ptr ss:[ebp-22],51
| C645 DF 5E | mov byte ptr ss:[ebp-21],5E
| C645 E0 5E | mov byte ptr ss:[ebp-20],5E
| C645 E1 97 | mov byte ptr ss:[ebp-1F],97
| C645 E2 A3 | mov byte ptr ss:[ebp-1E],A3
| C645 E3 80 | mov byte ptr ss:[ebp-1D],80
| C645 E4 90 | mov byte ptr ss:[ebp-1C],90
| C645 E5 A3 | mov byte ptr ss:[ebp-1B],A3
| C645 E6 80 | mov byte ptr ss:[ebp-1A],80
| C645 E7 90 | mov byte ptr ss:[ebp-19],90
| C645 E8 A3 | mov byte ptr ss:[ebp-18],A3
| C645 E9 80 | mov byte ptr ss:[ebp-17],80
| C645 EA 90 | mov byte ptr ss:[ebp-16],90
| C645 EB A3 | mov byte ptr ss:[ebp-15],A3
| C645 EC 80 | mov byte ptr ss:[ebp-14],80
| C645 ED 90 | mov byte ptr ss:[ebp-13],90
| C645 EE A3 | mov byte ptr ss:[ebp-12],A3
| C645 EF 80 | mov byte ptr ss:[ebp-11],80
| C645 F0 90 | mov byte ptr ss:[ebp-10],90
| C645 F1 A3 | mov byte ptr ss:[ebp-F],A3
| C645 F2 80 | mov byte ptr ss:[ebp-E],80
| C645 F3 90 | mov byte ptr ss:[ebp-D],90
| C645 F4 A3 | mov byte ptr ss:[ebp-C],A3
| C645 F5 80 | mov byte ptr ss:[ebp-B],80
| C645 F6 90 | mov byte ptr ss:[ebp-A],90
| C645 F7 A2 | mov byte ptr ss:[ebp-9],A2
| C645 F8 A3 | mov byte ptr ss:[ebp-8],A3
| C645 F9 6B | mov byte ptr ss:[ebp-7],6B
| C645 FA 7F | mov byte ptr ss:[ebp-6],7F
実行される命令がどのようなものになれば良いのか少し考えた。
コードを眺めると VirtualAllocを実行 → 引数文字列[i]+ハードコード文字列[i]を実行
という流れを36回繰り返しているだけで、それが終わるとループ処理を抜けるように見えたので、引数文字列[i]+ハードコード文字列[i]が全て 0xC3( = return命令)
になるような文字列を作成してみた。
c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3 c3
-
50 5E 5E A3 4F 5B 51 5E 5E 97 A3 80 90 A3 80 90 A3 80 90 A3 80 90 A3 80 90 A3 80 90 A3 80 90 A2 A3 6B 7F
↓
see three, C3 C3 C3 C3 C3 C3 C3! XD
作成した引数 see three, C3 C3 C3 C3 C3 C3 C3! XD
を与えて実行するとFlagが得られた。
E:\CTF\Flare-On 9\04 - darn_mice\04_darn_mice>darn_mice.exe "see three, C3 C3 C3 C3 C3 C3 C3! XD"
On your plate, you see four olives.
You leave the room, and a mouse EATS one!
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
Nibble...
When you return, you only: see three, C3 C3 C3 C3 C3 C3 C3! XD
i_w0uld_l1k3_to_RETurn_this_joke@flare-on.com
Flag : i_w0uld_l1k3_to_RETurn_this_joke@flare-on.com
05 - T8
設問
FLARE FACT #823: Studies show that C++ Reversers have fewer friends on average than normal people do. That's why you're here, reversing this, instead of with them, because they don't exist.
We've found an unknown executable on one of our hosts. The file has been there for a while, but our networking logs only show suspicious traffic on one day. Can you tell us what happened?
7-zip password: flare
ようやくマルウェア解析系の問題っぽいのが登場した。ホスト内で見つかった実行形式ファイル(t8.exe)と、それに関連するパケットキャプチャ(traffic.pcapng)が提供されているらしい。
t8.exeをDetect It Easyで解析する。問題文にある通り、C++で書かれた実行形式ファイルの模様。
traffic.pcapngを確認するとPOST通信のログが2件記録されている。
flare-on.comに対するPOST通信で、2件ともエンコードされていると思われるデータを送受信している。
また、User-Agentの末尾は1件目と2件目の通信で異なっている。
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; 11950)
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; CLR)
t8.exeを解析してC2サーバからのレスポンスデータを復号/デコードする問題のようなので、x64dbgとIDAを使用してt8.exeを解析する。
main関数から見ていくと長い時間Sleepを繰り返す箇所がある。この処理は潰してデバッグした
(この後段にも邪魔なコードがあったのでデバッグする時はそれも潰した)。
loc_4046E0: ; dwMilliseconds
68 00 2E 93 02 push 2932E00h
FF D6 call esi ; Sleep
0F 10 05 8C 08 45 00 movups xmm0, xmmword_45088C
83 EC 10 sub esp, 10h
8B C4 mov eax, esp
0F 11 00 movups xmmword ptr [eax], xmm0
E8 75 FE FF FF call sub_404570
83 C4 10 add esp, 10h
83 F8 0F cmp eax, 0Fh
75 DD jnz short loc_4046E0
XORによる文字列のデコードを行っている。"flare-on.com"という文字列がここで作成される。
loc_404750:
66 83 74 45 B8 11 xor word ptr [ebp+eax*2+var_48], 11h
40 inc eax
83 F8 0C cmp eax, 0Ch
72 F4 jb short loc_404750
更に進めていくと、sub_401020関数で時刻ベースのランダムな整数を作成して、 接頭辞("FO9")
を付けていることが分かった。
55 push ebp
8B EC mov ebp, esp
6A FF push 0FFFFFFFFh
68 AA C6 43 00 push offset SEH_401020
64 A1 00 00 00 00 mov eax, large fs:0
50 push eax
83 EC 14 sub esp, 14h
A1 10 00 45 00 mov eax, ___security_cookie
33 C5 xor eax, ebp
89 45 F0 mov [ebp+var_10], eax
50 push eax
8D 45 F4 lea eax, [ebp+var_C]
64 A3 00 00 00 00 mov large fs:0, eax
6A 03 push 3 ; int
68 18 B8 44 00 push offset aFo9 ; "FO9"
B9 74 08 45 00 mov ecx, offset dword_450874 ; void *
C7 45 FC 00 00 00 00 mov [ebp+var_4], 0
E8 D0 48 00 00 call aa_memmove?
8D 45 E0 lea eax, [ebp+SystemTime]
50 push eax ; lpSystemTime
FF 15 10 E0 43 00 call ds:GetLocalTime
0F B7 45 EA movzx eax, [ebp+SystemTime.wMinute]
8B C8 mov ecx, eax
C1 E1 04 shl ecx, 4
2B C8 sub ecx, eax
0F B7 45 EC movzx eax, [ebp+SystemTime.wSecond]
8D 04 88 lea eax, [eax+ecx*4]
69 C8 E8 03 00 00 imul ecx, eax, 3E8h
0F B7 45 EE movzx eax, [ebp+SystemTime.wMilliseconds]
03 C8 add ecx, eax
51 push ecx ; Seed
E8 87 0F 02 00 call _srand
E8 61 0F 02 00 call _rand
0F 10 45 E0 movups xmm0, xmmword ptr [ebp+SystemTime.wYear]
68 50 DC 43 00 push offset sub_43DC50 ; void (__cdecl *)()
A3 70 08 45 00 mov dword_450870, eax
0F 11 05 8C 08 45 00 movups xmmword_45088C, xmm0
E8 3D CA 01 00 call _atexit
83 C4 08 add esp, 8
8B 4D F4 mov ecx, [ebp+var_C]
64 89 0D 00 00 00 00 mov large fs:0, ecx
59 pop ecx
8B 4D F0 mov ecx, [ebp+var_10]
33 CD xor ecx, ebp ; StackCookie
E8 77 C5 01 00 call @__security_check_cookie@4 ; __security_check_cookie(x)
8B E5 mov esp, ebp
5D pop ebp
C3 retn
例えば、作成されたランダムな整数が"8977"の場合、"FO98977"として保持する。
009FF97C 66 00 6C 00 61 00 72 00 65 00 2D 00 6F 00 6E 00 f.l.a.r.e.-.o.n.
009FF98C 38 00 39 00 37 00 37 00 00 00 00 00 00 00 00 00 8.9.7.7.........
009FF99C 04 00 00 00 07 00 00 00 50 00 4F 00 53 00 54 00 ........P.O.S.T.
↓
| push ecx | ecx:&L"FO98977"
上記の文字列生成後、sub_4037A0関数がCallされて、更にsub_403910関数がCallされる。
55 push ebp
8B EC mov ebp, esp
6A FF push 0FFFFFFFFh
68 58 C7 43 00 push offset SEH_4037A0
64 A1 00 00 00 00 mov eax, large fs:0
50 push eax
56 push esi
57 push edi
A1 10 00 45 00 mov eax, ___security_cookie
33 C5 xor eax, ebp
50 push eax
8D 45 F4 lea eax, [ebp+var_C]
64 A3 00 00 00 00 mov large fs:0, eax
8B F9 mov edi, ecx
C7 45 FC 00 00 00 00 mov [ebp+var_4], 0
8D 55 08 lea edx, [ebp+Block]
83 7D 1C 08 cmp [ebp+arg_14], 8
8B 07 mov eax, [edi]
0F 43 55 08 cmovnb edx, [ebp+Block]
52 push edx
FF 50 28 call dword ptr [eax+28h] ; sub_403910
8B F0 mov esi, eax
8B CE mov ecx, esi
8D 41 02 lea eax, [ecx+2]
sub_403910関数では先に作成した"FO9"から始まる文字列のMD5ハッシュ値を取得している。
main関数内のC2通信処理の手前まで進めると文字列をXORデコードしている。デコードされた文字列は ahoy
という文字列になっている。
loc_40491B:
66 A1 B4 B8 44 00 mov ax, ds:word_44B8B4
8D 4D D0 lea ecx, [ebp+var_30]
F3 0F 7E 05 AC B8 44 00 movq xmm0, ds:enc_strings_1
8D 51 02 lea edx, [ecx+2]
66 0F D6 45 D0 movq [ebp+var_30], xmm0
66 83 75 D0 78 xor word ptr [ebp+var_30], 78h
66 83 75 D2 78 xor word ptr [ebp+var_30+2], 78h
66 83 75 D4 78 xor word ptr [ebp+var_30+4], 78h
66 83 75 D6 78 xor word ptr [ebp+var_30+6], 78h
66 89 45 D8 mov [ebp+var_28], ax
A0 B6 B8 44 00 mov al, ds:byte_44B8B6
88 45 DA mov [ebp+var_26], al
33 C0 xor eax, eax
C7 45 DB 00 00 00 00 mov [ebp+var_25], 0
C6 45 DF 00 mov [ebp+var_21], 0
C7 45 98 00 00 00 00 mov [ebp+var_68], 0
C7 45 9C 07 00 00 00 mov [ebp+var_64], 7
66 89 45 88 mov word ptr [ebp+var_78], ax
その後、sub_403860関数でRC4暗号と思われる処理を行っていて、"ahoy"を暗号化している。その際の暗号化鍵は先に作成していたMD5ハッシュ値が使用されている。
55 push ebp
8B EC mov ebp, esp
81 EC 10 01 00 00 sub esp, 110h
A1 10 00 45 00 mov eax, ___security_cookie
33 C5 xor eax, ebp
89 45 FC mov [ebp+var_4], eax
83 7D 1C 08 cmp [ebp+arg_14], 8
8D 4D 08 lea ecx, [ebp+Block]
8B 45 18 mov eax, [ebp+arg_10]
0F 43 4D 08 cmovnb ecx, [ebp+Block]
03 C0 add eax, eax
56 push esi
8B 75 24 mov esi, [ebp+Size]
57 push edi
50 push eax
51 push ecx
8D 8D F0 FE FF FF lea ecx, [ebp+var_110]
E8 2B D9 FF FF call aa_RC4_KSA
56 push esi ; Size
E8 E0 FF 01 00 call _malloc
56 push esi ; Size
8B F8 mov edi, eax
6A 00 push 0 ; Val
57 push edi ; void *
E8 3A BE 01 00 call _memset
83 C4 10 add esp, 10h
8D 8D F0 FE FF FF lea ecx, [ebp+var_110]
56 push esi
57 push edi
FF 75 20 push [ebp+arg_18]
E8 67 D8 FF FF call aa_RC4
8B 55 1C mov edx, [ebp+arg_14]
83 FA 08 cmp edx, 8
72 2E jb short loc_4038EF
暗号化したデータをBase64エンコードして、main関数からC2通信の関数(sub_403D70)をCallする。発生した通信を確認したところ、User-Agent文字列の末尾には先に作成されたランダムな整数が付与されることが確認できた。
ここまででtraffic.pcapngに記録されているC2通信を再現するための材料が揃ったと考えて、情報を整理して検証してみる。C2通信は下記のような流れで行われている。
- ランダムな整数の作成
- 作成した整数に接頭辞を付けて"FO9XXXX"のような文字列を作成する。
- 接頭辞を付けた文字列のMD5ハッシュ値を取得する。
- MD5ハッシュ値を暗号鍵として、RC4暗号で文字列"ahoy"を暗号化する。
- 暗号化した文字列をBase64エンコードする。
- Base64エンコードした文字列をPOST通信で送信する。通信時のUser-Agent文字列は前半部分が固定で、末尾には1.で作成されたランダムな整数を付与している。
- C2サーバ側から暗号化されたデータ?が返ってくる。
traffic.pcapngに記録されているPOST通信の場合、User-Agent文字列の末尾は 11950
となっているので、MD5ハッシュ値( = 暗号鍵)の元となっている文字列は FO911950
となることが分かる。これを使用してMD5ハッシュ値を作成する。(※ t8.exeが使用しているUTF-16LEエンコードに合わせる点に注意)
FO911950 → (Hex) → 46 00 4F 00 39 00 31 00 31 00 39 00 35 00 30 00 → (MD5) → a5c6993299429aa7b900211d4a279848 → (Hex) → 61 00 35 00 63 00 36 00 39 00 39 00 33 00 32 00 39 00 39 00 34 00 32 00 39 00 61 00 61 00 37 00 62 00 39 00 30 00 30 00 32 00 31 00 31 00 64 00 34 00 61 00 32 00 37 00 39 00 38 00 34 00 38 00
traffic.pcapngで最初にPOSTされている文字列は ydN8BXq16RE=
となっているので、これを復号して"ahoy"になるか確認する。下記の画像の通り、問題無く復号できてそうに見える。
ここまでで確認できた内容でtraffic.pcapngに記録されているPOST通信を再現できそう。
POST通信のレスポンスデータを対向サーバ側に置いて、t8.exeをデバッガ上で動作させる(t8.exeが生成した整数値は"11950"に書き換える)。
この状態でデバッグを進めていくと、最終的にt8.exeが処理したデータの中からFlagが得られた。
Flag : i_s33_you_m00n@flare-on.com
06 - a la mode
設問
FLARE FACT #824: Disregard flare fact #823 if you are a .NET Reverser too.
We will now reward your fantastic effort with a small binary challenge. You've earned it kid!
7-zip password: flare
設問の記載内容より、今度は.Net Framework製の実行形式ファイルを解析する問題っぽい。実行形式ファイルのHowDoesThisWork.dllと、IR chat log.txtというテキストファイルが提供されている。
Detect It EasyでHowDoesThisWork.dllを解析する。予想通り、.Netの実行形式ファイル。
チャットログを確認する。他にファイルがないかとFLARE TeamがIR Teamに聞いているが、これが全てだと言われている。
[FLARE Team] Hey IR Team, it looks like this sample has some other binary that might
interact with it, do you have any other files that might be of help.
[IR Team] Nope, sorry this is all we got from the client, let us know what you got.
dnSpyを使ってHowDoesThisWork.dllを解析する。コードはかなり少ない。
FlagクラスのGetFlagメソッドの引数にpasswordを与えて、名前付きパイプで通信を行うと思われる(パイプ通信のクライアント?)。ただし、実行ファイルはこのファイルしか与えられていないため、これとパイプを繋いで通信する別のファイルは無さそう。
// FlareOn.Flag
// Token: 0x06000002 RID: 2 RVA: 0x0000D158 File Offset: 0x0000C558
public Flag()
{
}
using System;
using System.IO.Pipes;
using System.Text;
namespace FlareOn
{
// Token: 0x02000002 RID: 2
public class Flag
{
// Token: 0x06000001 RID: 1 RVA: 0x0000D078 File Offset: 0x0000C478
public string GetFlag(string password)
{
Decoder decoder = Encoding.UTF8.GetDecoder();
UTF8Encoding utf8Encoding = new UTF8Encoding();
string text = "";
byte[] array = new byte[64];
char[] array2 = new char[64];
byte[] bytes = utf8Encoding.GetBytes(password + "\0");
using (NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(".", "FlareOn", PipeDirection.InOut))
{
namedPipeClientStream.Connect();
namedPipeClientStream.ReadMode = PipeTransmissionMode.Message;
namedPipeClientStream.Write(bytes, 0, Math.Min(bytes.Length, 64));
int num = namedPipeClientStream.Read(array, 0, array.Length);
int chars = decoder.GetChars(array, 0, num, array2, 0);
text += new string(array2, 0, chars);
}
return text;
}
}
}
何をすれば良いのか悩んだが、HowDoesThisWork.dllの末尾の方にC++のアセンブリコードっぽいものが含まれていることに気が付いたのでそっちも調査した。
0x100012F1ではハードコードされた文字列のデコード処理を行っている。デコード方法はシングルバイトのXORで、鍵は0x17だった(読みやすいように変数名は対応する内容に書き換えた)。
.data:10015034 56 a_Authorization_Failed db 56h ; V
.data:10015035 62 db 62h ; b
.data:10015036 63 db 63h ; c
.data:10015037 7F db 7Fh ;
.data:10015038 78 db 78h ; x
.data:10015039 65 db 65h ; e
.data:1001503A 7E db 7Eh ; ~
.data:1001503B 6D db 6Dh ; m
.data:1001503C 76 db 76h ; v
.data:1001503D 63 db 63h ; c
.data:1001503E 7E db 7Eh ; ~
.data:1001503F 78 db 78h ; x
.data:10015040 79 db 79h ; y
.data:10015041 37 db 37h ; 7
.data:10015042 51 db 51h ; Q
.data:10015043 76 db 76h ; v
.data:10015044 7E db 7Eh ; ~
.data:10015045 7B db 7Bh ; {
.data:10015046 72 db 72h ; r
.data:10015047 73 db 73h ; s
.data:10015048 00 db 0
.data:10015049 00 db 0
.data:1001504A 00 db 0
.data:1001504B 00 db 0
.data:1001504C 4B 4B 39 4B 67 7E 67 72+a_pipe_FlareOn db 'KK9Kg~grKQ{verXy',0 ; DATA XREF: aa_main_thread?+33↑o
.data:1001505D 00 00 00 align 10h
.data:10015060 54 a_CloseHandle db 54h ; T ; DATA XREF: aa_DecodeStrings+2D↑o
.data:10015061 7B db 7Bh ; {
.data:10015062 78 db 78h ; x
.data:10015063 64 db 64h ; d
.data:10015064 72 db 72h ; r
.data:10015065 5F db 5Fh ; _
.data:10015066 76 db 76h ; v
.data:10015067 79 db 79h ; y
.data:10015068 73 db 73h ; s
.data:10015069 7B db 7Bh ; {
.data:1001506A 72 db 72h ; r
.data:1001506B 1D db 1Dh
.data:1001506C 00 db 0
.data:1001506D 00 db 0
.data:1001506E 00 db 0
.data:1001506F 00 db 0
.data:10015070 54 78 79 79 72 74 63 59+a_ConnectNamedPipe db 'TxyyrtcYvzrsG~gr',0
.data:10015070 76 7A 72 73 47 7E 67 72+ ; DATA XREF: aa_DecodeStrings+3F↑o
.data:10015081 00 00 00 align 4
.data:10015084 54 65 72 76 63 72 59 76+a_CreateNamedPipeA db 'TervcrYvzrsG~grV',0
.data:10015084 7A 72 73 47 7E 67 72 56+ ; DATA XREF: aa_DecodeStrings+56↑o
.data:10015095 00 00 00 align 4
.data:10015098 54 a_CreateThread db 54h ; T ; DATA XREF: aa_DecodeStrings+6D↑o
.data:10015099 65 db 65h ; e
.data:1001509A 72 db 72h ; r
.data:1001509B 76 db 76h ; v
.data:1001509C 63 db 63h ; c
.data:1001509D 72 db 72h ; r
.data:1001509E 43 db 43h ; C
.data:1001509F 7F db 7Fh ;
.data:100150A0 65 db 65h ; e
.data:100150A1 72 db 72h ; r
.data:100150A2 76 db 76h ; v
.data:100150A3 73 db 73h ; s
.data:100150A4 00 db 0
.data:100150A5 00 db 0
.data:100150A6 00 db 0
.data:100150A7 00 db 0
.data:100150A8 53 7E 64 74 78 79 79 72+a_DisconnectNamedPipe db 'S~dtxyyrtcYvzrsG~gr',0
.data:100150A8 74 63 59 76 7A 72 73 47+ ; DATA XREF: aa_DecodeStrings+8C↑o
.data:100150BC 51 a_FlushFileBuffers db 51h ; Q ; DATA XREF: aa_DecodeStrings+9E↑o
.data:100150BD 7B db 7Bh ; {
.data:100150BE 62 db 62h ; b
.data:100150BF 64 db 64h ; d
.data:100150C0 7F db 7Fh ;
.data:100150C1 51 db 51h ; Q
.data:100150C2 7E db 7Eh ; ~
.data:100150C3 7B db 7Bh ; {
.data:100150C4 72 db 72h ; r
.data:100150C5 55 db 55h ; U
.data:100150C6 62 db 62h ; b
.data:100150C7 71 db 71h ; q
.data:100150C8 71 db 71h ; q
.data:100150C9 72 db 72h ; r
.data:100150CA 65 db 65h ; e
.data:100150CB 64 db 64h ; d
.data:100150CC 00 db 0
.data:100150CD 00 db 0
.data:100150CE 00 db 0
.data:100150CF 00 db 0
.data:100150D0 50 72 63 5B 76 64 63 52+a_GetLastError db 'Prc[vdcReexe',0 ; DATA XREF: aa_DecodeStrings+B5↑o
.data:100150DD 00 00 00 align 10h
.data:100150E0 50 72 63 47 65 78 74 72+a_GetProcessHeap db 'PrcGextrdd_rvg',0 ; DATA XREF: aa_DecodeStrings+CC↑o
.data:100150EF 00 align 10h
.data:100150F0 7B 64 63 65 74 7A 67 56+a_lstrcmpA db '{dcetzgV',0 ; DATA XREF: aa_DecodeStrings+EB↑o
.data:100150F9 00 00 00 align 4
.data:100150FC 45 72 76 73 51 7E 7B 72+a_ReadFile db 'ErvsQ~{r',0 ; DATA XREF: aa_DecodeStrings+FD↑o
.data:10015105 00 00 00 align 4
.data:10015108 40 65 7E 63 72 51 7E 7B+a_WriteFile db '@e~crQ~{r',0 ; DATA XREF: aa_DecodeStrings+119↑o
.data:10015112 00 00 align 4
Windows API名と思われる文字列が幾つか含まれていることが確認できた。パイプ通信関連のものも見える。
CloseHandle
ConnectNamedPipe
CreateNamedPipeA
CreateThread
DisconnectNamedPipe
FlushFileBuffers
GetLastError
GetProcessHeap
lstrcmpA
ReadFile
WriteFile
文字列のデコード後、0x1000109のアセンブリコードをCreateThreadでスレッド実行する。
33 C9 xor ecx, ecx
51 push ecx
51 push ecx
51 push ecx
68 94 10 00 10 push offset sub_1000109
51 push ecx
51 push ecx
FF 15 30 5A 01 10 call d_CreateThread
0x1000109のスレッドでは名前付きパイプを作成して、データの読み出しと復号処理(sub_10001000)を実行すると思われる。
59 pop ecx
59 pop ecx
50 push eax ; \\.\pipe\FlareOn
FF 15 2C 5A 01 10 call d_CreateNamedPipeA
8B F0 mov esi, eax
83 FE FF cmp esi, 0FFFFFFFFh
...
...
53 push ebx
56 push esi
FF 15 28 5A 01 10 call d_ConnectNamedPipeA
85 C0 test eax, eax
75 06 jnz short loc_100010F3
...
...
loc_100010F3:
53 push ebx
8D 45 FC lea eax, [ebp+var_4]
50 push eax ; read size
6A 40 push 40h ; '@' ; buffer size
8D 45 B4 lea eax, [ebp+var_4C]
50 push eax ; buffer
56 push esi ; file handle
FF 15 48 5A 01 10 call d_ReadFile
85 C0 test eax, eax
74 37 jz short loc_10001140
...
...
56 push esi
FF 15 38 5A 01 10 call d_FlushFileBuffers
8B 45 FC mov eax, [ebp+var_4]
88 5C 05 B4 mov [ebp+eax+var_4C], bl
8D 45 F8 lea eax, [ebp+var_8]
50 push eax
8D 45 B4 lea eax, [ebp+var_4C]
50 push eax
E8 D7 FE FF FF call sub_10001000
59 pop ecx
59 pop ecx
53 push ebx
8D 45 F4 lea eax, [ebp+var_C]
50 push eax
FF 75 F8 push [ebp+var_8]
8D 45 B4 lea eax, [ebp+var_4C]
50 push eax
56 push esi
FF 15 4C 5A 01 10 call d_WriteFile
EB 06 jmp short loc_10001146
0x10001000を確認すると何らかの暗号方式でデータを復号して、lstrcmpAで入力値と比較しているように見える。
処理を確認すると方式はRC4暗号のようで、RC4暗号鍵"55 8B EC 83 EC 20 EB FE"(offset d_RC4_Keyにリネーム済み)で暗号化されていると思われるデータ"3E 39 51 FB A2 11 F7 B9 2C"(offset d_Encrypted_Dataにリネーム済み)を復号している。実際に復号を試した結果、MyV0ic3!
という文字列が得られた。
55 push ebp
8B EC mov ebp, esp
81 EC 08 04 00 00 sub esp, 408h
8D 85 F8 FB FF FF lea eax, [ebp+var_408] ; Buffer?
56 push esi
57 push edi
6A 08 push 8
68 00 50 01 10 push offset d_RC4_Key
50 push eax
E8 D1 01 00 00 call aa_RC4_1
6A 09 push 9
BE 28 50 01 10 mov esi, offset d_Encrypted_Data
8D 85 F8 FB FF FF lea eax, [ebp+var_408]
56 push esi ; enc_data?
50 push eax ; key_handle?
E8 55 01 00 00 call aa_RC4_2
83 C4 18 add esp, 18h
FF 75 08 push [ebp+arg_0]
56 push esi
FF 15 44 5A 01 10 call d_lstrcmpA
85 C0 test eax, eax
74 20 jz short loc_10001063
後段の処理でもう1つのデータ"E1 60 A1 18 93 2E 96 AD 73 BB 4A 92 DE 18 0A AA 41 74 AD C0 1D 9F 3F 19 FF 2B 02 DB D1 CD 1A"(d_Encrypted_Data_2にリネーム済み)を復号しているようなので、こちらも同じ鍵で復号しようと思うが上手くいかなかった(KSAの処理が1回しか無いので、ストリームが初期化されずに使われてそう)。
...
...
loc_10001063:
6A 1F push 1Fh
5F pop edi
57 push edi
BE 08 50 01 10 mov esi, offset d_Encrypted_Data_2
8D 85 F8 FB FF FF lea eax, [ebp+var_408]
56 push esi ; enc_data
50 push eax ; key_handle
E8 0E 01 00 00 call aa_RC4_2
8B 45 0C mov eax, [ebp+arg_4]
83 C4 0C add esp, 0Ch
89 38 mov [eax], edi
33 FF xor edi, edi
47 inc edi
...
...
解いている時はどのように復号するのが良いかパッと思いつかなかったため、HowDoesThisWork.dllのEntryPointを書き換えてデバッガ上で無理やり動かしてFlagを復号させた。
Flag : M1x3d_M0dE_4_l1f3@flare-on.com
補足
他の方のWriteupを読むと、下記の画像のような方法でFlagを復号している人も居た。
Loaderを自作してFlagを取得することもできたらしい。
07 - anode
解けなかった。ここで足踏みしている間に競技期間が終了した。
おわりに
来年はもう少し頑張りたい。(毎年同じこと言っている)
Discussion