ISCC 的部分 Mobile+Misc WP,练武区域赛+总决赛。misc 过于逆天,尤其是神经网络和八卦 😅 也是赤完了
部分附件下载链接(提取码 iscc):百度网盘
Stage 1: ISCC2025 Stage1 WP
Mobile
邦布出击
下载附件,有一个 apk 和一个 sqlite 数据库,数据库无法直接打开,被加密了。
首先 jadx 反编译 apk,分析代码可知,flag 内容为对(a, "WhItenet", getiv())
DES 加密
其中getiv()
是MainActivity
类下的一个 native 层函数,可以直接 hook 调用
a
类调用了b.b()
方法得到一段 Blowfish 加密字符串,key 未知

在dH
类可找到几段 base64,解密较长的两段可得到提示三次加密及一个假 flag。随后将三段短的字符串拼接,连续解 3 次 base64,可以得到一个 key
尝试用这个 key 打开加密数据库,成功

在数据库内可以得到一个新的 key:CdEfGhIjKlMnOpQr

下面的 unicode 提示 key 是真的,而 flag 是假的。结合前面分析的代码,推测这个 key 就是 Blowfish 加密使用的 key,编写 js 脚本利用 frida 对com.example.mobile01
下的b
、DESHelper
及DESHelper
进行 hook,修改b.c
的返回为得到的 key
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
| function hook() { Java.perform(function () { let b = Java.use("com.example.mobile01.b"); b["c"].implementation = function () { return "CdEfGhIjKlMnOpQr"; }; let DESHelper = Java.use("com.example.mobile01.DESHelper"); DESHelper["encrypt"].implementation = function (str, str2, str3) { console.log( `DESHelper.encrypt is called: str=${str}, str2=${str2}, str3=${str3}` ); let result = this["encrypt"](str, str2, str3); console.log(`DESHelper.encrypt result=${result}`); return result; }; Java.choose("com.example.mobile01.MainActivity", { onMatch: function (instance) { instance.Jformat("ISCC{114514}"); }, onComplete: function () {}, }); }); }
setImmediate(hook);
|
终端运行frida –U mobile01 –l payload.js

得到 flagISCC{uuz5/S6X/LDAs8E7cbSOjposlYylNeHn}
Misc
返校之路
下载附件,解压后得到两个压缩包,第一个压缩包part1.zip
为伪加密,使用随波逐流工具一键修复后解压得到readme.txt
1 2 3 4 5
| 一转眼,寒假已经过去,同学们都怀着怎样的心情踏上返校之路呢?
你是一名学生,从刚下高铁,准备乘坐19站地铁返回学校。短短的假期总是让人留恋,而返校的路似乎格外漫长。
在途中,你发现了一个神秘的压缩包,以及一张写着bfs???的纸条,这似乎隐藏着一些重要的信息。。。
|
使用bfs???
作为掩码,利用 ARCHPR 工具爆破得到密码bfsYfx
,解压压缩包,得到三张图片,两张是地铁站站台图片,一张是北京城市轨道交通线网图
对picture2.png
lsb 提取 RGB 数据,可找到字符串flag_is_MNVUM2KXIRUFQU2VN46Q====
先解 base32 再解 base64 得rAbX8WIJ
题目中提到“19 站地铁”,从第一张图片3 号线朝阳站到4 号线魏公村站途经 19 站的地铁路线为 3->10->4,将3104
拼接到rAbX8WIJ
后得到 flag
取证分析
下载题目给的两个附件,一个为 vmem 镜像,一个为 word 文档
将文档改后缀为 zip 并解包,在[Content_Types].xml
内可以找到注释<!-- nawbcuzcdzbd -->
使用 R-Studio 扫描镜像,在原始文件-归档内可找到一个 zip 文件,直接恢复,需要密码才能解压
使用 ARCHPR 工具爆破压缩包,得到密码bfs775
,解压后得到三个文本文件
Alphabet.txt
1 2
| (2,10) (4,8) (2,4) (3,4) (11,13) (2,11) (1,1) (10,26) (5,6) (5,9) 杨辉三角是一种经典的数学数表,以中国古代数学家杨辉的名字命名。它是一个三角形数组,其中每个数字都是其上方两个数字的和。杨辉三角在组合数学、概率论和二项式定理等领域有广泛应用
|
hint.txt
readme.txt
对 hint 里的字符串凯撒解密得到flag{ vigenere cipher }
,提示维吉尼亚密码
Alphabet.txt
内提及了杨辉三角,并且给了 10 个数对,推测是利用这些数对从杨辉三角中提取数字生成 key
推测数对对应的是杨辉三角中的行与列,由于杨辉三角第n行只有n个数,且第n行m列的数为(m−1n−1),所以后一个数字为行数而前一个数字为列数,编写脚本取出这些数字,并把它们映射为模 26 意义下的大写字母 A-Z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from math import comb
pairs = [ (2, 10), (4, 8), (2, 4), (3, 4), (11, 13), (2, 11), (1, 1), (10, 26), (5, 6), (5, 9), ]
def decrypt(col, row): v = comb(row - 1, col - 1) m = v % 26 print(f"第{row}行第{col}列:", v, "对应", chr((m - 1) % 26 + ord("A"))) return chr((m - 1) % 26 + ord("A"))
plaintext = "".join(decrypt(c, r) for c, r in pairs) print("Key: ", plaintext)
|
解密得到 key 为IICCNJAYER
,作为维吉尼亚密码的 key 解密nawbcuzcdzbd
得到 flag
签个到吧
下载附件并解压,得到一张 jpg 图片flag_is_not_here.jpg
和一个 zip 压缩包,压缩包无法直接解压,用随波逐流查看可以发现是文件头被修改,010 改504b0304
或直接用随波逐流修复即可解压,得到一张 png 图片0001_5.png

第二张图片根据资料可推断为猫脸变换(Arnold 变换)

推测需要利用两张图片异或,编写脚本
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
| import cv2 import numpy as np from PIL import Image def arnold_decode(image, shuffle_times, a, b): decode_image = np.zeros(shape=image.shape, dtype=np.uint8) h, w = image.shape[0], image.shape[1] N = h for _ in range(shuffle_times): tmp_image = np.zeros_like(decode_image) for x in range(h): for y in range(w): new_x = ((a * b + 1) * x - b * y) % N new_y = (-a * x + y) % N tmp_image[new_x, new_y, :] = image[x, y, :] decode_image = tmp_image cv2.imwrite('0001_5.png', decode_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0]) return decode_image def xor_images(image_path_1, image_path_2, output_path): img1 = Image.open(image_path_1).convert('RGB').rotate(90, expand=True) img2 = Image.open(image_path_2).convert('RGB') if img1.size != img2.size: raise ValueError(f"图片尺寸不一致: img1={img1.size}, img2={img2.size}") img1_data = np.array(img1) img2_data = np.array(img2) xor_data = np.bitwise_xor(img1_data, img2_data) xor_image = Image.fromarray(xor_data) xor_image.save(output_path) print(f"异或结果已保存至: {output_path}") if __name__ == "__main__": input_image = cv2.imread('0001_5.png') decoded_image = arnold_decode(input_image, shuffle_times=1, a=1, b=-2) cv2.imwrite('decoded_0001_5.png', decoded_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0]) xor_images("decoded_0001_5.png", "flag_is_not_here.jpg", "result.png")
|
运行后得到图片

支付宝扫码得到 flag
睡美人
下载附件,得到一个压缩包,解压后为一张图片。使用 binwalk 提取得到一个加密的压缩包,需要密码
由题目中提示“红红红红红红绿绿绿蓝”的“色彩秘方”可以推测是从 RGB 数据中按一定方式提取值,编写脚本还原压缩包密码
具体方式为sumR*0.6+sumG*0.3+sumB*0.1
,即遍历所有像素得到的 RGB 值按6:3:1
比例运算得到一个浮点数1375729349.6
利用该浮点数解压压缩包得到一段 wav 音频

在提示后藏了一段曼彻斯特编码,解码后就能获取 flag
后期重找的脚本
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
| import scipy.io.wavfile as wavfile import numpy as np
def load_audio_file(filename): try: sample_rate, data = wavfile.read(filename) return sample_rate, data except Exception as e: print(f"ERROR : {e}") return None, None
def preprocess_signal(data): return data[:, 0] if data.ndim == 2 else data
def analyze_segment(segment, threshold=0): binary = (segment > threshold).astype(int) if np.all(binary == 1): return '0' elif np.any(np.diff(binary) == -1): return '1' return None
def decode_audio_stream(audio_signal, sample_rate, start_sample, samples_per_segment): decoded_bits = [] current_pos = start_sample total_samples = len(audio_signal) segment_count = 0 print(f"Sample: {sample_rate} Hz") print(f"Points: {samples_per_segment}") print(f"Start : {start_sample}") while current_pos + samples_per_segment <= total_samples: segment = audio_signal[current_pos:current_pos + samples_per_segment] decoded_bit = analyze_segment(segment) if decoded_bit is not None: decoded_bits.append(decoded_bit) current_pos += samples_per_segment segment_count += 1 print(f"Total : {segment_count}") return decoded_bits
def decode(filename="normal_speech_18.wav", start_time_sec=6.0, segment_duration_sec=0.1): sample_rate, data = load_audio_file(filename) if sample_rate is None: return "" audio_signal = preprocess_signal(data) start_sample = int(start_time_sec * sample_rate) samples_per_segment = int(segment_duration_sec * sample_rate) if start_sample + samples_per_segment > len(audio_signal): return "" result_bits = decode_audio_stream(audio_signal, sample_rate, start_sample, samples_per_segment) return "".join(result_bits)
def from_binary(binary_str): chars = [] for i in range(0, len(binary_str), 8): byte = binary_str[i:i+8] if len(byte) == 8: chars.append(chr(int(byte, 2))) return ''.join(chars)
if __name__ == "__main__": decoded = decode() print(from_binary(decoded))
|
运行脚本,即可在控制台获得 flagISCC{Slogan}

神经网络迷踪
神金题,改为压缩包后缀解开后拿 ISCC 套一下就是 flag
下载附件,是一个 pth 文件,load 模型可以找到一些提示,例如提示:2025ISCC2025KEY!
,缩小255倍放大255倍

直接提交ISCC{appl}
八卦
无敌了
下载附件,得到一张 GIF。首先 010 打开,可以发现最后有一段 7z 开头的额外信息

提取出来得到一个 data.7z,用 7-zip 打开,里面有一个 txt 需要密码

分析 hint

首先根据提示 LSB,用 StegSolve 提取

对应 base64 编码解码得到坤为地
。再用 PS 打开 GIF,提取对应的信息,时间及 base 编码,提取出5Lm+5Li65aSp
,4WY3DZVQWTUJFGI=
,5rC06Zu35bGv
,42YLJZNEVHUZZAA=
分别用 base32 和 base64 解出四个卦象为乾为天
,山水蒙
,水雷屯
,水天需
然后观察持续时间,0.2 和 0.3 循环,意为 23,寻找易经六十四卦得到第 23 卦为山地剥
接着由第 1 2 3 5 卦有内容,第 4 6 无内容,1 为有 0 为无得到 111010,对应的十进制为 58,寻找易经六十四卦得到第 58 卦为兑为泽
至此,已经得到全部 7 卦:坤为地 乾为天 山水蒙 水雷屯 水天需 山地剥 兑为泽
按照易经六十四卦中的顺序从小到大排序,再将它们转为上下卦,得到:乾乾坤坤坎震艮坎坎乾艮坤兑兑
解密 7z 压缩包得到U1ZORFEzdFhUa3h2YTNGb2MyOXVNVGswZlE9PQ==
,解两次 base64 得到ISCC{WNLokqhson194}