ISCTF

crakeme
用exeinfo打开,发现又upx壳
直接上脱壳
报错upx: D:\google\a\crackme.exe: CantUnpackException: file is possibly modified/hacked/protected; take care!
网上搜一下大概率是数据段被修改了,导致upx不敢脱壳,两种方式解决,手动改回来,或者用改过的upx
我选择手动更省事,exeinfo可以看到一些相关信息,
image.png
这里看出再name那一行中upx被改为了pfx,通过010改回来后就可以正常脱壳
脱完后放进ida中就看到flag
easyre

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
_main();
strcpy(v4, "]P_ISRF^PCY[I_YWERYC");
memset(v5, 0, sizeof(v5));
v6 = 0;
v7 = 0;
puts("please input your strings:");
gets(Str);
v10 = strlen(Str);
while ( Str[i] )
{
for ( i = 0; i < v10; ++i )
v8[i] = Str[i] ^ 0x11;
}
for ( i = 0; i < v10; ++i )
{
if ( v8[i] == 'B' || v8[i] == 'X' )
v8[i] = 155 - v8[i];
}
for ( i = v10 - 1; i >= 0; --i )
v8[v10 - i - 1] = v8[i];
i = 0;
if ( v10 > 0 )
{
if ( v8[i] == v4[i] )
printf("yes!!!");
else
printf("no!!!");
}
return 0;

字符串定位后,看到关键代码,代码不难,大致看一下逻辑关系就可以理出来
str->v8->替换一些字符->字符串对称交换(但是似乎代码有问题)
逻辑很简单,代码也很简单

1
2
3
4
5
6
7
8
9
10
11
a = ']P_ISRF^PCY[I_YWERYC'
flag = []
for i in range(len(a) - 1, 0, -1):
if ord(a[i]) == 155 - ord('B'):
flag.append('B')
elif ord(a[i]) == 155 - ord('X'):
flag.append('X')
else:
flag.append(a[i])
for i in range(len(flag)):
print(chr(ord(flag[i]) ^ 0x11), end='')

babyre
下载下来后,发现图标是python打包,于是使用pyinstxtractor和uncomple6将其中主要文件反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
p+q=292884018782106151080211087047278002613718113661882871562870811030932129300110050822187903340426820507419488984883216665816506575312384940488196435920320779296487709207011656728480651848786849994095965852212548311864730225380390740637527033103610408592664948012814290769567441038868614508362013860087396409860
(p+1)*(q+1)=21292789073160227295768319780997976991300923684414991432030077313041762314144710093780468352616448047534339208324518089727210764843655182515955359309813600286949887218916518346391288151954579692912105787780604137276300957046899460796651855983154616583709095921532639371311099659697834887064510351319531902433355833604752638757132129136704458119767279776712516825379722837005380965686817229771252693736534397063201880826010273930761767650438638395019411119979149337260776965247144705915951674697425506236801595477159432369862377378306461809669885764689526096087635635247658396780671976617716801660025870405374520076160
c=5203005542361323780340103662023144468501161788183930759975924790394097999367062944602228590598053194005601497154183700604614648980958953643596732510635460233363517206803267054976506058495592964781868943617992245808463957957161100800155936109928340808755112091651619258385206684038063600864669934451439637410568700470057362554045334836098013308228518175901113235436257998397401389511926288739759268080251377782356779624616546966237213737535252748926042086203600860251557074440685879354169866206490962331203234019516485700964227924668452181975961352914304357731769081382406940750260817547299552705287482926593175925396
import libnum
from crypto.Util.number import *
flag = 'ISCTF{******************}'
flags = flag.encode()
e = 65537
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n = p * q
m = bytes_to_long(flags)
c = pow(m, e, n)
output = open('output.txt', 'w')
output.write('p+q =' + str(p + q) + '\n')
output.write('(p+1)*(q+1)=' + str((p + 1) * (q + 1)) + '\n')
output.write('c=' + str(c) + '\n')
output.close()

得到一下这些代码,这段代码我想挺久想不出来
大概思路是,通过(q+1)(p+1)和q+p得出pq的值也就是n,得出pow(in+c,1/e)=m,最后算出bytes_to_long(flags)
但是我怎么算都不行,感觉精度都不够,最后由我沈总秒杀,直接出flag
easy_z3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
print("Please input flag:")
flag = input()
if len(flag)!=42:
print("Check your length!")
exit()

l=[]
for i in range(6):
s=""
for j in flag[i*7:i*7+7]:
s+=hex(ord(j))[2:]
l.append(int(s,16))
if (
(593*l[5] + 997*l[0] + 811*l[1] + 258*l[2] + 829*l[3] + 532*l[4])== 0x54eb02012bed42c08 and \
(605*l[4] + 686*l[5] + 328*l[0] + 602*l[1] + 695*l[2] + 576*l[3])== 0x4f039a9f601affc3a and \
(373*l[3] + 512*l[4] + 449*l[5] + 756*l[0] + 448*l[1] + 580*l[2])== 0x442b62c4ad653e7d9 and \
(560*l[2] + 635*l[3] + 422*l[4] + 971*l[5] + 855*l[0] + 597*l[1])== 0x588aabb6a4cb26838 and \
(717*l[1] + 507*l[2] + 388*l[3] + 925*l[4] + 324*l[5] + 524*l[0])== 0x48f8e42ac70c9af91 and \
(312*l[0] + 368*l[1] + 884*l[2] + 518*l[3] + 495*l[4] + 414*l[5])== 0x4656c19578a6b1170):
print("Good job!")
else:
print("Wrong\nTry again!!!")
exit()

给出了python文件必然是用z3库来解方程

1
2
3
4
5
6
7
8
9
10
11
12
13
from z3 import *
l=[0]*6
a,b,c,d,e,f=Ints('a b c d e f')
z=Solver()
z.add((593*f + 997*a + 811*b + 258*c + 829*d + 532*e)== 0x54eb02012bed42c08)
z.add((605*e + 686*f + 328*a + 602*b + 695*c + 576*d)== 0x4f039a9f601affc3a)
z.add((373*d + 512*e + 449*f + 756*a + 448*b + 580*c)== 0x442b62c4ad653e7d9)
z.add((560*c + 635*d + 422*e + 971*f + 855*a + 597*b)== 0x588aabb6a4cb26838)
z.add((717*b + 507*c + 388*d + 925*e + 324*f + 524*a)== 0x48f8e42ac70c9af91)
z.add((312*a + 368*b + 884*c + 518*d + 495*e + 414*f)== 0x4656c19578a6b1170)
print(z.check())
print(z.model())

1
2
3
4
5
6
[d = 32765855640286324,
b = 13615593641303915,
a = 20639221941697358,
f = 26860403902456189,
c = 31015537033047360,
e = 28554726411354222]

得到这串答案,这串是由将flag每7个字符分一段的方式分为了6段,每段将他们的ascii的16进制数拼接然后转为10进制得到的结果,然后随便找个16进制转数组,或者自己写也可以,得到flag
where
放入32位ida中,通过字符串找到位置

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
{
if ( a1 > 3 )
{
if ( a1 != 100 )
{
if ( a1 == 404 )
{
printf("Debug Error");
ExitProcess(0);
}
if ( a1 == 666 )
{
printf("Please try again.");
ExitProcess(0);
}
LABEL_18:
printf("???");
ExitProcess(0);
}
printf("Yeah!You are right!\nThe flag is flag{input1+input2}");
}
else
{
switch ( a1 )
{
case 3:
printf("Now,You need input password.");
break;
case 0:
printf("Invalid ID");
ExitProcess(0);
case 1:
printf("Length is incorrect!");
ExitProcess(0);
case 2:
printf("Welcome, please enter the correct ID");
break;
default:
goto LABEL_18;
}
}
return printf("\n");
}

可以看到已经出现了flag,然后发现进入flag分支的条件是a1=100就可以了,然后回溯到被调用的位置

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
scanf("%d", &v16);
v15 = v16 / 10000;
v14 = v16 % 10000 / 100;
v13 = v16 % 10000 % 100;
v5 = v14;
v3 = sub_401390(7 * v14, 1);
v4 = sub_401390(11 * (13 * v3 + v13 + 3), v5);
v17 = ((v4 - v13) / 10 + 11) ^ 0x104B4;
if ( (v16 & 1) != 0 || v16 < 233 || v15 > 9999 || v14 > 12 || v13 > 32 || v17 * (v14 + v13 + v15) != 136398636 )
sub_4013C0(0);
sub_4013C0(3);
if ( --File._cnt < 0 )
_filbuf(&File);
else
++File._ptr;
scanf("%s", &Str);
v18 = strlen(&Str);
if ( v18 != 32 )
sub_4013C0(1);
v19 = &Str;
MEMORY[0] = v16;
NtClose = GetProcAddress(::hModule, "NtClose");
(NtClose)(v17, v6, sub_401660);
if ( sub_4019B0(&Str) == -1 )
sub_4013C0(666);
else
sub_4013C0(100);

这一段是比较重要的代码,整个代码中输入了一个数字,一个字符串
数字通过判断使得a1=3,而字符串就是上面代码所说的密码,
输入数字的代码好解决,翻译过来就是数字是十万位以上,千百位小于12,个十位小于32,整个数字大于233
然后有个sub_401390函数,返回这个算式~(a2 + ~a1)
v3,v4也可以算出来,然后使用列举爆破就行了

1
2
3
4
5
6
7
for i in range(13):
for j in range(33):
v3 = ~(~(7 * i) + 1)
v4 = ~(~(11 * (13 * v3 + j + 3)) + i)
v19 = int((v4 - j) / 10 + 11) ^ 0x104B4
print(136398636/v19)

这样会得到很多值,但是只有一个是被除清了的2046,所以v19==2046,然后我们就可以知道v19==2046时i和j
也就得到了整个数字,20220222,可以运行程序验证
然后继续向下分析,最终要使得传入100,最后刚好就是,不过要先将str进行判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for ( i = 0; i < 32; ++i )
*(i + a1) ^= byte_432034[i];
for ( j = 0; j < 30; j += 2 )
{
if ( *(j + a1) >= *(j + a1 + 2) )
return -1;
}
for ( k = 0; k < 32; k += 2 )
byte_434D3C[300 * *(k + a1) + *(k + a1 + 1)] = 1;
for ( l = 0; l < 300; ++l )
{
for ( m = 0; m < 300; ++m )
{
if ( byte_434D3C[300 * l + m] != byte_41C0A0[300 * l + m] )
return -1;
}
}
return 0;

这里byte_[432034]和byte_41c0a0是知道了
通过将byte_434d3c赋值,等于byte41c0a0,由于赋值为一,所以重点是索引,
byte41c0a0的范围很大,有90000个,所以需要使用idc(ida中的脚本)来输出索引

1
2
3
4
5
6
7
8
auto s=0x41c0a0;
auto i=0;
Message("\n");
for(;i<=90000;i++){
if(Byte(s+i)==1)
Message("%d,",i);
}
9015,12928,25805,29200,36021,40589,41428,46450,54099,56800,60001,66213,67554,70735,73866,76755,90000,

得到索引后再来分析程序
第一个for循环是再异或,第二个for循环作为一个限制条件偶数列是递增,第三个for循环才是重点,通过一个表达式来决定byte434d3c的哪个值是1,于是我们可以写出代码

1
2
3
4
5
6
7
8
9
10
11
key = [241, 239, 97, 187, 201, 69, 87, 67, 54, 235, 195, 245, 97, 31, 224, 237, 95, 25, 195, 131, 11, 103, 91, 68, 122,
157, 178, 126, 245, 181, 34, 101]
m = [9015, 12928, 25805, 29200, 36021, 40589, 41428, 46450, 54099, 56800, 60001, 66213, 67554, 70735, 73866, 76755]


for i in range(200):
for j in range(200):
for n in range(len(m)):
if (i ^ key[0]) * 300 + (j ^ key[1]) == m[n] :
#print(i, j)
print(chr(i), chr(j))

这种逻辑可以爆破出来满足条件的字符,虽然每个位置的字符能爆破出来,但是有些位置却没有满足条件(如果每个字符是再32-126范围内)的结果,所以只能放一放了