GUETCTF 2019 number game
爆破
看了一遍代码,不多,但逻辑感觉挺恶心的,所以我们要像神·RX
学习,爆破!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from pwn import * import os def vio(tmp): print("test_",": try_",tmp)
p = process("./number_game") p.sendline(tmp) re = p.recv() if b'T' in re: print("ans: ",tmp); os.system('spd-say "Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh Ahh your program has finished"') a=input() p.close() def fuck(cnt,tmp): if cnt==10: vio(tmp) return for i in range(48,53): fuck(cnt+1,tmp+chr(i))
fuck(0,"")
|
这里用pwntools
与程序进行交互。这个爆破程序感觉有很多地方可以优化,例如多线程,但是多线程又不太会……所以只能手动修改代码来跑,同时运行多个,也算是多线程了……(逃
挖个坑吧,抽空学学多线程……
但是好像pwntools
的效率比较低……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import subprocess from itertools import * list = '01234' j = 0 for i in product(list, repeat=10): input = "".join(i)
obj = subprocess.Popen(["./number_game"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) obj.stdin.write(input) obj.stdin.close() cmd_out = obj.stdout.read() obj.stdout.close() print input print cmd_out
if 'cxk' not in cmd_out : print "bingo!!!!!! : ",input exit()
|
这是网上嫖的wp
,貌似效率很高……下来再学习以下这个。继续挖坑……
angr
md
,我第一遍的angr
脚本少了一个avoid
导致没跑出来,后来瞅瞅树树的才发现……
1 2 3 4 5 6 7
| import angr p = angr.Project("./number_game",load_options={"auto_load_libs": False}) sta = p.factory.entry_state() sim = p.factory.simulation_manager(sta) sim.explore(find=0x400AC1,avoid=[0x400AFC,0x4006F4,0x400736,0x4009DF]) print(sim.found[0].posix.dumps(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
| unsigned __int64 __fastcall main(int a1, char **a2, char **a3) { char *v4; __int64 input; __int16 v6; __int64 v7; __int16 v8; char v9; unsigned __int64 v10;
v10 = __readfsqword(0x28u); input = 0LL; v6 = 0; v7 = 0LL; v8 = 0; v9 = 0; __isoc99_scanf("%s", &input); if ( !(unsigned int)sub_4006D6((const char *)&input) ) { v4 = (char *)sub_400758((char *)&input, 0, 0xAu); sub_400807(v4, (int *)&v7); v9 = 0; sub_400881((char *)&v7); if ( (unsigned int)sub_400917() ) { puts("TQL!"); printf("flag{"); printf("%s", (const char *)&input); puts("}"); } else { puts("your are cxk!!"); } } return __readfsqword(0x28u) ^ v10; }
|
那我们能不能这样,我们把校验输入的sub_4006D6
给patch
掉,然后将0123456789
作为输入,反正程序没有修改输入,只是换了一个顺序:
然后再通过sub_400881
函数进行修改sub_601062~sub_601077
的值,下来就是看sub_400917
的内容,来看看他的判断条件:
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
| 0X601060 != 0X601061 0X601065 != 0X601066 0X60106A != 0X60106B 0X601060 != 0X601062 0X601065 != 0X601067 0X60106A != 0X60106C 0X601060 != 0X601063 0X601065 != 0X601068 0X60106A != 0X60106D 0X601060 != 0X601064 0X601065 != 0X601069 0X60106A != 0X60106E 0X601061 != 0X601062 0X601066 != 0X601067 0X60106B != 0X60106C 0X601061 != 0X601063 0X601066 != 0X601068 0X60106B != 0X60106D 0X601061 != 0X601064 0X601066 != 0X601069 0X60106B != 0X60106E 0X601062 != 0X601063 0X601067 != 0X601068 0X60106C != 0X60106D 0X601062 != 0X601064 0X601067 != 0X601069 0X60106C != 0X60106E 0X601063 != 0X601064 0X601068 != 0X601069 0X60106D != 0X60106E
0X60106F != 0X601070 0X601074 != 0X601075 0X601060 != 0X601065 0X60106F != 0X601071 0X601074 != 0X601076 0X601060 != 0X60106A 0X60106F != 0X601072 0X601074 != 0X601077 0X601060 != 0X60106F 0X60106F != 0X601073 0X601074 != 0X601078 0X601060 != 0X601074 0X601070 != 0X601071 0X601075 != 0X601076 0X601065 != 0X60106A 0X601070 != 0X601072 0X601075 != 0X601077 0X601065 != 0X60106F 0X601070 != 0X601073 0X601075 != 0X601078 0X601065 != 0X601074 0X601071 != 0X601072 0X601076 != 0X601077 0X60106A != 0X60106F 0X601071 != 0X601073 0X601076 != 0X601078 0X60106A != 0X601074 0X601072 != 0X601073 0X601077 != 0X601078 0X60106F != 0X601074
0X601061 != 0X601066 0X601062 != 0X601067 0X601063 != 0X601068 0X601061 != 0X60106B 0X601062 != 0X60106C 0X601063 != 0X60106D 0X601061 != 0X601070 0X601062 != 0X601071 0X601063 != 0X601072 0X601061 != 0X601075 0X601062 != 0X601076 0X601063 != 0X601077 0X601066 != 0X60106B 0X601067 != 0X60106C 0X601068 != 0X60106D 0X601066 != 0X601070 0X601067 != 0X601071 0X601068 != 0X601072 0X601066 != 0X601075 0X601067 != 0X601076 0X601068 != 0X601077 0X60106B != 0X601070 0X60106C != 0X601071 0X60106D != 0X601072 0X60106B != 0X601075 0X60106C != 0X601076 0X60106D != 0X601077 0X601070 != 0X601075 0X601071 != 0X601076 0X601072 != 0X601077
0X601064 != 0X601069 0X601064 != 0X60106E 0X601064 != 0X601073 0X601064 != 0X601078 0X601069 != 0X60106E 0X601069 != 0X601073 0X601069 != 0X601078 0X60106E != 0X601073 0X60106E != 0X601078 0X601073 != 0X601078
|
这里先简述一下他的规律:从0x601060
开始到0x601078
,每五个分成一组,每组不能有重复的,仅为01234
;每一组的第n
个数字不能相同;以下受输入影响:
1 2 3 4 5 6 7 8 9 10
| byte_601062 = a1[0]; byte_601067 = a1[1]; byte_601069 = a1[2]; byte_60106B = a1[3]; byte_60106E = a1[4]; byte_60106F = a1[5]; byte_601071 = a1[6]; byte_601072 = a1[7]; byte_601076 = a1[8]; byte_601077 = a1[9];
|
那我们根据规律排除出受输入影响的几个数字应该是多少(有数独内味了~),好吧,就是数独,一个5*5的数独。然后再按照顺序还原成输入:
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
| 0x601060 = 31h ; 1 0x601061 = 34h ; 4 0x601062 = a1[0] = 30h ; 0 0x601063 = 32h ; 2 0x601064 = 33h ; 3
0x601065 = 33h ; 3 0x601066 = 30h ; 0 0x601067 = a1[1] = 34h ; 4 0x601068 = 31h ; 1 0x601069 = a1[2] = 32h ; 2
0x60106A = 30h ; 0 0x60106B = a1[3] = 31h ; 1 0x60106C = 32h ; 2 0x60106D = 33h ; 3 0x60106E = a1[4] = 34h ; 4
0x60106F = a1[5] = 32h ; 2 0x601070 = 33h ; 3 0x601071 = a1[6] = 31h ; 1 0x601072 = a1[7] = 34h ; 4 0x601073 = 30h ; 0
0x601074 = 34h ; 4 0x601075 = 32h ; 2 0x601076 = a1[8] = 33h ; 3 0x601077 = a1[9] = 30h ; 0 0x601078 = 31h ; 1
|
再按照
这个顺序还原成输入就好了~
HITCTF 2020 Node
好难,先鸽着……
GKCTF 2020 BabyDriver
DIE,IDA64,F12。
怎么像一个maze
题……
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| **************** o.*..*......*..* *.**...**.*.*.** *.****.**.*.*.** *...**....*.*.** ***..***.**.*..* *.**.***.**.**.* *.**.******.**.* *.**....***.**.* *.*****.***....* *...***.******** **..***......#** **.************* ****************
|
起点和终点明了,主要是上下左右。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| __int64 __fastcall sub_140001380(__int64 a1, __int64 a2) { __int64 v3; __int64 v4; int v5; __int16 *v6; __int64 v7; __int16 v8; char v9; const CHAR *v10;
if ( *(int *)(a2 + 48) >= 0 ) { v3 = *(_QWORD *)(a2 + 24); v4 = *(_QWORD *)(a2 + 56) >> 3; if ( (_DWORD)v4 ) { v5 = dword_1400030E4; v6 = (__int16 *)(v3 + 2); v7 = (unsigned int)v4; while ( *(_WORD *)(v3 + 4) ) { LABEL_28: v6 += 6; if ( !--v7 ) goto LABEL_29; } aO[v5] = '.'; v8 = *v6; if ( *v6 == 23 ) { if ( (v5 & 0xFFFFFFF0) != 0 ) { v5 -= 16; goto LABEL_21; } v5 += 208; dword_1400030E4 = v5; } if ( v8 == 37 ) { if ( (v5 & 0xFFFFFFF0) != 208 ) { v5 += 16; goto LABEL_21; } v5 -= 208; dword_1400030E4 = v5; } if ( v8 == 36 ) { if ( (v5 & 0xF) != 0 ) { --v5; goto LABEL_21; } v5 += 15; dword_1400030E4 = v5; } if ( v8 != 38 ) goto LABEL_22; if ( (v5 & 0xF) == 15 ) v5 -= 15; else ++v5; LABEL_21: dword_1400030E4 = v5; LABEL_22: v9 = aO[v5]; if ( v9 == '*' ) { v10 = "failed!\n"; } else { if ( v9 != '#' ) { LABEL_27: aO[v5] = 111; goto LABEL_28; } v10 = "success! flag is flag{md5(input)}\n"; } dword_1400030E4 = 16; DbgPrint(v10); v5 = dword_1400030E4; goto LABEL_27; } } LABEL_29: if ( *(_BYTE *)(a2 + 65) ) *(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u; return *(unsigned int *)(a2 + 48); }
|
其实很容易看出来哪些分支是上下左右,,,但是23,36,37,38
明显不是常规的ASCII。上网查了一波才知道是键盘扫描码,对应着ijkl
手动走一下迷宫:
LKKKLLKLKKKLLLKKKLLLLLL
flag{403950a6f64f7fc4b655dea696997851}