Skip to main content

NTUSTISC - Pwn 2学习笔记

ElegyAbout 3 minpwnheap

NTUSTISC - Pwn 2学习笔记

查看glibc的源码网址:https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c

TSL 了解

gdb 如何查 fs?

print (void)arch_prctl(option_num,addr)
  • option_num 是决定是我们的操作 比如0x1003就是取fs的值

  • addr就是取fs放在什么地方

  • pwngdb的话直接输入tls也可以查到

bin

free 源码分析

  • 运行_int_free函数
  • 如果size小于get_max_fast()就进入fastbin
  • 通过fastbin_index(size)获取对应大小的fastbin
  • fastbin(av,idx)获取fastbin的位置
  • 然后chunk入链

malloc源码分析

  • 运行_int_malloc函数
  • 先获取我们真正需要的 chunk 大小
  • 判断是否是fastbin范围
  • 获取对应大小的fastbin然后获取位置
  • 然后 chunk出链

Fastbin

  • fd指向下一个free chunkchunk head(也就是下一个free的chunk)
  • 不去修改p

Tcache

  • 从libc2.26开始引入

  • 从0x20到0x410

  • 每个tcache最多收取7个chunk

  • 用结构tcache_perthread_struct管理tcache

    • 存在于TLS
    • 一共两个部分 一个是对应大小的tcache bin的链表(每个最多存7个chunk) 一个是对应链表的元素数量
  • chunk的bk是一串随机的安全数 防止double free的

  • fd指向的是 下一个free chunkmem(也就是payload的部分)

Fastbin dup

算是一个正式的攻击手段

  • 利用double free让整个链表陷入循环

    比如现在fastBin->chunk1->chunk2 然后此时chunk1的fd指向chunk2然后我们再次free chunk2那么chunk2的fd指向chunk1就会变成:
    fastBin->chunk2->chunk1->chunk2->chunk1的死循环

  • 然后我们malloc一下 获取了chunk2然后此时链表:fastbin->chunk1->chunk2->chunk1...

  • 然后我们修改chunk2fd指向我们想要修改的地方 那么链表:fastbin->chunk1->chunk2->addr_we_want

  • 那么我们malloc三次获取我们想要的地址的读写权

    记住是我们想要写入地址-0x10 因为还有prev_size+size

  • 然后根据源码 我们可以知道

    • size检查

    我们的size得>=2*SIZE_SZ然后必须<=av->system_mem

     if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
    	|| __builtin_expect (chunksize (chunk_at_offset (p, size))
    			     >= av->system_mem, 0))
          {
    	/* We might not have a lock at this point and concurrent modifications
    	   of system_mem might have let to a false positive.  Redo the test
    	   after getting the lock.  */
    	if (have_lock
    	    || ({ assert (locked == 0);
    		  mutex_lock(&av->mutex);
    		  locked = 1;
    		  chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
    		    || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
    	      }))
    	  {
    	    errstr = "free(): invalid next size (fast)";
    	    goto errout;
    	  }
    	if (! have_lock)
    	  {
    	    (void)mutex_unlock(&av->mutex);
    	    locked = 0;
    	  }
          }
    
    
    • 检查double chunk

      仅仅是检查bin中第一个chunk是否是相同的chunk

      free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
      
          set_fastchunks(av);
          unsigned int idx = fastbin_index(size);
          fb = &fastbin (av, idx);
      
          /* Atomically link P to its fastbin: P->FD = *FB; *FB = P;  */
          mchunkptr old = *fb, old2;
          unsigned int old_idx = ~0u;
          do
            {
      	/* Check that the top of the bin is not the record we are going to add
      	   (i.e., double free).  */
      	if (__builtin_expect (old == p, 0))
      	  {
      	    errstr = "double free or corruption (fasttop)";
      	    goto errout;
      	  }
      	/* Check that size of fastbin chunk at the top is the same as
      	   size of the chunk that we are adding.  We can dereference OLD
      	   only if we have the lock, otherwise it might have already been
      	   deallocated.  See use of OLD_IDX below for the actual check.  */
      	if (have_lock && old != NULL)
      	  old_idx = fastbin_index(chunksize(old));
      	p->fd = old2 = old;
            }
          while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
      
          if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
            {
      	errstr = "invalid fastbin entry (free)";
      	goto errout;
            }
      

    Tcache利用

    大多数性质和fastbin是一样的 但是calloc是不会拿tcache的 所以我们一般把tcache填满来绕过