Misc
流量分析-1
Challenge
我需要你流量分析😡
首次发起端口扫描的IP是?
将答案经过md5 32位加密后小写形式放入CM{}中
以下是附件链接:
通过网盘分享的文件:抓取流量.pcapng 链接: https://pan.baidu.com/s/1ye0KLzXGqyCYec2kGSlPMw?pwd=CM66 提取码: CM66 —来自百度网盘超级会员v5的分享
Solution

192.168.37.3
CM{d28ee9d60772acbcd4eca38e1a3c94b8}流量分析-2
Challenge
扫描次数最多的IP?
将答案经过md5 32位加密后小写形式放入CM{}中
Solution

192.168.37.3
CM{d28ee9d60772acbcd4eca38e1a3c94b8}流量分析-3
Challenge
扫描次数第二的IP?
将答案经过md5 32位加密后小写形式放入CM{}中
Solution
图同上题
192.168.37.1
CM{1edaa78b26c43a0cf438b4437f6ceeb3}流量分析-4
Challenge
哪个IP用了AWVS?
将答案经过md5 32位加密后小写形式放入CM{}中
Solution
IP数量很少,试一下就出来了
192.168.37.1
CM{1edaa78b26c43a0cf438b4437f6ceeb3}流量分析-6
Challenge
有IP进行了WEB登录爆破😲,提交其IP?
将答案经过md5 32位加密后小写形式放入CM{}中
hint:登录页面为login.php
Solution
直接搜索字符串POST /login.php,一条条看发现192.168.37.87多次连续出现
CM{83779b479698b76581244f6ac8acd8a6}流量分析-7
Challenge
有IP进行了WEB登录爆破,提交其爆破次数
(将爆破次数比如55, 进行加密)
将答案经过md5 32位加密后小写形式放入CM{}中
Solution

ip.src == 192.168.37.87 && http contains "login.php"筛选导出得到107条数据,除去第1条GET请求,剩下的106条全都是
CM{f0935e4cd5920aa6c7c996a5ee53a70f}流量分析-8
Challenge
提交攻击者登录成功admin用户的IP和密码,以&连接
(示例: 答案为 192.168.92.111&CM666, 将上述内容进行加密)
将答案经过md5 32位加密后小写形式放入CM{}中
Solution

192.168.37.200&zhoudi123
CM{3ca6dd54928fcfe47289ae62439116dd}段涵涵学姐最爱的音乐
Challenge
王振宇从学姐闺蜜那边了解到学姐最爱的歌手是Taylor Swift,猜猜这个音频有什么秘密吧
flag格式为CM{XXXXXXXXX}
Solution

CM{U_Kn0w_TaYLOR}OSINT
杜浩学姐の朋友圈
Challenge
王阵雨辗转反侧,突然刷到了杜浩学姐朋友圈的一张图,猜猜这是在哪里呢
flag格式为CM{所在城市英文拼音-距离图中最近的地铁站名}
例:CM{Shanghai-陆家嘴}
Solution
发现玻璃反射泄露信息,将图片水平镜像后放大发现这两处关键信息

首先是右边的City花园城,搜索发现它改名为招商花园城了,因此后续的搜索中使用关键词招商花园城
然后是左边盒马的广告,说明这附近有盒马的店,并且极大概率就开设在招商花园城内
因此搜索盒马 招商花园城

发现这篇文章盒马鲜生南京新店开业 市民多了新消费地标_腾讯新闻
文中提到的是南京招商花园城
使用手机高德地图搜索南京招商花园城后搜索其附近的地铁站

CM{Nanjing-万寿}Web
小猿口算签到重生版
Challenge
考验手速和脑速的挑战
Solution
import requests def main(): # 定义目标URL generate_url = "http://27.25.151.40:32926/generate" verify_url = "http://27.25.151.40:32926/verify" # 创建一个Session对象,确保所有请求在同一个session内 with requests.Session() as session: try: # 发送GET请求获取表达式 response = session.get(generate_url) response.raise_for_status() # 检查请求是否成功 # 解析返回的JSON数据 data = response.json() expression = data.get("expression") if not expression: print("未获取到有效的表达式") return print(f"获取到的表达式: {expression}") # 去掉等号并计算表达式的值 expression_to_eval = expression.replace("=?", "") try: result = eval(expression_to_eval) # 计算表达式结果 except Exception as e: print(f"表达式计算失败: {e}") return print(f"计算结果: {result}") # 准备POST请求的数据 payload = { "user_input": str(result) } # 发送POST请求提交结果 verify_response = session.post(verify_url, json=payload) verify_response.raise_for_status() # 检查请求是否成功 # 输出服务器返回的验证结果 verify_data = verify_response.json() print(f"验证结果: {verify_data}") except requests.exceptions.RequestException as e: print(f"网络请求失败: {e}") if __name__ == "__main__": main()flag{CAD709DE7E0B803D8BA72A55C4EB8C50}lottery签到重生版
Challenge
抽抽抽
flag格式为CM{xxxxxx}!
Solution
找了一圈都没线索,所以就试着爆破一下
import requestsimport time URL = "http://27.25.151.40:33411/spin" # 爆破次数上限MAX_ATTEMPTS = 200 def spin_the_wheel(): """ 发送POST请求到 /spin 接口 """ try: # 参照前端代码,发送一个空的POST请求 headers = {'Content-Type': 'application/json'} response = requests.post(URL, headers=headers, data='{}', timeout=10) if response.status_code == 200: return response.json() else: print(f"请求失败,状态码: {response.status_code}") return None except requests.exceptions.RequestException as e: print(f"请求发生错误: {e}") return None def main(): print(f"[*] 开始爆破,目标URL: {URL}") print(f"[*] 最大尝试次数: {MAX_ATTEMPTS}") print("-" * 30) for i in range(1, MAX_ATTEMPTS + 1): print(f"[*] 正在进行第 {i} 次尝试...") result = spin_the_wheel() if result: # 打印每次的结果,方便调试 print(f" [+] 收到结果: {result}") # 检查是否存在 'flagContent' 字段 if 'flagContent' in result: print("\n" + "=" * 40) print(result['flagContent']) print("=" * 40 + "\n") return time.sleep(0.1) if __name__ == "__main__": main()结果到了第178次就爆出来了
flag{B4F8EC958F70E3EE2245F97068D00109}Reverse
IDA
Challenge
flag格式为CM{xxxxxx}!
Solution

CM{W3lc0me_2_R3ver5e_h@v3_fun!}Xor
Challenge
flag格式为CM{xxxxxx}!
Solution
# 提取 flag 数组内容flag = [ 0x5F, 0x55, 0x58, 0x5E, 0x42, 0x61, 0x09, 0x6B, 0x66, 0x08, 0x4A, 0x66, 0x0F, 0x79, 0x4A, 0x08, 0x5A, 0x66, 0x5F, 0x09, 0x4B, 0x66, 0x6B, 0x0A, 0x4F, 0x5C, 0x4B, 0x0C, 0x5C, 0x18, 0x44] # 异或密钥key = 57 # 逆向计算原始输入字符串original_input = ''.join([chr(byte ^ key) for byte in flag]) # 输出结果print("原始输入字符串为:", original_input)CM{X0R_1s_6@s1c_f0r_R3ver5e!}maze
Challenge
点击。。。?就。。。?送。。。?诶这些01仿佛组成了一条路
Solution
import sys # 增加递归深度限制,以防迷宫过大导致栈溢出sys.setrecursionlimit(2000) # 从C++代码中复制的地图数据MAP_STRING = "$11111111100111111111010000111001011011101101101110000110111111110011111111011111111101111111110000#"WIDTH = 10HEIGHT = len(MAP_STRING) // WIDTH def solve_maze(): """ 解析并解决迷宫问题 """ # 1. 将一维字符串地图转换为二维列表 maze = [] for i in range(HEIGHT): row = list(MAP_STRING[i * WIDTH : (i + 1) * WIDTH]) maze.append(row) print("--- Maze Layout ---") for row in maze: print("".join(row)) print("--------------------") # 起点是 (x=0, y=0) start_pos = (0, 0) # 2. 定义深度优先搜索 (DFS) 函数 # path: 记录移动指令 (e.g., "SSDDW...") # visited: 记录访问过的坐标 (x, y),防止走回头路或无限循环 def find_path(x, y, path, visited): # --- 检查边界条件和失败条件 --- # 检查是否越界 if not (0 <= x < WIDTH and 0 <= y < HEIGHT): return None # 检查是否撞墙 ('1') if maze[y][x] == '1': return None # 检查是否访问过 (防止循环) if (x, y) in visited: return None # --- 检查胜利条件 --- # 如果当前位置是终点 if maze[y][x] == '#': # 并且路径长度正好是28 if len(path) == 28: print(f"[*] Path found with length {len(path)}!") return path else: # 找到了终点,但路径长度不对,这条路是错的 return None # 如果路径已经超过28步,没必要继续了 if len(path) > 28: return None # --- 递归探索 --- # 标记当前点为已访问 new_visited = visited.copy() new_visited.add((x, y)) # 尝试四个方向: S(下), D(右), A(左), W(上) # 这个顺序可以随便定,但会影响找到的第一条解 # Move Down (S) solution = find_path(x, y + 1, path + 'S', new_visited) if solution: return solution # Move Right (D) solution = find_path(x + 1, y, path + 'D', new_visited) if solution: return solution # Move Left (A) solution = find_path(x - 1, y, path + 'A', new_visited) if solution: return solution # Move Up (W) solution = find_path(x, y - 1, path + 'W', new_visited) if solution: return solution # 所有方向都走不通 return None # 3. 从起点开始搜索 print("[*] Searching for a path of length 28...") # 初始路径为空,访问过的集合只包含起点 solution_path = find_path(start_pos[0], start_pos[1], "", set()) # 4. 输出结果 if solution_path: print("\n[+] Success! Found the correct input:") print(solution_path) else: print("\n[-] Failed to find a valid path of length 28.") if __name__ == "__main__": solve_maze()CM{SDSSASSDDDWWWDDDSSSSASSSDDDD}sw1f7’s TEA
Challenge
相传sw1f7学姐喜欢做甜点,我猜她应该也喜欢泡茶,只有她认可的人才能喝到茶
flag格式为CM{xxxxxx}!
Solution
把checkdebug给nop掉,然后在第19行下断点

动调拿到密文
.data:0000000000404020 flag dd 5B5C5F08h, 2766AE05h, 8C4D477Dh, 554F7F8Dh, 0E20BD674h.data:0000000000404020 ; DATA XREF: sub_114514(void)+1D↑w.data:0000000000404020 ; sub_114514(void)+57↑o ....data:0000000000404034 dd 0BE678AAh, 0F44B5224h, 0CA619F04h然后AI一把梭
import struct def decrypt(v, k): """ 这是提供的 encrypt 函数的逆函数。 它将一个 8 字节的数据块 v (拆分为 v0, v1) 进行 32 轮解密。 参数: v (tuple): 一个包含2个32位无符号整数的元组 (v0, v1)。 k (tuple): 一个包含4个32位无符号整数的元组,代表密钥 (k0, k1, k2, k3)。 返回: tuple: 解密后的 (v0, v1) 元组。 """ # 将元组解包到单独的变量中以便操作 v0, v1 = v k0, k1, k2, k3 = k # 这是C代码中的魔数: 1640531527 == 0x61C88647 delta = 1640531527 # ---- 逆向 sum 的计算 ---- # 在加密函数中,sum 的初始值为 0,然后循环 32 次 sum -= delta。 # 所以,在解密开始时,sum 的值应该是加密结束时的值,即 0 - (32 * delta)。 # 使用 & 0xFFFFFFFF 是为了模拟32位无符号整数的环绕溢出行为。 current_sum = (0 - (32 * delta)) & 0xFFFFFFFF # ---- 解密循环 ---- # 加密是从 0 到 31 轮,解密则需要逆向这个过程。 for i in range(32): # 1. 逆向 v1 的更新操作 (必须先做这一步) # 原始公式: v1 += (v0 + sum) ^ (k[2] + 16 * v0) ^ ((v0 >> 5) + k[3]); # 逆向公式: v1 -= (v0 + sum) ^ (k[2] + 16 * v0) ^ ((v0 >> 5) + k[3]); # 注意: v0 << 4 等价于 16 * v0 term_v1 = (((v0 + current_sum) & 0xFFFFFFFF) ^ (k2 + (v0 << 4)) ^ (((v0 >> 5) & 0xFFFFFFFF) + k3)) & 0xFFFFFFFF v1 = (v1 - term_v1) & 0xFFFFFFFF # 2. 逆向 v0 的更新操作 (后做这一步) # 原始公式: v0 += (v1 + sum) ^ (*k + 16 * v1) ^ ((v1 >> 5) + k[1]); # 逆向公式: v0 -= (v1 + sum) ^ (*k + 16 * v1) ^ ((v1 >> 5) + k[1]); term_v0 = (((v1 + current_sum) & 0xFFFFFFFF) ^ (k0 + (v1 << 4)) ^ (((v1 >> 5) & 0xFFFFFFFF) + k1)) & 0xFFFFFFFF v0 = (v0 - term_v0) & 0xFFFFFFFF # 3. 逆向 sum 的更新操作 # 加密时 sum -= delta,所以解密时 sum += delta current_sum = (current_sum + delta) & 0xFFFFFFFF return (v0, v1) def solve(): """ 主求解函数,整合所有信息并执行解密。 """ # 1. 从 main 函数中提取的密钥 # key[0] = 36; key[1] = 66; key[2] = 82; key[3] = 118; key = (36, 66, 82, 118) print(f"[*] 使用密钥: {key}") # 2. 从 IDA .data 段中获取的加密后的 flag 数据 (8个32位整数) encrypted_flag_words = [ 0x5B5C5F08, 0x2766AE05, 0x8C4D477D, 0x554F7F8D, 0xE20BD674, 0x0BE678AA, 0xF44B5224, 0xCA619F04 ] # 3. 将32位整数列表转换为小端序(Little-Endian)的字节串,以匹配内存布局 # '<L' 表示小端序的无符号长整型 (4字节) encrypted_flag_bytes = b''.join([struct.pack('<L', word) for word in encrypted_flag_words]) print(f"[*] 待解密的密文 (hex): {encrypted_flag_bytes.hex()}") decrypted_result = b'' # 4. 将32字节的密文分成4个8字节的块,并对每个块进行解密 print("\n[+] 开始解密...") num_blocks = len(encrypted_flag_bytes) // 8 for i in range(num_blocks): # 提取当前块 block_start = i * 8 block_end = block_start + 8 encrypted_block = encrypted_flag_bytes[block_start:block_end] # 将8字节块解包成两个32位无符号整数 (v0, v1) # '<II' 表示两个小端序的无符号整型 (4字节 + 4字节) v = struct.unpack('<II', encrypted_block) # 调用解密函数 decrypted_v = decrypt(v, key) # 将解密后的 (v0, v1) 打包回8字节的块 decrypted_block = struct.pack('<II', decrypted_v[0], decrypted_v[1]) decrypted_result += decrypted_block print(f" - 块 {i+1} 解密完成,得到: {decrypted_block.decode('ascii', errors='ignore')}") # 5. 打印最终的、完整的解密结果 # 使用 .decode('ascii') 将最终的字节串转换为人类可读的字符串 # .strip('\x00') 用于移除末尾可能存在的空字符填充 final_flag = decrypted_result.decode('ascii').strip('\x00') print(f"\n{final_flag}") # 当脚本被直接运行时,调用 solve() 函数if __name__ == "__main__": solve()得到输出:
[*] 使用密钥: (36, 66, 82, 118)[*] 待解密的密文 (hex): 085f5c5b05ae66277d474d8c8d7f4f5574d60be2aa78e60b24524bf4049f61ca[+] 开始解密... - 块 1 解密完成,得到: flag{sw1 - 块 2 解密完成,得到: f7's_Tea - 块 3 解密完成,得到: _is_clas - 块 4 解密完成,得到: sical!!}flag{sw1f7's_Tea_is_classical!!}CM{sw1f7's_Tea_is_classical!!}sw1f7’s XXTEA
Challenge
sw1f7学姐的茶被喝了,这次她决定泡一壶更浓厚的茶,在走之前放了个盖子
flag格式为CM{xxxxxx}!
Solution
AI一把梭了
加密算法是XXTEA
密文 (Ciphertext): 存储在 flag 地址的数据。
.data:0000000000403020 flag dd 19EA7A62h, 5BE6801h, 0D2AD8A17h, 1A1456A1h.data:0000000000403030 dd 843B635Bh, 0E2369508h, 0BF552654h, 0FC87047Ch这些是32位的DWORD(双字),在小端序(Little-Endian)的x86架构中,内存中的字节序是反的。不过,当我们将它们作为uint32_t数组处理时,数值就是这些。
ciphertext = [0x19EA7A62, 0x05BE6801, 0xD2AD8A17, 0x1A1456A1, 0x843B635B, 0xE2369508, 0xBF552654, 0xFC87047C]
密钥 (Key):key = {36, 66, 82, 118}
参数:n = 8,rounds = 12
import struct def decrypt(v, n, key): """ XXTEA解密函数 v: uint32_t 整数列表,代表密文数据 n: 数据块的数量 key: uint32_t 整数列表,代表密钥 """ # 定义算法常量和参数 ROUNDS = 52 // n + 6 DELTA = 0x9E3779B9 # 解密的初始sum值 sum_val = (ROUNDS * DELTA) & 0xFFFFFFFF # y用于传播上一个解密后的块,初始值为v[0] y = v[0] # 解密轮循环 for _ in range(ROUNDS): e = (sum_val >> 2) & 3 # 内部循环,从p=n-1递减到1 # y持有v[p+1]解密后的值,z持有v[p-1]加密时的值 for p in range(n - 1, 0, -1): z = v[p - 1] term1 = (y ^ sum_val) term2 = (z ^ key[(p & 3) ^ e]) term3 = ((y << 2) ^ (z >> 5)) & 0xFFFFFFFF term4 = ((y >> 3) ^ (z << 4)) & 0xFFFFFFFF # 逆运算:减法 v[p] = (v[p] - ((term1 + term2) ^ (term3 + term4))) & 0xFFFFFFFF y = v[p] # 处理第0个块(循环边界情况) z = v[n - 1] term1 = (y ^ sum_val) term2 = (z ^ key[(0 & 3) ^ e]) term3 = ((y << 2) ^ (z >> 5)) & 0xFFFFFFFF term4 = ((y >> 3) ^ (z << 4)) & 0xFFFFFFFF v[0] = (v[0] - ((term1 + term2) ^ (term3 + term4))) & 0xFFFFFFFF y = v[0] # 更新sum sum_val = (sum_val - DELTA) & 0xFFFFFFFF return v def main(): # 从.data段提取的密文数据 (8个DWORD) ciphertext = [ 0x19EA7A62, 0x05BE6801, 0xD2AD8A17, 0x1A1456A1, 0x843B635B, 0xE2369508, 0xBF552654, 0xFC87047C ] # 程序中硬编码的密钥 key = [36, 66, 82, 118] # 块数量 n = len(ciphertext) # 执行解密 decrypted_data = decrypt(ciphertext, n, key) print(f"解密后的整数数组: { [hex(x) for x in decrypted_data] }") # 将解密后的uint32_t数组转换回字节串 # '<' 表示小端序, 'I' 表示32位无符号整数 plaintext_bytes = b'' for dword in decrypted_data: plaintext_bytes += struct.pack('<I', dword) # 打印最终结果 try: flag = plaintext_bytes.decode('utf-8') print(f"\n[+] 成功找到Flag: {flag}") except UnicodeDecodeError: print(f"\n[-] 解码失败,原始字节: {plaintext_bytes}") if __name__ == '__main__': main()得到输出:
解密后的整数数组: ['0x67616c66', '0x3177737b', '0x73273766', '0x5458585f', '0x695f6165', '0x6f6d5f73', '0x79666964', '0x7d676e69'][+] 成功找到Flag: flag{sw1f7's_XXTea_is_modifying}CM{sw1f7's_XXTea_is_modifying}Mobile
base_android
Challenge
flag格式为CM{xxxxxxx}!
Solution
逆向发现程序从 assets 文件夹中读取 timg_2.zip 文件的内容,然后将这些内容一字不差地写入到 /data/data/com.example.test.ctf02/databases/img.jpg 文件中
因此手动将assets/timg_2.zip提取出来,然后把.zip后缀改为.jpg

CM{08067-wlecome}