当前位置: 首页 > news >正文

MOBILE-消失的喵星密使

ISCC2026 WriteUp 提交模板

MOBILE-消失的喵星密使

解题思路

1.看一下程序

image.png

来一个有emoji点选的密码界面

image.png

上面的注释里写了详细逻辑
我们继续往下看

image.png

这个函数提取flag中间部分,并检验。提取逻辑在下面这个函数里面:

image.png

这些字节异或 0x42 后得到 ISCC{,并且要求输入以 } 结尾,所以 flag 格式确定为 ISCC{flag_body}

2.根据协议文件和 MD5 恢复点击序列
回忆一下初始化:

image.png

6x6 表格的行列字符集是:

D V F G X A

同时,assets/sys/OBSERVER_PROTOCOL 看起来是十六进制文本。解码后能看到协议约束:

P1 = (1,1)
P2 = (2,4)
P6 = (6,1)
Row_Total ≡ 21
Col_Total ≡ 20

image.png

image.png

image.png

因为由 6 个格子组成,每个格子贡献一对行列字符,所以可以固定 P1、P2、P6,枚举中间 3 个点,再用行列和与 MD5 过滤。得到:

q = DDVGFAGVXAAD

3.复现native校验

Java 层 _A.check() 调用 native:

public native boolean _v(String str, String str2, Bitmap bitmap);

参数分别是 flag_bodyqassets/hint.png。分析 libmobile03.so 可以看到:

  • _f_exthint.png 的 LSB 中提取数据,并要求前 4 字节是 MEOW
  • MEOW 后面的 256 字节是自定义 S-box
  • _f1 使用 _q 和 native 中的 36 字符表生成 32 字节 key
  • _f_enc 是使用该 S-box 的 AES-256 变体
  • 内置目标串为 o8kfT+HTQprrOJ0XZSR0WKoVkhfBq/xUhTIQa1D2O+Q=

实际图片数据藏在蓝色通道 LSB 中。Base64 目标串解码后还要逐字节异或 0x66,得到真正密文。逆向 AES-256 变体后得到 flag body。

完整解题脚本如下,直接从 APK 读取 assets/hint.png,恢复 _q,提取 S-box,派生 key,并输出 flag:

运行结果:

q = DDVGFAGVXAAD
key = 331f39644b3b418987f6b9b5d7fb15403727656563d2a5d1fbd7f15c13430941
flag = ISCC{k1t7y_pa7rol_adE6r}

image.png

Exp

#!/usr/bin/env python3
from __future__ import annotationsimport hashlib
import itertools
import sys
import zipfile
from base64 import b64decode
from dataclasses import dataclass
from pathlib import Pathfrom PIL import Image@dataclass(frozen=True)
class ChallengeSpec:apk_name: str = "app-release.apk"row_col_alphabet: str = "DVFGXA"q_md5: str = "ef37302826a6f0fb5fc3372ae060ba58"lookup_table: bytes = b"g5zkvit4yjnqohamucds628lwr703ebfp19x"cipher_b64: str = "o8kfT+HTQprrOJ0XZSR0WKoVkhfBq/xUhTIQa1D2O+Q="rcon: tuple[int, ...] = (1, 2, 4, 8, 16, 32, 64, 128, 0x1B, 0x36)inverse_shift: tuple[int, ...] = (0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11)hidden_magic: bytes = b"MEOW"SPEC = ChallengeSpec()def apk_path_from_argv() -> Path:if len(sys.argv) > 1:return Path(sys.argv[1])return Path(__file__).with_name(SPEC.apk_name)def read_hint_image(apk_path: Path) -> Image.Image:with zipfile.ZipFile(apk_path, "r") as archive:with archive.open("assets/hint.png") as fp:return Image.open(fp).convert("RGB")def recover_sbox_from_image(img: Image.Image) -> list[int]:payload = bytearray(0x106)bit_cursor = 0for y in range(img.height):for x in range(img.width):blue = img.getpixel((x, y))[2]if blue & 1:payload[bit_cursor // 8] |= 1 << (7 - (bit_cursor % 8))bit_cursor += 1if bit_cursor >= 0x830:breakif bit_cursor >= 0x830:breakif bytes(payload[:4]) != SPEC.hidden_magic:raise RuntimeError(f"unexpected embedded magic: {bytes(payload[:4])!r}")sbox = list(payload[4:260])if len(set(sbox)) != 256:raise RuntimeError("embedded S-box is not a permutation")return sboxdef pair_from_coordinates(row: int, col: int) -> str:alphabet = SPEC.row_col_alphabetreturn alphabet[row - 1] + alphabet[col - 1]def coordinates_from_pair(pair: str) -> tuple[int, int]:alphabet = SPEC.row_col_alphabetreturn alphabet.index(pair[0]) + 1, alphabet.index(pair[1]) + 1def recover_click_sequence() -> str:first = pair_from_coordinates(1, 1)second = pair_from_coordinates(2, 4)sixth = pair_from_coordinates(6, 1)candidates = [a + b for a in SPEC.row_col_alphabet for b in SPEC.row_col_alphabet]for middle in itertools.product(candidates, repeat=3):sequence = [first, second, *middle, sixth]rows, cols = zip(*(coordinates_from_pair(token) for token in sequence))if sum(rows) != 21 or sum(cols) != 20:continueq = "".join(sequence)if hashlib.md5(q.encode()).hexdigest() == SPEC.q_md5:return qraise RuntimeError("failed to recover _q")def reorder_click_sequence(q: str) -> str:# Decompiled _f1 groups indices by offset modulo 4 in this order.order = (1, 0, 2, 3)return "".join(q[i] for offset in order for i in range(offset, len(q), 4))def derive_aes_key(q: str, sbox: list[int]) -> bytes:reordered = reorder_click_sequence(q)axis_index = {ch: i for i, ch in enumerate("ADFGVX")}out = bytearray()for i in range(32):left = reordered[(2 * i) % len(reordered)]right = reordered[(2 * i + 1) % len(reordered)]table_index = axis_index[left] * 6 + axis_index[right]out.append(sbox[SPEC.lookup_table[table_index]] ^ ((i * 0x13) & 0xFF))return bytes(out)def xtime(value: int) -> int:return ((value << 1) & 0xFF) ^ (0x1B if value & 0x80 else 0)def gf_mul(a: int, b: int) -> int:result = 0while b:if b & 1:result ^= aa = xtime(a)b >>= 1return result & 0xFFdef invert_sbox(sbox: list[int]) -> list[int]:inverse = [0] * 256for i, value in enumerate(sbox):inverse[value] = ireturn inversedef expand_round_keys(key: bytes, sbox: list[int]) -> bytes:round_keys = bytearray(240)round_keys[:32] = keyfor word_index in range(8, 60):temp = list(round_keys[(word_index - 1) * 4 : word_index * 4])if word_index % 8 == 0:temp = [sbox[temp[1]] ^ SPEC.rcon[word_index // 8 - 1],sbox[temp[2]],sbox[temp[3]],sbox[temp[0]],]elif word_index % 8 == 4:temp = [sbox[x] for x in temp]for j in range(4):round_keys[word_index * 4 + j] = round_keys[(word_index - 8) * 4 + j] ^ temp[j]return bytes(round_keys)def add_round_key(state: bytearray, round_keys: bytes, round_index: int) -> bytearray:start = 16 * round_indexreturn bytearray(x ^ y for x, y in zip(state, round_keys[start : start + 16]))def inverse_sub_shift(state: bytearray, inverse_sbox: list[int]) -> bytearray:out = bytearray(16)for out_idx, src_idx in enumerate(SPEC.inverse_shift):out[src_idx] = inverse_sbox[state[out_idx]]return outdef inverse_mix_columns(state: bytearray) -> bytearray:out = bytearray(16)for col in range(4):a = state[4 * col : 4 * col + 4]out[4 * col + 0] = gf_mul(a[0], 14) ^ gf_mul(a[1], 11) ^ gf_mul(a[2], 13) ^ gf_mul(a[3], 9)out[4 * col + 1] = gf_mul(a[0], 9) ^ gf_mul(a[1], 14) ^ gf_mul(a[2], 11) ^ gf_mul(a[3], 13)out[4 * col + 2] = gf_mul(a[0], 13) ^ gf_mul(a[1], 9) ^ gf_mul(a[2], 14) ^ gf_mul(a[3], 11)out[4 * col + 3] = gf_mul(a[0], 11) ^ gf_mul(a[1], 13) ^ gf_mul(a[2], 9) ^ gf_mul(a[3], 14)return outdef decrypt_block(block: bytes, round_keys: bytes, inverse_sbox: list[int]) -> bytes:state = bytearray(block)for round_index in range(14, 0, -1):state = add_round_key(state, round_keys, round_index)if round_index != 14:state = inverse_mix_columns(state)state = inverse_sub_shift(state, inverse_sbox)return bytes(add_round_key(state, round_keys, 0))def decode_flag_body(round_keys: bytes, inverse_sbox: list[int]) -> str:ciphertext = bytes(x ^ 0x66 for x in b64decode(SPEC.cipher_b64))plaintext = b"".join(decrypt_block(ciphertext[offset : offset + 16], round_keys, inverse_sbox)for offset in range(0, len(ciphertext), 16))return plaintext.rstrip(b"\x00").decode()def decode_flag_prefix() -> str:return "".join(chr(x ^ 0x42) for x in (11, 17, 1, 1, 57))def main() -> None:apk_path = apk_path_from_argv()sbox = recover_sbox_from_image(read_hint_image(apk_path))q = recover_click_sequence()key = derive_aes_key(q, sbox)round_keys = expand_round_keys(key, sbox)flag_body = decode_flag_body(round_keys, invert_sbox(sbox))flag = f"{decode_flag_prefix()}{flag_body}}}"print(f"q = {q}")print(f"key = {key.hex()}")print(f"flag = {flag}")if __name__ == "__main__":main()
http://www.jsqmd.com/news/845900/

相关文章:

  • uni-app项目实战:集成uQRCode插件生成动态二维码并保存到相册(避坑指南)
  • 沈阳实地探访大牌包包回收实体店,拆解行业常规评估方式 - 奢侈品回收测评
  • RISC-V开发板深度测评指南:从硬件解析到生态实战
  • 昇思(MindSpore)Web 与 API 推理云托管模型服务技术
  • 2026深圳专业搬家收费标准 大件搬运收费全解 - 从来都是英雄出少年
  • 告别枯燥词汇练习:词达人自动化助手让你的学习效率提升10倍
  • 手把手教你用STM32F103驱动TLC7528双路DAC(附完整代码与避坑指南)
  • 2026产业新风向,细数机器人赛道优质新锐创业公司 - 品牌2025
  • 武汉黄金回收实测:5家头部机构多维度选择标准盘点 - 奢侈品回收测评
  • 2026淄博租车门店推荐,免押金租赁,旅游包车,企事业单位租车,同城租车,机场租车门店优选指南! - 品牌鉴赏师
  • SaaS多租户数据隔离:三种主流方案深度解析与实战避坑指南
  • 抠图软件在线使用有哪些?2026年最全对比测试,找到适合你的工具
  • 2026 中国澳门国际商标注册平台推荐:5 大机构实测,避坑 + 高效指南 - 速递信息
  • 2026年南宁养生馆公司推荐:三个标准帮你选对好公司
  • 座位预约小程序|基于微信小程序的图书馆自习室座位预约管理系统设计与实现(源码+数据库+文档)
  • 2026重口味火锅技术解析:非遗牛油锅底与硬核菜品标准 - 奔跑123
  • 2026年宁夏银川B2B企业网络营销与AI-GEO获客服务商深度评测指南 - 精选优质企业推荐官
  • K8s安全加固实战:认证、授权、网络策略三维度解读
  • 保姆级避坑指南:在Ubuntu 20.04上搞定浙大lidar_IMU_calib(含RS雷达适配)
  • 2026北京工商注册代办机构排行:5家靠谱机构全解析 - 互联网科技品牌测评
  • 2026义乌瓷砖专卖店市场观察:产品交付力与空间适配成熟度评估指南 - 企业品牌优选推荐官
  • Google Veo 4 实战测评!和Kling 3.5/Hailuo 3.0比到底值不值?附详细教程
  • 三大AI黑科技:Video2X让你的老旧视频重获新生
  • 【上饶装修公司排行榜】全优装饰深度解析与2026高性价比整装避坑指南 - 博客万
  • Cadence Virtuoso里搞定ADC动态性能仿真:从FFT设置到Spectrum工具避坑全流程
  • 基于IVC共享内存的虚拟化显示架构:解决汽车座舱多屏性能与隔离难题
  • 别再手动点KEGG了!用R包pathviewR批量给通路图上色,效率翻倍
  • 2026年,行业内债权债务纠纷律师名声究竟如何?真相大揭秘! - 速递信息
  • 嵌入式GUI开发实战:从emWin架构到性能优化全解析
  • 华硕路由器全网广告拦截:AdGuardHome一键安装全攻略 [特殊字符]