ISCC 2026 移动安全题解
字数 6014
更新时间 2026-06-02 00:01:13

ISCC 2026 移动安全题解教学文档

本文档基于 ISCC 2026 移动安全赛题官方题解整理,涵盖四道题目的完整解题思路与技术细节。


一、灰签名回廊

1.1 题目概述

典型 Android 多层验证逆向题。flag 未明文存放,而是通过 ContentProvider、BroadcastReceiver 和 Deep Link 三条独立隐藏通道分别下发不同片段,最终在 native 层完成链式收口校验。

核心特征:签名绑定机制,native 层 Part1 校验与 APK 证书 SHA-256 摘要绑定,防止重打包。

1.2 基本信息

项目 内容
APK attachment-68.apk
包名 com.example.gnd
应用名 SecureVault
Native 库 lib/x86_64/libnative-verify.so

关键组件

  • com.example.gnd.hidden.SecretContentProvider
  • com.example.gnd.hidden.SecretReceiver
  • com.example.gnd.hidden.SecretActivity
  • com.example.gnd.security.SecureValidator

1.3 隐藏片段触发与分发

ContentProvider 路径

  • URI:content://com.example.gnd.secret.provider/keys/fragment
  • 功能:生成并缓存 fragment2
  • 注意事项:依赖内部计数器状态,需确保只触发一次查询

BroadcastReceiver 路径

  • Action:com.example.gnd.SECRET_ACTION
  • 两步验证机制:
    1. 先发认证广播:code=auth + token=ISCC2026
    2. 再发取 key 广播:code=getkey + part=3
  • 功能:生成并缓存 fragment3

Deep Link 路径

  • URI:securevault://secret/unlock
  • 必需参数:token=RC4_KEY_PART4
  • 功能:写入 fragment4,若 token 错误则写入无效值

1.4 自定义 Base64 编码

字母表

ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/

特点:大写字母反序、小写字母反序、数字反序、+/ 保持不变。

1.5 Java 层校验逻辑

入口:SecureValidator.c()

校验流程:

  1. flag 格式:ISCC{...}
  2. 内部字符串 inner = flag[5:-1]
  3. 长度要求:len(inner) == 24(由 nativeGetMagicNumber() 返回 12 决定)
  4. 切分为 4 段:7 / 5 / 5 / 7 字节
  5. 前 3 段分别进入 nativeVerifyPart1/2/3
  6. 第 4 段进入 nativeVerifyPart4
  7. 中间经过 nativeComputeChainToken() 串联结果

1.6 Native 层校验链

Part1(7 字节):e5JXv2T

  • 结合 APK 证书 SHA-256 参与校验
  • 通过 JNI 回调获取签名信息,与 so 中硬编码 hash 比较

Part2(5 字节):_qrGM

  • 输入通过自定义 Base64 解码
  • 与 Provider 产生的 fragment2 逐字节异或,检查结果是否符合预设常量

Part3(5 字节):_iJ5n

  • 涉及线性反馈移位寄存器(LFSR)变换
  • 输入字节经 LFSR 后与 fragment3 关联数据比较

Part4(7 字节):_bD24iR

  • 使用前三段计算的 chain token
  • 拼上 deep link 触发的 fragment4
  • 进入 RC4 风格状态变换
  • 最终与硬编码 SHA-256 值对比完成验证

1.7 最终 Flag

ISCC{e5JXv2T_qrGM_iJ5n_bD24iR}

二、Flag Shop

2.1 题目概述

flag 不在 Java 层明文,核心判定逻辑在 native 层 libflagshop.so。JNI 接口 NativeBridge.encryptFlag(String a, String b, String c, String d) 接收四元输入:商品编号、候选 flag、管理员账号、管理员密码。

2.2 商品结构

商品 类型
fakeflag1 假 flag
fakeflag2 假 flag
fakeflag3 假 flag
realflag 真 flag

假 flag 明文统一为 ISCC{fakeflagfake}

realflag 目标密文

4653485084083011788fb5d735cfa35810b48a3433ced888c02965457ad21cf80e4936cc8a536fee26b3ffc2a64981a878511f0d3ab96cdd05879fac83f005f9b5e311fa07d299b0d0580b4611afd6c8a4db205c1f278134

2.3 管理员凭据恢复

  • 用户名admin
  • 密码FlagShopAdmin2026(注意末尾 6 不可遗漏)

2.4 解题策略

  1. 使用 fake 商品校准仿真环境
  2. 构建测试输入:fakeflag1 + ISCC{fakeflagfake} + admin + FlagShopAdmin2026
  3. 验证 native oracle 正常工作
  4. 切换到 realflag 获取真实结果

2.5 本地 Oracle 搭建要点

  • 抽取 libflagshop.so
  • 用户态 ARM64 仿真
  • 需完成:PT_LOAD 段映射、重定位处理、JNIEnv 伪造、Java 字符串转换、导入函数 stub

2.6 最终 Flag

ISCC{7H15_1$_r431_f146_4nd_17_h45n'7_501d_0u7?!}

三、消失的喵星密使

3.1 题目概述

三层独立密码学设计叠加:ADFGVX 坐标矩阵、图片 LSB 隐写提取自定义 S 盒、基于该 S 盒的 AES-256 变体解密。

3.2 APK 结构

关键文件

  • classes.dexclasses2.dex
  • lib/arm64-v8a/libmobile03.so
  • assets/hint.png
  • assets/sys/OBSERVER_PROTOCOL

核心类

  • com.example.mobile03.MainActivity
  • com.example.mobile03._A_B_C_P

3.3 Java 层前置处理

  • 加载 native 库:System.loadLibrary("mobile03")
  • native 函数:_v(String flagBody, String path, Bitmap bitmap)
  • 读取 assets/hint.png 传入 native
  • flag 格式:ISCC{...}_B._g() 通过异或还原前缀(0x0b, 0x11, 0x01, 0x01, 0x39 异或 0x42

3.4 ADFGVX 坐标矩阵

坐标字母表

index letter
1 V
2 F
3 D
4 G
5 A
6 X

点击一个格子追加 rowLetter + colLetter,点击 6 个格子得到 12 字符路径。

3.5 MD5 前置校验

目标 MD5:0899ffac222b48317a57149d6df056c0

3.6 OBSERVER_PROTOCOL 约束推导

P1 求解

  • Ψ(r,c) = (r-c)² + (r+c-2)²
  • 梯度为零得 r=1, c=1
  • P1 = (1,1) = VV

P2 求解

  • 日志 tag = 0x1918
  • T = 0x1918, τ = 1
  • r_B = 2, c_B = 4
  • P2 = (2,4) = FG

P3 求解

  • 数学推导得 (6,1),但实际受全局约束影响
  • 结合 MD5 和路径约束暴力搜索得 P3 = (3,6) = DX

3.7 爆破点击序列

约束条件

  • Row_Total = 21
  • Col_Total = 20
  • P2.c > P1.c
  • P6.c < P2.c
  • MD5(sequence) = 目标值

唯一序列VVFGDXGFAXXV

对应坐标

坐标 字符
P1 (1,1) VV
P2 (2,4) FG
P3 (3,6) DX
P4 (4,2) GF
P5 (5,6) AX
P6 (6,1) XV

3.8 图片隐写

提取 hint.png 蓝色通道 LSB,得到以 MEOW 开头的数据块,后跟 256 字节 S-box(一一映射置换表)。

3.9 Native 层自定义 AES-256 解密

流程

  1. 检查 flag body
  2. 从 hint.png LSB 提取 S-box
  3. 根据点击序列派生 32 字节 key
  4. 使用自定义 S-box 的 AES-256-like 算法解密密文
  5. 解出明文与输入比较

密钥派生:序列经交错重排 → 坐标对映射到 36 字符字母表 → 自定义 S 盒替换 → 与递增计数器异或

密文

  • Base64:z5hNvYOCx3SmRtyC9LmQdCI8NG6wcQ4l63I8Jkmg6cY=
  • 异或 0x66 后:a9fe2bdbe5e4a112c020bae492dff612445a5208d61768438d145a402fc68fa0

3.10 最终 Flag

ISCC{whi5ker_7r4ce_ayAP}

四、折叠回声

4.1 题目概述

flag 不是静态常量,而是由 APK 自身环境状态动态计算得出。第一层假 flag 用于筛选,真正校验在 EchoGate.verify() 中。

4.2 APK 结构

关键文件

  • AndroidManifest.xml
  • META-INF/ECHOFOLD.RSA
  • assets/sleep_loop.webp
  • classes.dex
  • resources.arsc

特点:无 native 库,所有逻辑在 Java/DEX 层实现。

4.3 代码结构

核心类

  • com.iscc.echofold.MainActivity
  • com.iscc.echofold.FirstEchoCodec(假 flag 校验)
  • com.iscc.echofold.EchoGate(真 flag 校验)
  • com.iscc.echofold.TraceRecorder
  • com.iscc.echofold.R$styleable

4.4 假 flag

ISCC{this_is_only_the_first_echo}

4.5 WebP 中的 ECH0 Chunk

sleep_loop.webp 为 RIFF/WebP 容器,包含非标准 chunk ECH0(236 字节),为自定义 VM 数据块。

提取方法:按 RIFF chunk 结构遍历,查找 tag 为 ECH0 的 chunk。

4.6 EchoGate.verify 工作机制

输入构成

key_material = sha256(classes.dex)
             || sha256(apk_signature_cert)
             || TraceRecorder.TRACE
             || R.styleable.EchoFoldPulse

处理流程

  1. ECH0 chunk → EFVM → keystream
  2. cipher XOR keystream → flag body

TraceRecorder.TRACE11 23 7a 42 51 66

R$styleable.EchoFoldPulse:32 个资源 ID,从 0x7f010000 递增至 0x7f01001f

4.7 最终 Flag

ISCC{f0lded_echo_is_a_state_not_a_string}

五、蔡文姬的胡笳琴音波谜阵

5.1 题目概述

Java 层生成 cw: tag,native 层用 tag 解密 assets/appview_cache.db,从解密后数据库提取种子初始化 RC6-like 加密器,反推 flag body。

5.2 APK 结构

关键文件

  • classes.dex
  • lib/arm64-v8a/libmobile04.so
  • assets/appview_cache.db

包名com.example.mobile04

5.3 皮肤与琴音序列

8 个 Skin 对应 8 个频率,按频率从低到高排序:

顺序 名称 Code
1 CWJ
2 XMAS
3 FLOW
4 STAR
5 ROSE
6 变宫 GOAL
7 变徵 DANCE
8 清角 MAGI

正确点击序列CWJXMASFLOWSTARROSEGOALDANCEMAGI(32 字节)

5.4 Tag 生成密码学链

流程

  1. 获取点击序列
  2. 每个字节做 bit swap(相邻位交换:0↔1, 2↔3, 4↔5, 6↔7)
  3. FNV-1a 64 位哈希
  4. 拆分为低 32 位和高 32 位
  5. TEA-like 函数加密
  6. 结果按小端打包为 8 字节
  7. CRC32 处理
  8. 拼接得到 cw:%s:%08x 格式 tag

TEA-like 密钥派生:native 层对包名字符串进行异或和移位操作生成 4 个 32 位常数。

5.5 Native 层校验

参数cache.dat 路径、tag、flag body

body 长度要求:32 字节

5.6 解密 appview_cache.db

解密方式

  1. 读取文件前 16 字节
  2. 计算 crc32(tag + first_16_bytes)
  3. 用 CRC32 值对后续内容做异或解密
  4. 异或方式:4 字节循环密钥,字节偏移量 ((i * 8) & 0x18),附加 + i 扰动

解密成功后数据开头变为 SQLite format 3

5.7 种子提取

从解密后数据库提取两个 32 位小端整数作为 seed,用于初始化 RC6-like 加密器。

5.8 RC6-like 加密与反推

关键常量

  • TGT(Base58 解码得 32 字节密文)
  • K1(native 中硬编码的 32 字节异或密钥)

校验关系

flag_body == RC6_decrypt(TGT XOR repeat(K1))

反推

flag_body = RC6_decrypt(TGT XOR repeat(K1))

解出的 bodyGuiHanQu8s9Dp68*G-ZN_#9Z3zLIbet6

5.9 最终 Flag

ISCC{GuiHanQu8s9Dp68*G-ZN_#9Z3zLIbet6}
相似文章
相似文章
 全屏