🦊Back to NiNi's Den

2020::Flare-On 7::Write-up

Word count: 3.3kReading time: 14 min
2020/10/22 Share

喔耶,雖然名次沒有很讚,但還是破台了,又看了一堆奇技淫巧🤔️
今年前面打的太悠閒,導致 11 題差點解不掉,哭啊…
這次解題邊解邊把 writeup 捏的差不多了,順手來發一篇

心得

根據官方的統計數據,今年度台灣總共有 4 個人破台,但這個數據有點失真,因為就我所知至少還有兩個人破台但是沒掛台灣國籍,但也看得出來大家越來越忙,2019 年的時候還有 8 個人。

完賽人數

就看得到的結果來說,我是第 2 個破台的台灣人,不過跟第一名的 Lays 實在是差有點遠🤔️,這次官方還很有趣的統計了國家的人均破台率,我個人是覺得看完笑笑就好了,但新加坡的數字還真是可怕。

人均完賽率

是說寫這種 writeup 都有點麻煩,寫到超級詳細讓沒有打的人都看得懂當然是可行,但是時間成本太高了,所以我就在這裡留個大概,如果真的有人用 Flare-On 在練習 reversing 的時候看不懂的話,可以直接戳我沒關係。

今年的 badge 是鑰匙,挺酷酷的:

1 - fidler

Description

Welcome to the Seventh Flare-On Challenge!
This is a simple game. Win it by any means necessary and the victory screen will reveal the flag. Enter the flag here on this site to score and move on to the next level.
This challenge is written in Python and is distributed as a runnable EXE and matching source code for your convenience.
You can run the source code directly on any Python platform with PyGame if you would prefer.
*7zip password: flare

Solution

從圖片上看得出來是使用 PyInstaller 打包過的程式,直接找用工具把 python bytecode 抽出來,PyInstaller Extractor

同時運行程式看一下有什麼目標,看起來是要先找密碼:

從剛剛 decompile 來的 python code 可以知道密碼是「ghost」:

fidler.py
14
15
16
17
def password_check(input):
altered_key = 'hiptu'
key = ''.join([chr(ord(x) - 1) for x in altered_key])
return input == key

看起來是 pygame,遊戲模式類似 Cookie Clicker

直接 call 解密 flag 的地方:

fidler.py
249
250
251
252
if __name__ == '__main__':
target_amount = 103079215104
print(decode_flag(int(target_amount / 100000000)))
#idle_with_kitty@flare-on.com

2 - garbage

Description

One of our team members developed a Flare-On challenge but accidentally deleted it.
We recovered it using extreme digital forensic techniques but it seems to be corrupted.
We would fix it but we are too busy solving today’s most important information security threats affecting our global economy.
You should be able to get it working again, reverse engineer it, and acquire the flag.
*7zip password: flare

Solution

直接執行題目給的 garbage.exe , windows 直接跳錯誤在臉上:

檢查 PE Header 的各種欄位看起來沒什麼問題,但檢查 Section header 會發現,這是一隻由 UPX 壓縮過的程式,同時最後一個 .rsrc 的 size 不對,根據題目敘述,這裡可以合理推斷該地方的數據遺失,直接 xxd garbage.exe 可以看到尾端的 xml 被截斷了所以沒有閉合:

00009e40: 0904 0000 4800 0000 5c90 0100 7d01 0000  ....H...\...}...
00009e50: 0000 0000 0000 0000 6050 0100 3c3f 786d ........`P..<?xm
00009e60: 6c20 7665 7273 696f 6e3d 2731 2e30 2720 l version='1.0'
00009e70: 656e 636f 6469 6e67 3d27 5554 462d 3827 encoding='UTF-8'
00009e80: 2073 7461 6e64 616c 6f6e 653d 2779 6573 standalone='yes
00009e90: 273f 3e0d 0a3c 6173 7365 6d62 6c79 2078 '?>..<assembly x
00009ea0: 6d6c 6e73 3d27 7572 6e3a 7363 6865 6d61 mlns='urn:schema
00009eb0: 732d 6d69 6372 6f73 6f66 742d 636f 6d3a s-microsoft-com:
00009ec0: 6173 6d2e 7631 2720 6d61 6e69 6665 7374 asm.v1' manifest
00009ed0: 5665 7273 696f 6e3d 2731 2e30 273e 0d0a Version='1.0'>..
00009ee0: 2020 3c74 7275 7374 496e 666f 2078 6d6c <trustInfo xml
00009ef0: 6e73 3d22 7572 6e3a 7363 6865 6d61 732d ns="urn:schemas-
00009f00: 6d69 6372 6f73 6f66 742d 636f 6d3a 6173 microsoft-com:as
00009f10: 6d2e 7633 223e 0d0a 2020 2020 3c73 6563 m.v3">.. <sec
00009f20: 7572 6974 0a urit.

總之就是把 IAT 修好,讓 UPX 找得到需要用的 Dll 跟對應的 function,因為是執行檔所以 relocation 可以不管,修好的 binary 我放在這裏,直接執行就會生成名為 sink_the_tanker.vbs 的腳本負責把 flag 直接印出來:

3 - wednesday

Description

Be the wednesday. Unlike challenge 1, you probably won’t be able to beat this game the old fashioned way. Read the README.txt file, it is very important.

README.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
██╗    ██╗███████╗██████╗ ███╗   ██╗███████╗███████╗██████╗  █████╗ ██╗   ██╗
██║ ██║██╔════╝██╔══██╗████╗ ██║██╔════╝██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝
██║ █╗ ██║█████╗ ██║ ██║██╔██╗ ██║█████╗ ███████╗██║ ██║███████║ ╚████╔╝
██║███╗██║██╔══╝ ██║ ██║██║╚██╗██║██╔══╝ ╚════██║██║ ██║██╔══██║ ╚██╔╝
╚███╔███╔╝███████╗██████╔╝██║ ╚████║███████╗███████║██████╔╝██║ ██║ ██║
╚══╝╚══╝ ╚══════╝╚═════╝ ╚═╝ ╚═══╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝

--- BE THE WEDNESDAY ---

S
M
T
DUDE
T
F
S

--- Enable accelerated graphics in VM ---
--- Attach sound card device to VM ---
--- Only reverse mydude.exe ---
--- Enjoy it my dudes ---%

Solution

從 dll 可以知道是用 SDL 捏出來的遊戲,打開個五分鐘大概就能抓到規則,只要有 M 這個箱子就必須從箱子的下面通過,否則就一定要從上面跳過去:

稍微看一下 binary 可以發現這個程式是用 nim 撰寫的,他的特色是會被編譯成等價的 C 或是 C++ 等語言,總之不同於 bytecode,nim 造出來的檔案是無法直接 decompile 的,不過 function 的 symbol 倒是沒有全部都拔除,可以粗略的猜得出相關功能:

這裏直接從碰撞判定下手,在 onCollide__9byAjE9cSmbSbow3F9cTFQfLg 中可以發現角色的碰撞判定以及分數判定,function 的上半部(不在圖片上)是角色直接碰撞箱子本體的情況,無視,直接把下半段關於 M 箱子判斷的 if 條件式(截圖中最上面的那條 if) patch 成一定會進入加分的程式碼,同時可以在 update__Arw3f6ryHvqdibU49aaayOg 的最下面知道分數到達 296 之後可以看到 flag:

patch 完之後就無腦玩,一直蹲下直到分數到 296 就好了,直接用 cheat engine 做加速:

4 - report

Description

Nobody likes analysing infected documents, but it pays the bills. Reverse this macro thrill-ride to discover how to get it to show you the key.

Solution

看起來是 office 的巨集病毒,少東西所以不會真的執行,直接看巨集的部分,大部分的東西都是簡單的 encode,首先把一些物件用到的字串解出來,其中可以看到有些字串並沒有真的被使用到,可能需要猜一下被拿去做了什麼事情:

string.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def rigmarole(a):
t = ''
for i in range(0,len(a),4):
t += chr(int(a[i:i+2],16)-int(a[i+2:i+4],16))
return t

def folderol():
onzo = "9655B040B64667238524D15D6201.B95D4E01C55CC562C7557405A532D768C55FA12DD074DC697A06E172992CAF3F8A5C7306B7476B38.C555AC40A7469C234424.853FA85C470699477D3851249A4B9C4E.A855AF40B84695239D24895D2101D05CCA62BE5578055232D568C05F902DDC74D2697406D7724C2CA83FCF5C2606B547A73898246B4BC14E941F9121D464D263B947EB77D36E7F1B8254.853FA85C470699477D3851249A4B9C4E.9A55B240B84692239624.CC55A940B44690238B24CA5D7501CF5C9C62B15561056032C468D15F9C2DE374DD696206B572752C8C3FB25C3806.A8558540924668236724B15D2101AA5CC362C2556A055232AE68B15F7C2DC17489695D06DB729A2C723F8E5C65069747AA389324AE4BB34E921F9421.CB55A240B5469B23.AC559340A94695238D24CD5D75018A5CB062BA557905A932D768D15F982D.D074B6696F06D5729E2CAE3FCF5C7506AD47AC388024C14B7C4E8F1F8F21CB64".split(".")
for idx, i in enumerate(onzo):
print(idx,i)
print(f"onzo[{idx}] = {rigmarole(i)}")

folderol()
# 0 9655B040B64667238524D15D6201
# onzo[0] = AppData
# 1 B95D4E01C55CC562C7557405A532D768C55FA12DD074DC697A06E172992CAF3F8A5C7306B7476B38
# onzo[1] = \Microsoft\stomp.mp3
# 2 C555AC40A7469C234424
# onzo[2] = play
# 3 853FA85C470699477D3851249A4B9C4E
# onzo[3] = FLARE-ON
# 4 A855AF40B84695239D24895D2101D05CCA62BE5578055232D568C05F902DDC74D2697406D7724C2CA83FCF5C2606B547A73898246B4BC14E941F9121D464D263B947EB77D36E7F1B8254
# onzo[4] = Sorry, this machine is not supported.
# 5 853FA85C470699477D3851249A4B9C4E
# onzo[5] = FLARE-ON
# 6 9A55B240B84692239624
# onzo[6] = Error
# 7 CC55A940B44690238B24CA5D7501CF5C9C62B15561056032C468D15F9C2DE374DD696206B572752C8C3FB25C3806
# onzo[7] = winmgmts:\\.\root\CIMV2
# 8 A8558540924668236724B15D2101AA5CC362C2556A055232AE68B15F7C2DC17489695D06DB729A2C723F8E5C65069747AA389324AE4BB34E921F9421
# onzo[8] = SELECT Name FROM Win32_Process
# 9 CB55A240B5469B23
# onzo[9] = vbox
# 10 AC559340A94695238D24CD5D75018A5CB062BA557905A932D768D15F982D
# onzo[10] = WScript.Network
# 11 D074B6696F06D5729E2CAE3FCF5C7506AD47AC388024C14B7C4E8F1F8F21CB64
# onzo[11] = \Microsoft\v.png

繼續往下看可以發現,巨集會解密一段 data 然後儲存為 stomp.mp3,寫個 python 模仿他解密,但我們得到的音檔聽起來跟 flag 一點關係也沒有,不過可以注意到這裡也有部分的 data 是完全沒有被使用到的:

decrypt.py
1
2
3
4
5
6
f = open("./T",'r').read().strip()
key = [0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee]

kuffle = bytes([(int(f[i:i+2],16) ^ key[(i//4)%len(key)]) for i in range(0,168667*4,4)])
with open("stomp.mp3",'wb') as z:
z.write(kuffle)

沒有用到的是 data 的另外一半,以及一些字串 \Microsoft\v.pngFLARE-ON,如果把 data 抽出來,然後假設檔頭是 PNG 的格式,就可以發現 xor 出來的結果是 NO-E,所以可以直接猜 key 就是 "FLARE-ON"[::-1],直接寫腳本解:

flag.py
1
2
3
4
5
6
f = open("./T",'r').read().strip()
key = b'FLARE-ON'[::-1]

kuffle = [int(f[i:i+2],16) ^ key[((i-2)//4)%len(key)] for i in range(2,len(f),4)]
with open("flag.png",'wb') as z:
z.write(bytes(kuffle))

然後就拿到 flag 的圖片了:

5 - TKApp

Description

Now you can play Flare-On on your watch! As long as you still have an arm left to put a watch on, or emulate the watch’s operating system with sophisticated developer tools.

Solution

一樣是滿無聊的一題,題目給的是 Galaxy Watch 的應用程式,但動態並沒有什麼明顯的好處,先直接靜態理解一下,直接解壓縮 TKApp.tpk 後走走看看,應該能夠確定應用程式的部分是 .NET 程式:

ExifLib.Standard.dll:                         PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
TKApp.dll: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows
Tizen.Wearable.CircularUI.Forms.Renderer.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
Tizen.Wearable.CircularUI.Forms.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
Xamarin.Forms.Core.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
Xamarin.Forms.Platform.Tizen.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
Xamarin.Forms.Platform.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
Xamarin.Forms.Xaml.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows

總之拿個 .NET 的 decompiler 看一下,程式拿一堆字串來當作 AES 的 key,用來解出 flag,所有的東西都能直接找到,去找 app description,圖片的 exif 資訊等等,然後身為 cipher 的 Runtime.dll 則是直接躺在 TPK.dll 裡面,直接寫解密

decrypt.py
1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import AES
from hashlib import sha256
from base64 import *

IV = b'NoSaltOfTheEarth'
key = b'the kind of challenges we are gonna make here'
cipher = open("Runtime.dll",'rb').read()
decryptor = AES.new(sha256(key).digest(),AES.MODE_CBC,iv=IV)
f = decryptor.decrypt(cipher)
with open("flag.png",'wb') as flag:
flag.write(b64decode(f))

6 - codeit

Description

Reverse engineer this little compiled script to figure out what you need to do to make it give you the flag (as a QR code).

Solution

binary 是一個會把輸入做成 QRcode 之後用圖片展示出來程式,看起來是這樣

觀察一下 binary 裡面的字串不難發現這是一個從 AutoIt 編譯而來的程式,隨便找一下就可以找到工具 能夠做到 extract byte code 跟 decompile byte code,這裡我是用 AutoIt-Ripper

程式碼算是重度混淆,不過最後的邏輯是這樣的,使用 Windows API GetComputerNameA 先取得電腦名字,然後對其做 SHA-256 之後可以得到一個 hash,之後用在印出真的 QRcode 之前,程式會先偷偷用這個 hash 當作 AES 的 key 來解密一個 cipher,如果解出來的起頭是 FLARE 且同時結尾是 ERALF 時,程式會直接把解出來的東西當作 QRcode 用圖片展示出來,否則就展示本來的輸入所對應的 QRcode,也就是說我們要找到正確的 ComputerName。

在 227 行附近可以看到一些 bitwise 的操作( function 我重命名過了):

script.au3
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
FUNC readSpriteBmp ( BYREF $computerName )
LOCAL $FLQVIZHEZM = InstallFile ( 14 )
LOCAL $FLFWEZDBYC = createFile ( $FLQVIZHEZM )
IF $FLFWEZDBYC <> - $FLTGQYKODM THEN
LOCAL $FLVBURIUYD = _GetFileSize_ ( $FLFWEZDBYC )
IF $FLVBURIUYD <> - $FLTMXODMFL AND DLLSTRUCTGETSIZE ( $computerName ) < $FLVBURIUYD - 54 THEN
LOCAL $FLNFUFVECT = DLLSTRUCTCREATE (_Hex2Str_( 'struct;byte[' & '269156' & '];endstruct')
LOCAL $FLSKUANQBG = ReadFile ( $FLFWEZDBYC , $FLNFUFVECT )
IF $FLSKUANQBG <> - $FLVUJARIHO THEN
LOCAL $FLXMDCHRQD = DLLSTRUCTCREATE ( 'struct;byte[54];byte[' & $FLVBURIUYD - 54 & '];endstruct', DLLSTRUCTGETPTR ( $FLNFUFVECT ) )
LOCAL $FLQGWNZJZC = 1
LOCAL $FLOCTXPGQH = ''
FOR $FLTERGXSKH = 1 TO DLLSTRUCTGETSIZE ( $computerName )
LOCAL $FLYDTVGPNC = NUMBER ( DLLSTRUCTGETDATA ( $computerName , 1 , $FLTERGXSKH ) )
FOR $FLTAJBYKXX = 6 TO 0 STEP -1
$FLYDTVGPNC += BITSHIFT ( BITAND ( NUMBER ( DLLSTRUCTGETDATA ( $FLXMDCHRQD , 2 , $FLQGWNZJZC ) ) , 1 ) , -1 * $FLTAJBYKXX )
$FLQGWNZJZC += 1
NEXT
$FLOCTXPGQH &= CHR ( BITSHIFT ( $FLYDTVGPNC ,1) + BITSHIFT ( BITAND ( $FLYDTVGPNC ,1) , -7 ) )
NEXT
DLLSTRUCTSETDATA ( $computerName , 1 , $FLOCTXPGQH )
ENDIF
ENDIF
CloseHandle ( $FLFWEZDBYC )
ENDIF
DeleteFileA ( $FLQVIZHEZM )
ENDFUNC

他做的事情就是把一開始看到的那個在敲鍵盤的 flare icon 的每 7bits 組起來,這聽起來就是 ASCII,所以如果我們照做的話就可以抽出這段字 aut01tfan1999 這就是正確的電腦名稱,最後就是看要直接改 ComputerName 還是 Hook Function,或是直接解密都行。

Flag: L00ks_L1k3_Y0u_D1dnt_Run_Aut0_Tim3_0n_Th1s_0ne!@flare-on.com


下面的題目忘記截圖…,雖然都記得很清楚,但是懶得寫ㄌ,我就寫個大概就好了,反正有官方 writeup,2ㄏ2ㄏ。

7 - re_crowd

會拿到一個 pcap 檔案,實際上是攻擊者在用 CVE 攻擊伺服器並且偷檔案,裏面包含 alphanumeric unicode shellcode 把它倒出來分析看懂就好了,解完 RC4 就可以得到 flag。

8 - Aardvark

這支程式利用 wsl 叫起一個 ELF 執行檔(放在 PE resource 裏面),之後用 UNIX Socket 溝通來玩 OOXX(Tic-tac-toe) 的遊戲,由於 Tic-tac-toe 在玩家都下最佳位置時是必定平手的遊戲,解法就是把 ELF 裏面初始化遊戲盤面的地方改成預先放一堆 O 這樣就必勝了。

9 - crackinstaller

crackinstaller 會先安裝一個有簽章且能繞過 SMEP 的 driver 之後透過這個 driver 安裝一個沒有簽章的 driver,目標是要在登錄檔裡面寫上正確的密碼然後寫一個 COM 的 Client 跟 crackinstaller 安裝的 COM 做互動,最後就可以拿到 flag。

比較有趣的點是「一個有簽章且能繞過 SMEP 的 driver 」,一開始我解完題目後一直覺得裡面的 magic number 感覺可以直接查到,搞不好我不用花這麼多時間逆向,結果發現這東西根本就是卡普空的東西,PC 版的 Street Fighter V 的 driver 而且他唯一的功能就是把 SMEP 關掉然後跳上 userland 執行任意程式,天啊卡普空…

10 - break

簡單來說就是執行主要程式後,主要程式會 fork B 來 debug 自己,B 會 fork C 來 debug 自己,邏輯都被往下外包,因為 C 很簡單,所以可以把那些功能都 patch 回 B,這樣就可以用 gdb 跟 B 在做什麼了,就醬子,後面就想辦法搞一搞就會有 flag 了。

11 - rabbithole

非常惡劣,極盡我通靈之能把全部的樣本都看完了,但其實不用看 32bit 那一部分的 1x 個 payload,總之裡面的 PE 檔案都被 PX 這個算法給包起來了,首先要先寫腳本把他們好好解開,然後就可以看懂 dll 之間互相呼叫,被加密的 flag 在 DiMap 這個登錄檔裡面,解開就是了。

Original Author: Terrynini

Original link: http://blog.terrynini.tw/tw/2020-Flare-On-7-Write-up/

Publish at: October 22nd 2020, 3:57:07

Copyright: This article is licensed under CC BY-NC 4.0

CATALOG
  1. 1. 心得
  2. 2. 1 - fidler
    1. 2.1. Description
    2. 2.2. Solution
  3. 3. 2 - garbage
    1. 3.1. Description
    2. 3.2. Solution
  4. 4. 3 - wednesday
    1. 4.1. Description
    2. 4.2. Solution
  5. 5. 4 - report
    1. 5.1. Description
    2. 5.2. Solution
  6. 6. 5 - TKApp
    1. 6.1. Description
    2. 6.2. Solution
  7. 7. 6 - codeit
    1. 7.1. Description
    2. 7.2. Solution
  8. 8. 7 - re_crowd
  9. 9. 8 - Aardvark
  10. 10. 9 - crackinstaller
  11. 11. 10 - break
  12. 12. 11 - rabbithole