🔥

Flare-On 9 Challenge - Writeup

2022/11/25に公開

はじめに

Mandiant社が毎年開催しているリバースエンジニアリング分野のCTF、Flare-On Challengeに参加しました。出題された11問のうち、6問目まで解きました。

Easyな問題しか解けなかったうえに公式からもWriteup出ていますが、せっかくなので書きました。

(公式Writeupは以下のページで公開されています)
https://www.mandiant.com/resources/blog/flareon9-challenge-solutions

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通信は下記のような流れで行われている。

  1. ランダムな整数の作成
  2. 作成した整数に接頭辞を付けて"FO9XXXX"のような文字列を作成する。
  3. 接頭辞を付けた文字列のMD5ハッシュ値を取得する。
  4. MD5ハッシュ値を暗号鍵として、RC4暗号で文字列"ahoy"を暗号化する。
  5. 暗号化した文字列をBase64エンコードする。
  6. Base64エンコードした文字列をPOST通信で送信する。通信時のUser-Agent文字列は前半部分が固定で、末尾には1.で作成されたランダムな整数を付与している。
  7. 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を復号している人も居た。
https://0xdf.gitlab.io/flare-on-2022/alamode

Loaderを自作してFlagを取得することもできたらしい。
https://skyblue.team/posts/flareon9/#06---à-la-mode

07 - anode

解けなかった。ここで足踏みしている間に競技期間が終了した。

https://twitter.com/hamasho_sec/status/1591292664141729792

おわりに

来年はもう少し頑張りたい。(毎年同じこと言っている)

Discussion