DASCTF 2025下半年赛 RE wp ezmac 简单逻辑
1 2 3 4 5 enc = [0x7d ,0x7b ,0x68 ,0x7f ,0x69 ,0x78 ,0x44 ,0x78 ,0x72 ,0x21 ,0x74 ,0x76 ,0x75 ,0x22 ,0x26 ,0x7b ,0x7c ,0x7e ,0x78 ,0x7a ,0x2e ,0x2d ,0x7f ,0x2d ] key_start = 0x39 flag = '' .join(chr (b ^ ((key_start + i) & 0xFF )) for i, b in enumerate (enc)) print (flag)
DASCTF{83c720da35436cc0}
androidfile
hacker截获了某公司的重要数据包和RSA公私钥,请你帮助他提取藏在数据包中的flag。
RSA
Java 层
生成两个 16 字节随机串作为 keyStr 和 ivStr(代码里就是 A() 生成的)。
用 RSA 公钥加密 keyStr、ivStr,分别前缀成 enkey_eniv_,整体作为输入传给 native a_p,得到 RC4+base64 的第一段。
AES 部分:用 AES/CBC/PKCS5 对用户输入加密,得到第二段。UI 把第一段 + <-encryptinput-> + 第二段展示。
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 package com.dasctf.androidfile;import R0.c;import android.content.res.Resources;import android.os.Build;import android.os.Bundle;import android.util.Base64;import android.view.View;import android.view.Window;import android.widget.Button;import android.widget.TextView;import androidx.activity.J;import androidx.activity.K;import androidx.activity.p;import androidx.activity.w;import c0.C0121a;import f.AbstractActivityC0139h;import f.C0138g;import i0.View$OnClickListenerC0168a;import java.security.KeyFactory;import java.security.PublicKey;import java.security.spec.X509EncodedKeySpec;import java.util.Random;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;public class MainActivity extends AbstractActivityC0139h { public TextView f1684A; public Button f1685y; public TextView f1686z; static { System.loadLibrary(w.i("ZLIbw2UnROtssBo=\n" , "Bdx/sQpOII0=\n" )); } public MainActivity () { this .f739d.f1683b.f("androidx:appcompat" , new C0121a (this )); i(new C0138g (this )); } public static String A () { String i2 = w.i("EDXRQcjNLspDYIYUm5BxwktojhyTiGnaU3CWBIu9Xu9oTak5sLVW53BVsSGorU7/eF25\n" , "IATjcvz4GKg=\n" ); StringBuffer stringBuffer = new StringBuffer (); Random random = new Random (); for (int i3 = 0 ; i3 < 16 ; i3++) { stringBuffer.append(i2.charAt(random.nextInt(i2.length()))); } return stringBuffer.toString(); } public static String C (String str, String str2, String str3) { byte [] bytes = str2.getBytes(); byte [] bytes2 = str3.getBytes(); SecretKeySpec secretKeySpec = new SecretKeySpec (bytes, w.i("Udks\n" , "EJx/huJaZmg=\n" )); IvParameterSpec ivParameterSpec = new IvParameterSpec (bytes2); Cipher cipher = Cipher.getInstance(w.i("BchPNMUH8BUUxl9IsxXSXiDkcnw=\n" , "RI0cG4ZFszo=\n" )); cipher.init(1 , secretKeySpec, ivParameterSpec); return Base64.encodeToString(cipher.doFinal(str.getBytes(w.i("yd86S2M=\n" , "nIt8ZlvsRB4=\n" ))), 0 ); } public static String D (String str) { byte [] bytes = str.getBytes(); PublicKey generatePublic = KeyFactory.getInstance(w.i("asEy\n" , "OJJz9SnyFic=\n" )).generatePublic(new X509EncodedKeySpec (Base64.decode(w.i("QMXCGE8qPL1G7O8mYw0GuUzS8C1JKiSzXvT0GFg6L7VMyYYubTo33EXs/gEzEjSWS9eNF2EoKZxH\n5Z4aQw49wmnQ/UBsCCmkTO/EJmAtALZJy81YZBA3tmvvgDo5CCaSPcKaXVgiXIRJ9scoRDcto1Tg\n2CdKDiC0TPTwLkoqWMo=\n" , "DYO1bwt7Zfc=\n" ), 0 ))); Cipher cipher = Cipher.getInstance(w.i("sSby\n" , "43WztTWiQRk=\n" )); cipher.init(1 , generatePublic); return Base64.encodeToString(cipher.doFinal(bytes), 0 ); } public native String a_p (String str) ; @Override public final void onCreate (Bundle bundle) { ?? r10; super .onCreate(bundle); int i2 = p.f753a; J j2 = J.f705a; K k2 = new K (0 , 0 , j2); K k3 = new K (p.f753a, p.f754b, j2); View decorView = getWindow().getDecorView(); c.d(decorView, "window.decorView" ); Resources resources = decorView.getResources(); c.d(resources, "view.resources" ); boolean booleanValue = ((Boolean) j2.b(resources)).booleanValue(); Resources resources2 = decorView.getResources(); c.d(resources2, "view.resources" ); boolean booleanValue2 = ((Boolean) j2.b(resources2)).booleanValue(); int i3 = Build.VERSION.SDK_INT; if (i3 >= 30 ) { r10 = new Object (); } else if (i3 >= 29 ) { r10 = new Object (); } else if (i3 >= 28 ) { r10 = new Object (); } else if (i3 >= 26 ) { r10 = new Object (); } else { r10 = new Object (); } Window window = getWindow(); c.d(window, "window" ); r10.C0(k2, k3, window, decorView, booleanValue, booleanValue2); Window window2 = getWindow(); c.d(window2, "window" ); r10.d(window2); setContentView(R.layout.activity_main); this .f1685y = (Button) findViewById(R.id.mybutton1); this .f1686z = (TextView) findViewById(R.id.edit_text_1); this .f1684A = (TextView) findViewById(R.id.edit_text_2); String i4 = w.i("ZKIxD0oa9odiixwxZj3Mg2i1AzpMGu6JepMHD10K5Y9ornU5aAr95mGLDRY2Iv6sb7B+AGQY46Zj\ngm0NRj73+E23DldpOOOeaIg3MWUdyoxtrD5PYSD9jE+Icy08OOyoGaVpSl0Slr5tkTQ/QQfnmXCH\nKzBPPuqOaJMDOU8akvA=\n" , "KeRGeA5Lr80=\n" ); w.i("r2hpyBZCQnOjZWHEAnRgQIpKSc15ZDtzo3BlzAFSWHKjdRj9J3ROBqNGZcsBeE5wjEJisgJbP1SF\nUEbzClFkZ7JbZ8QJZlpdzRcU73V1ZwCrRwvJN2dCcrVOSdgWJ0p8hGlV4xJWSRq6TXTrN1k8YKYO\nesAqIXx+1FJ5vjN3RVmbeEPJdEJCdaNwYcgBeE5wihkRzSR0IFqBZ2jlBCpKQoBKctJvcn9Et1VD\n/Rh4Un3WRmu4DF5fWZJFZcwIWkQGsUJSoRNKbUaTTE2lDF5/WoBOSs8HVmV/jWhG5y9ffXaESXjr\nAUJCWaNvZN0vK0Rir3JxzC5lYwDQGEPMKUVtaKlNc74lcDkFi1lWzAQrbWSmFXPYAXpOcJV2Yv8a\nIGBemhBOuHFSeGWjWU/na1Y4S9dqdd8PQF5bsnlWzXZnUXOFd2XJCVdEYdBYEP4Tej0ek2hM5nZR\neneaTFjNeXZYX6EVcMcmclpaj05O0gJcQ2OjSGLnCkZbQrdmTeB4PG5pmkpOyTAkfWKheFOzE0k4\neaVCZOYwIz57j0REsglCQlmja07PcUNFVNtNY78PcnFWsHhI2QclaXahdULsBltfB61UV8kWWnNj\nsVkU2g==\n" , "4iEgikATCzE=\n" ); this .f1685y.setOnClickListener(new View$OnClickListenerC0168a (this , A(), i4, A())); } }
1 2 enkey_QMz2qirA80LJiOs30Efl00JsrIv+ZdrM9iB74P/nCWOrzEemEOaq2lN1/V5/rOAoTgBanJO/AcpookhVIOVdsA== eniv_hKH/M/v8zwVICeWlc652BZk2eA/c2g0cLpBwvWBVlphiwBBasdn9HPWk7sb/IaRh8eppZrToUwz6f1eomFJkEQ=
直接看so层
Java_com_dasctf_androidfile_MainActivity_a_1p
有个明显的RC4,最后还有个base64,很清晰的逻辑
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 import base64, refrom Crypto.Cipher import ARC4, AESfrom Crypto.Util.Padding import unpadfrom Crypto.PublicKey import RSAtxt = open ("截获包含flag的数据包.txt" ,"r" ,encoding="utf-8" ).read() first, rest = txt.split("<-encryptinput->" , 1 ) aes_b64 = rest.split("\n" )[0 ].strip() rc4_plain = ARC4.new(b"REVERSE" ).decrypt(base64.b64decode(first)).decode() key_b64, iv_b64 = rc4_plain[len ("enkey_" ):].split("eniv_" ) key_b64 = "" .join(key_b64.split()) iv_b64 = "" .join(iv_b64.split()) pem_priv = re.search(r"-----BEGIN PRIVATE KEY-----\\s*(.+?)\\s*-----END PRIVATE KEY-----" , txt, re.S).group(1 ).replace(" " ,"" ).replace("\n" ,"" ) priv = RSA.import_key("-----BEGIN PRIVATE KEY-----\n" +pem_priv+"\n-----END PRIVATE KEY-----" ) def rsa_raw (b64_ct ): ct = int .from_bytes(base64.b64decode(b64_ct), "big" ) pt = pow (ct, priv.d, priv.n).to_bytes((priv.size_in_bits()+7 )//8 , "big" ) while pt and pt[0 ]==0 : pt = pt[1 :] return pt aes_key = rsa_raw(key_b64) aes_iv = rsa_raw(iv_b64) cipher_bytes = base64.b64decode(aes_b64) flag = unpad(AES.new(aes_key, AES.MODE_CBC, aes_iv).decrypt(cipher_bytes), 16 ) print (flag.decode())
DASCTF{android_encrypto_file_and_plains}
androidfff flutter逆向
先用blutter导出name然后导入IDA分析,blutter产生以下文件
把ida_script导入ida即可
然后看asm的main.dart,可以发现_checkFlag
_checkFlag(起始 0x29c7b0)的逻辑块在文件中 _checkFlag(/* No info */)段。这里能看到
LoadField 取 TextEditingController 的文本
调 _xorEncrypt(bl #0x29cb18)
取构造函数里写死的列表 field_1b;
调 ListEquality.equals
_xorEncrypt 本体在同一文件的 “_xorEncrypt(/* No info */)” 段
入口创建 CodeUnits 对象,保存输入字符串;
取闭包地址 [pp+0xc038](注释 “AnonymousClosure … _xorEncrypt (0x29cb18)”)创建闭包;
闭包体就在 _xorEncrypt 段后面
1 2 3 cipher = [236,230,194,226,204,232,146,168,188,142,140,140,174,128,218,182,130,218,130,186,218,174,166,130,150,158] flag = ''.join(chr((v >> 1) ^ 0x32) for v in cipher) print(flag) # DASCTF{flutter_is_so_easy}
DASCTF{flutter_is_so_easy}
login server里有个RSA
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 import socketKEY = b"qwertyui" magic1 = bytes .fromhex("1638e0eb936140b5527033292cbefcd73b55cfc7fb79df51ae3768a0dd9c84ae4580e47a5133b425f4c93eac97e4b1aa0b4cd30589d004f6d0d19fcbc709e86cc2996b433d29f650b69987a466f05bef7f69945860dcc44742a511f3621385c89fbd4d73153615789634b25cfc3151a4115bc30c96979e5f965290f36a863e3378b 5cfc9ba31438c4bae22b23ef815edf7cf1771803bd392a5072b468900b75f5a4377d1daf3d6f7b7b6850d1a4a4134f2f65840efaa9b83d31083051df0fc80a786529159484f62bbb9524f68285f48c7ab8e03bdfeca1a6025aaed9f9728b390689c0c963920c728eb5695fcb9413f9f4e06d3b93db40e26d6275c84e6126a" )magic2 = bytes .fromhex("373a2a27b38fd778c716728ebb95be89a0a057109119a08d5ce49261ebb0e0776d254a40c4d21bd2463e61608771de401eed13ac6660d996bea8c8b82bdd0eaf56c38466776eba31f7b2219230b654a77ec0af395a01c31c139a4f6b7b8ba845192096165dd7acd0331e79dbe434ed8c9a66581d26f69e5faa295f66010076b91a6 dd61db7abd325f8bd25d928debcc02e5555ff81f7ae3e548e3e4659a37f5d3d3c39fbcad1b583e42fb04fa328ebb77e7841f45b711e77ee23e11989db2c0e06b8191a456d56bd1a7d42c47fdfdf1179228b57c6efca9b9b6a7d22682e5b67c7c46a877fb677f5f317b4823fcdc812f0362be27c0f5453037148ed30127b26" )magic3 = bytes .fromhex("add1d11960c22d9166dac3c26725c81909176b238e3003aa57aacba0a226b7c31c220b8d209cb495b55db4e27d4e438e088000000000000009820000000000001082000000000000188300000000000020840000000000000000000000000000637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7f d9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdb" )def rc4 (key: bytes , data: bytes ) -> bytes : S = list (range (256 )); j = 0 for i in range (256 ): j = (j + S[i] + key[i % len (key)]) & 0xFF S[i], S[j] = S[j], S[i] i = j = 0 ; out = bytearray () for b in data: i = (i + 1 ) & 0xFF j = (j + S[i]) & 0xFF S[i], S[j] = S[j], S[i] out.append(b ^ S[(S[i] + S[j]) & 0xFF ]) return bytes (out) def send_stage (sock, payload: bytes ): sock.sendall(rc4(KEY, payload)) resp = rc4(KEY, sock.recv(4096 )) print (resp) return resp if __name__ == "__main__" : with socket.create_connection(("127.0.0.1" , 8080 )) as s: send_stage(s, b"req login..." ) send_stage(s, magic1) send_stage(s, magic2) send_stage(s, magic3) print (rc4(KEY, s.recv(4096 )))
account: aassddffgghhjjll
key: qqwweerrttyyuuii
Server:handle_client(0x29C0) 调用 rsa_priv_decrypt(0x73B2)
sub_73B2 用 .rodata 里的十六进制字符串 rsa_{n,e,p,q,d}_hex 生成 RSA 私钥,并用 RSA_private_decrypt(…, padding=OAEP) 解密收到的密文。但解出的结果仅存入局部变量,未参与任何校验,后面仅比较收到的 RC4 明文是否等于 magic_stage1/2/3,构成后门。
Client:sub_5042 用同样的 rsa_{n,e,p,q}_hex 构建 RSA 公钥,RSA_public_encrypt(…, padding=OAEP) 加密 account、key,配合 AES-CBC 加密 passwd 发送。解出的明文实际上是
这里调了4125为AES加密,account是IV
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Cipher import AESiv = b'aassddffgghhjjll' key = b'qqwweerrttyyuuii' ct = bytes .fromhex( "add1d11960c22d9166dac3c26725c81909176b238e3003aa57aacba0a226b7c" "31c220b8d209cb495b55db4e27d4e438e0880000000000000098200000000000" "0108200000000000018830000000000002084000000000000000000000000000" "00637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca47" "2c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27" "b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4" "c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110" "fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814d" "e5e0bdb" )pt = AES.new(key, AES.MODE_CBC, iv).decrypt(ct) flag = pt.split(b'\x06' )[0 ] print (flag.decode())
DASCTF{dqmaxfwkm921kr21m;df1m1dqmlk1d12d1}