GUETCTF 2019 number game
爆破
看了一遍代码,不多,但逻辑感觉挺恶心的,所以我们要像神·RX
学习,爆破!!!
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
的效率比较低……
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
导致没跑出来,后来瞅瞅树树的才发现……
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))
#b'1134240024\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
硬刚
总之爆破交给虚拟机去跑,我们继续分析程序:
我们就一点一点分析函数(含树……,
unsigned __int64 __fastcall main(int a1, char **a2, char **a3)
{
char *v4; // [rsp+8h] [rbp-38h]
__int64 input; // [rsp+10h] [rbp-30h] BYREF
__int16 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h] BYREF
__int16 v8; // [rsp+28h] [rbp-18h]
char v9; // [rsp+2Ah] [rbp-16h]
unsigned __int64 v10; // [rsp+38h] [rbp-8h]
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) )// 48<= input <=52
{
v4 = (char *)sub_400758((char *)&input, 0, 0xAu);// 根据输入形成了一个类似链表或者树一样的东西……
sub_400807(v4, (int *)&v7); // 把输入换了一个顺序,存储到dword_601080里面,同时改变了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
的内容,来看看他的判断条件:
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
个数字不能相同;以下受输入影响:
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的数独。然后再按照顺序还原成输入:
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
题……
****************
o.*..*......*..*
*.**...**.*.*.**
*.****.**.*.*.**
*...**....*.*.**
***..***.**.*..*
*.**.***.**.**.*
*.**.******.**.*
*.**....***.**.*
*.*****.***....*
*...***.********
**..***......#**
**.*************
****************
起点和终点明了,主要是上下左右。
__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
__int64 v3; // rdi
__int64 v4; // rax
int v5; // ecx
__int16 *v6; // rsi
__int64 v7; // rbp
__int16 v8; // dx
char v9; // dl
const CHAR *v10; // rcx
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}