Flare on writeup 2017 writeup

Flareon是主要题目为逆向的一场CTF比赛,已经是第四届了,比赛时间一个月左右。
这是第一次参加这个比赛,今年没拿到牌子,明年再战!



ROT13

题面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>

解题

解rot13即可

1
2
alias rot13="tr '[A-Za-z]' '[N-ZA-Mn-za-m]'"
echo PyvragFvqrYbtvafNerRnfl@syner-ba.pbz | rot13

flag: ClientSideLoginsAreEasy@flare-on.com

Igniteme

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
signed int check()
{
int len; // ST04_4
int i; // [esp+4h] [ebp-8h]
unsigned int ia; // [esp+4h] [ebp-8h]
char key; // [esp+Bh] [ebp-1h]

len = strlen((int)input_cpy);
key = getkey();
for ( i = len - 1; i >= 0; --i )
{
cmpprep[i] = key ^ input_cpy[i];
key = input_cpy[i];
}
for ( ia = 0; ia < 0x27; ++ia )
{
if ( cmpprep[ia] != (unsigned __int8)cmptable[ia] )
return 0;
}
return 1;
}

程序启动后输入字符串XOR然后做对比。

解题

1
2
3
4
5
vp = [0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C, 0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E, 0x56, 0x09, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13, 0x17, 0x48, 0x42, 0x01, 0x40, 0x4D, 0x0C, 0x02, 0x69, 0x04]
for i in range(len(vp)-2, -1, -1):
vp[i] = vp[i] ^ vp[i+1]
vp = ''.join([ chr(i) for i in vp[:-1]])
# R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com

Greektome



这是一个使用套接字从另一台机器接收数据的程序。看看代码我们可以看到IP地址=’127.0.0.1’和端口= 0x8AE = 2222的程序是开放的。Buf 接收4个字节=> len(data_send)<= 4

收到数据后,程序使用loc_40107c []数组中的元素将Buf [0]替换为xor, 并添加22h ,然后将其保存到数组。



实质上
输入被分成多组,每组20个字符,密钥验证算法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# # Key judgement
# for &bytes in 0x40107c -> 0x40107c+0x79:
# bytes ^= key
# bytes += 0x22
# assert( 0xFB5E == sub_4011E6(0x40107C, 0x79) )

# # sub_4011E6(x,y)
# assert( y != 0)
# ebx = x
# eax = 0x14
# {
# di = (int16)var_4
# esi = min(eax, edx)
# edx -= esi
# {
# eax =
# }
# }
# // CMOVx: Conditional move according to X
# v2: 0x79 -> 0x65 -> 0x51 -> 0x3D -> 0x29 -> 0x15 -> 0x01 -> 0

解题

爆破

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
38
39
40
41
42
43
44
45
46
47
48
49
50
def generator(a1,a2):
v2 = a2
v3 = 0xff
v8 = 0xff
if not a2:
raise ValueError
v4 = a1
scanned = 0
while v2:
v5 = v8
v6 = 0x14 if v2>0x14 else v2
v2 = v2 - v6
for i in range(v6):
v5 = v5 + a1[scanned]
v5 = v5 & 0xFFFF
v3 = v3 + v5
v3 = v3 & 0xFFFF
scanned = scanned + 1
#print("% 3d - % 3d"%(scanned, v6))
v8 = (v5 >> 8) + (0xff&v5)
v8 = v8 & 0xFFFF
v3 = (v3 >> 8) + (0xff&v3)
v3 = v3 & 0xFFFF
print("=== % 4x = % 4x ==="%(v8,v3))
return ( (v8 >> 8) + ((0xFF) & v8) ) | ( 0xFFFF & ( (v3 << 8) + ((0xFF00) & v3) ) )

def check(xk):
borg = """
33 E1 C4 99
11 06 81 16 F0 32 9F C4 91 17 06 81 14 F0 06 81
15 F1 C4 91 1A 06 81 1B E2 06 81 18 F2 06 81 19
F1 06 81 1E F0 C4 99 1F C4 91 1C 06 81 1D E6 06
81 62 EF 06 81 63 F2 06 81 60 E3 C4 99 61 06 81
66 BC 06 81 67 E6 06 81 64 E8 06 81 65 9D 06 81
6A F2 C4 99 6B 06 81 68 A9 06 81 69 EF 06 81 6E
EE 06 81 6F AE 06 81 6C E3 06 81 6D EF 06 81 72
E9 06 81 73 7C """
bbs = [(0xff&((i^xk)+0x22)) for i in list(bytes.fromhex(borg.replace("\n", "")))]
return generator(bbs, 0x79)
j = 0
for i in range(0xff):
if ( check(i) == 0xFB5E ):
j=i
break

# j = 0xa2
# Now it is time to check bytes
for i in range(0x79):
ida_bytes.patch_byte(0x40107c+i, ida_bytes.get_byte(0x40107c+i)^j)
# et_tu_brute_force@flare-on.com

Notepad

这个题目就略坑了。。
此题wp参考 杜师傅 https://bbs.xdsec.club/d/116-flare-on-4-challange-problem-1-11-writeup

一道侧重于样本分析的程序。源程序是Windows的记事本,被在另一个段注入了新代码,并修改了OEP。新代码会使用ROR13获取所有加载模块的哈希,并按照如下算法比较并存储之

1
2
3
for module in PEB->LDRDATA->InMemoryOrderModuleList
if HashROR13(module.name)==HashROR13(TargetModuleName):
return module # for futher usage ;)

注意的是,在我的Windows 10中,这段程序会出问题,因为由于一些未知的原因,这段代码在我机器上获取的模块名称为大写,而ROR13的实现是区分大小写的。我们在0x10153F6下断点,断点日志为Parsing {s:edx},查看日志:

1
2
3
4
5
6

Parsing L"4_notepad.exe"
Parsing L"ntdll.DLL"
Parsing L"KERNEL32.DLL"
Parsing L"KERNELBASE.dll"
Parsing L"comdlg32.dll"

可以发现KERNEL32被跳过了。patch掉此处,便于动态调试。

对于被调用的函数,我们可以用Flare-IDA中的Shellcode-hashes脚本来辅助分析。静态分析后发现,程序在遍历%USERPROFILE%/flareon2016challenge目录下的文件,检查其时间是否符合特征值。如果符合,就提取出其中的八个字节到key.bin中,以此类推。Swings师傅提醒后,发现正确的文件是从去年的题目包中下载的(P.S 我觉得这个题设计的一般)。释放文件后再次执行程序,会弹出对话框:

bl457_fr0m_th3_p457@flare-on.com

Pewpewboat

这是一个linux下的游戏
关卡有100关,
我们在静态分析下main函数



该函数genInitialSeed (0x403C85)生成用于“解密”游戏数据的初始种子。我会让你读它,但它本质上MD5是”loading… %d%%”一个循环中的字符串,%d每次递增。
然后,我们将看到99个级别的循环,每次迭代从576个字节的表复制游戏数据,然后使用以下命令解密它seed

我们可以进看一下他的循环



从drawGrid (0x403263)功能上我们可以看到,我们输入的坐标和“船”的实际位置都存储在gameState。该代码将根据用户输入的坐标来检查正确的坐标,并且只有在有匹配的情况下才绘制



我们继续process (0x04038D6)处理从用户输入的功能,检查命中并根据需要提前游戏状态。在这个功能中,你将会看到游戏状态中的更多内容,比如你的排名(例如“Seaman Recruit”):

解题

回到游戏流程。每轮游戏中,你的输入值除了会影响DiskStatus外,还会被原样放到上面结构体的LastInputUpperCase中。如果对应的点在GoodStatus中为True,那么就算击中。此外,每轮游戏中,checksum值都会变,用来解密下一轮游戏。

所以我们通过解数据的方式解决



这个程序算出来的值的前8个字节就是这关的过关关键

依次过关之后会出现

1
Aye! You found some letters did ya? To find what you're looking for, you'll want to re-order them: 9, 1, 2, 7, 3, 5, 6, 5, 8, 0, 2, 3, 5, 6, 1, 4. Next you let 13 ROT in the sea! THE FINAL SECRET CAN BE FOUND WITH ONLY THE UPPER CASE

的提示

重新组织地图字母FHGUZREJVO得到OHGJURERVFGUREHZ
rot13之后得到BUTWHEREISTHERUM

直接输入到程序中就得到flag了。。

y0usUnK_mYP3Wp3w_b04t@flare-on.com

今晚先写到这。。明天继续写

Payload

突然发现国内有有人写的挺完整的 我就不继续往下写了

突然发现国内有有人写的挺完整的 我就不继续往下写了
附上连接
http://blog.nsfocus.net/flare-onchallenge4th/