DASCTF2025下半年赛 reverse Writeup

ezmac

总共就没几个函数,定位到 sub_10000045C 函数,调用 sub_100000634 ,传入 a6 参数为 57

image-20251209153242037

sub_100000634 只做了异或操作

image-20251209153510536

找到密文 byte_100004022 即可解密

image-20251209153604845

exp

1
2
3
4
5
6
7
8
9
enc = "7D7B687F69784478722174767522267B7C7E787A2E2D7F2D"
a = 57
for i in range(len(enc)//2):
byte = int(enc[2*i:2*i+2], 16)
decoded_byte = byte ^ a
a = a + 1
print(chr(decoded_byte), end='')

# DASCTF{83c720da35436cc0}

androidfff

解压在 /lib 里就看到 flutter 特征,Blutter 直接解就好了,app 名字叫 untitled3 ,那就去 asm/untitled3/main.dart 里找,根据 ui 里的 incorrect 去定位关键逻辑

image-20251209154302690

上下翻翻得到关键信息

image-20251209154842096

现在去恢复 libapp.so 里的符号表,搜索 flagcheck 出来也没几个函数

image-20251209155103805

untitled3_main__FlagCheckerState::ctor_2f7c1c 里找到密文,记得要除2,原因在第一篇博客里解释过了

image-20251209155336633

untitled3_main__FlagCheckerState::_xorEncrypt_29cb18 里找到异或操作

image-20251209155708914

得到 flag:DASCTF{flutter_is_so_easy}

image-20251209160022079

androidfile

jeb 打开,在 MainActivity 找到逻辑,注意到 AES 加密标志

image-20251209161934387

仔细看这里的字符串和题目txt中给的公钥私钥是一样的

image-20251209160716594

直接进 i0.a 函数看

image-20251209160903699

看来原来的数据应该是有 enkey 和 eniv 字段的,那看来 AES 是最后一重加密,继续跟进 B

image-20251209161035701

回到了 MainActivity ,发现是调用了个 native 函数,继续跟进看看,在 libandroidfile.so 里定位到 Java_com_dasctf_androidfile_MainActivity_a_1p

image-20251209161244608

可以看出是个 rc4 加密,密钥是 REVERSE ,最后进行 base64 编码

image-20251209161708619

接下来用 RSA 公私钥解 enkey 和 eniv ,再用他们解 AES 即可

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
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad
from base64 import b64decode

# === 输入 ===
enkey_b64 = '''QMz2qirA80LJiOs30Efl00JsrIv+ZdrM9iB74P/nCWOrzEemEOaq2lN1/V5/rOAoTgBanJO/AcpookhVIOVdsA=='''
eniv_b64 = '''hKH/M/v8zwVICeWlc652BZk2eA/c2g0cLpBwvWBVlphiwBBasdn9HPWk7sb/IaRh8eppZrToUwz6f1eomFJkEQ=='''
aes_ct_b64 = '''UBUSWb+1P3Z/aokV67e5xQ7eaHoEj3JAeC0XA1RckTWdWZYCB/+D7qC3Hao74goX'''

private_key_pem = '''-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAncB8BH4egqfyJBoV
PzGNIuQl/64e5fl1If+CwtICWoiRV4AMfHuiREB+XlTawJ7QD/ZJj2wO6sY4sdNh
yYcC4QIDAQABAkEAh81Gdg+kcFHoD9AsbkRX/atuUtcwXkYL4gK2LMThpdEFHIO7
Scr+SYfwqmm/LMtkbojEGEnNoIfmoLvGfhXaAQIhANDWo8OSMSQFnvh129cFiVfY
KlS4ec24ixvFD8fUD4SRAiEAwWBuZ3kox1n21AsTAxom+E3z5KUUOSUjPXvG6tZB
gVECIDOP2y0tSi6/qIll6BqFxmxG9eSnC4PMfaQkmonXBOHRAiBmJUPsUGmj8/eX
xknCp7vSCYs9SZ3HGcDlp05Jmed8IQIhAJnE1PNe9lC5OazgRYhSG6bGCTbfFHT6
OuwCVIxRSx4P
-----END PRIVATE KEY-----'''

# ==== Step 1: Base64 decode ====
enkey = b64decode(enkey_b64)
eniv = b64decode(eniv_b64)
aes_ct = b64decode(aes_ct_b64)

# ==== Step 2: RSA Raw Decrypt ====
key = RSA.import_key(private_key_pem)

def rsa_raw_decrypt_raw(data):
decrypted_int = pow(int.from_bytes(data, "big"), key.d, key.n)
decrypted = decrypted_int.to_bytes((key.size_in_bits() + 7) // 8, "big")
return decrypted

# 得到原始解密(包含前导 zeros)
raw_key = rsa_raw_decrypt_raw(enkey)
raw_iv = rsa_raw_decrypt_raw(eniv)

# ==== Step 3: 真正 AES key / IV 是最后 16 bytes ====
aes_key = raw_key[-16:]
aes_iv = raw_iv[-16:]

print("[+] AES Key =", aes_key)
print("[+] AES IV =", aes_iv)

# ==== Step 4: AES-CBC 解密 ====
cipher = AES.new(aes_key, AES.MODE_CBC, aes_iv)
pt_padded = cipher.decrypt(aes_ct)
pt = unpad(pt_padded, 16)

print("\n[+] Plaintext =", pt.decode())

"""
[+] AES Key = b'ElmGJYfKbc2gJh0G'
[+] AES IV = b'JZ4tQgwSm3ZZIELJ'

[+] Plaintext = DASCTF{android_encrypto_file_and_plains}
"""

login

先看服务端文件,在 sub_29C0 定位到关键逻辑

服务段先发送 inp u account ,rc4 加密后发送出去

image-20251209163343244

接收到客户端信息,会先解一次 rc4

image-20251209162925166

接着和数组 byte_C1A0 进行比较,错误则提示 back door...

image-20251209163352722

同时还需要验证 key ,逻辑相同,最后比较的数组是 byte_C0A0

这两个数组都是会进行 RSA 解密的,对应的函数都是 sub_73B2

image-20251209164001046

而这里这么多参数,不用去分析程序逻辑,直接计算验证就能分出,谁是n,谁是d

image-20251209164221447

别说d了,甚至p和q都给了

直接解密即可

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
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii

# 密钥参数(n, e, d)
n = 0x9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3
d = 0x28c7df24a5798679db2a44979275f5f3179db180d91335702942fb1b70e985de825da90f2eb65d20ddf8be1d9d4e15bc1d84e95795ff8c0c28ce3c33fde054f6e82a4f4cc22597b350c9c62ccc0188bd4152a701a3601558f22aa9fae8b9fdac6c2bc09b1637f71e0511805e04b203c4fdb2b36ad232fe819b06ed4e57c74f39fd9b72623c16ff2100f148f622bf12876260c4859672360dc0da3da6b45c5c8c6215ccda072765840c213fba11a91d6bf598a8a8065797566c8950a34ea0a072a9ed0c38bdc58662f186ec578ca55d5098443fd566cc722ace9c4e89afc4e302c8a4870e11a003b935f4a102695bfd64bb0fa74dcc372682e2b24ff45a1a69
e = 0x10001

# 密文 HEX(c1、c2)
c1 = "373A2A27B38FD778C716728EBB95BE89A0A057109119A08D5CE49261EBB0E0776D254A40C4D21BD2463E61608771DE401EED13AC6660D996BEA8C8B82BDD0EAF56C38466776EBA31F7B2219230B654A77EC0AF395A01C31C139A4F6B7B8BA845192096165DD7ACD0331E79DBE434ED8C9A66581D26F69E5FAA295F66010076B91A6DD61DB7ABD325F8BD25D928DEBCC02E5555FF81F7AE3E548E3E4659A37F5D3D3C39FBCAD1B583E42FB04FA328EBB77E7841F45B711E77EE23E11989DB2C0E06B8191A456D56BD1A7D42C47FDFDF1179228B57C6EFCA9B9B6A7D22682E5B67C7C46A877FB677F5F317B4823FCDC812F0362BE27C0F5453037148ED30127B26"
c2 = "1638E0EB936140B5527033292CBEFCD73B55CFC7FB79DF51AE3768A0DD9C84AE4580E47A5133B425F4C93EAC97E4B1AA0B4CD30589D004F6D0D19FCBC709E86CC2996B433D29F650B69987A466F05BEF7F69945860DCC44742A511F3621385C89FBD4D73153615789634B25CFC3151A4115BC30C96979E5F965290F36A863E3378B5CFC9BA31438C4BAE22B23EF815EDF7CF1771803BD392A5072B468900B75F5A4377D1DAF3D6F7B7B6850D1A4A4134F2F65840EFAA9B83D31083051DF0FC80A786529159484F62BBB9524F68285F48C7AB8E03BDFECA1A6025AAED9F9728B390689C0C963920C728EB5695FCB9413F9F4E06D3B93DB40E26D6275C84E6126A"

# 构造 RSA 私钥
key = RSA.construct((n, e, d))
cipher = PKCS1_OAEP.new(key)

# 解密并输出
try:
m1 = cipher.decrypt(bytes.fromhex(c1))
print("[+] m1 解密成功:", m1)
except Exception as err:
print("[-] 解密 m1 出错:", err)

try:
m2 = cipher.decrypt(bytes.fromhex(c2))
print("[+] m2 解密成功:", m2)
except Exception as err:
print("[-] 解密 m2 出错:", err)

"""
[+] m1 解密成功: b'qqwweerrttyyuuii'
[+] m2 解密成功: b'aassddffgghhjjll'
"""

得到两个字符串,不知道怎么使用,该去客户端看看了,在关键词 passwd 找到函数 sub_49DD ,各种特征都显示是标准 AES 加密

image-20251209164718928

那两个字符串看来一个是 iv ,一个是 key 了,密文就是 server 里的数组 byte_C2A0

image-20251209165132634

flag :DASCTF{dqmaxfwkm921kr21m;df1m1dqmlk1d12d1}

总结

题目难度还算不大,不过用来给我爽玩一个月之后的复健还是够用了(●’◡’●)