YALA 2
250 pts
题解
拿到密码后点登陆还是登录失败,应该是有什么其他验证方式。
看APK反编译的代码,我们很快发现在验证完密码以后他调用了一个native函数ix
if (var4.ix(var4.a, var4.b, var16) == -1) {
var13 = new b.b.a.d.c.b(new Exception("Initialization failed"));
break label89;
}
它的定义是
public native int ix(byte[] arr1, byte[] arr2, byte[] arr3)
arr3是上一题用户名:密码
的SHA256, 也就是0xAdmin:aeroplane
的SHA256, 34f37b328d0e1666dcf86307dc1bdbbdb3605750385650069ac74ac1edeb359f
随后可以看到arr1被用作AES解密的密钥,arr2是密文。
byte[] var19 = var4.a;
var16 = var4.b;
SecretKeySpec var20 = new SecretKeySpec(var19, "AES");
Cipher var25 = Cipher.getInstance("AES");
var25.init(2, var20);
var16 = var25.doFinal(var16);
解密后的结果应该以“FLAG”开头,代表解密成功,登录成功。
if (var16[0] == 70 && var16[1] == 76 && var16[2] == 65 && var16[3] == 71) {
label54: {
try {
var17 = new StringBuilder();
var17.append("CONGRATS! The last flag is ");
var17.append(b.b.a.c.b(var16));
var17.append(", you have completed this challenge.");
Log.d("ctflevel3", var17.toString());
......
而flag就会是解密的内容。唯一可能出问题的就是这个native方法了
反编译native库, 函数ix就是 sub_DFC
(armv7a)
int __fastcall sub_DFC(JNIEnv *a1, jobject a2, jbyteArray a3, jbyteArray a4, jbyteArray a5)
{
int v8; // r0
unsigned int v9; // r5
char *v10; // r6
int v11; // r0
char *v12; // r2
unsigned int i; // r0
char *v14; // r1
char v15; // r3
jsize v16; // r10
int v17; // r5
int v18; // r4
jsize v19; // r10
jbyteArray v21; // [sp+8h] [bp-88h]
char v22[56]; // [sp+10h] [bp-80h] BYREF
char v23[16]; // [sp+48h] [bp-48h] BYREF
_QWORD v24[2]; // [sp+58h] [bp-38h] BYREF
char v25; // [sp+68h] [bp-28h]
v8 = sub_C0C();
if ( !sub_DC4(v8) )
return -1;
v9 = 0;
v10 = (*a1)->GetByteArrayElements(a1, a5, 0);
v11 = sub_B00(v10);
if ( !v11 )
{
(*a1)->ReleaseByteArrayElements(a1, a3, v10, 2);
return -1;
}
v21 = a4;
v24[0] = 0LL;
v24[1] = 0LL;
v25 = 0;
while ( v9 < 16 )
{
v12 = (char *)v24 + v9;
*((_BYTE *)v24 + v9) = v11;
v9 += 2;
v12[1] = BYTE1(v11);
}
for ( i = 0; i < 16; i += 2 )
{
*((_BYTE *)v24 + i) ^= v10[i];
v14 = (char *)v24 + i;
v15 = v10[i + 1];
v14[1] ^= v15;
}
v16 = (*a1)->GetArrayLength(a1, a3);
v17 = 0;
(*a1)->GetByteArrayRegion(a1, a3, 0, v16, v23);
while ( v17 != 16 )
{
v23[v17] = aACFJandrgukxp[v17] ^ *((_BYTE *)v24 + v17);
++v17;
}
v18 = 0;
(*a1)->SetByteArrayRegion(a1, a3, 0, v16, v23);
v19 = (*a1)->GetArrayLength(a1, v21);
qmemcpy(v22, &unk_2A30, 0x31u);
(*a1)->SetByteArrayRegion(a1, v21, 0, v19, v22);
(*a1)->ReleaseByteArrayElements(a1, a3, v10, 2);
return v18;
}
这个函数调用了 sub_B00
, 把返回值和 arr3 异或然后再与 aACFJandrgukxp 异或, 存在 arr1 中。注意只有 sub_B00
返回值的前16位被用到了。接下来,unk_2A30 被复制到了 arr2 种,函数结束。很明显,可能出问题的就是 sub_B00
的返回值了。
int __fastcall sub_B00(const char *a1)
{
int v2; // r6
int v3; // r0
int v4; // r4
struct hostent *v5; // r0
char *v6; // r0
size_t v7; // r0
ssize_t v8; // r6
__int64 v10; // [sp+0h] [bp-440h] BYREF
_QWORD v11[2]; // [sp+8h] [bp-438h]
unsigned __int16 dest[512]; // [sp+18h] [bp-428h] BYREF
struct sockaddr v13; // [sp+418h] [bp-28h] BYREF
v10 = unk_29F0;
v11[0] = unk_29F8;
*(_DWORD *)((char *)v11 + 7) = 16463121;
sub_AE8(&v10, &unk_2A61, 18);
v2 = 0;
v3 = socket(2, 1, 0);
if ( v3 >= 0 )
{
v4 = v3;
v5 = gethostbyname((const char *)&v10);
if ( !v5 )
goto LABEL_7;
*(_QWORD *)&v13.sa_family = 2LL;
*(_QWORD *)&v13.sa_data[6] = 0LL;
_memmove_chk(&v13.sa_data[2], *v5->h_addr_list, v5->h_length, 12);
*(_WORD *)v13.sa_data = -28641;
if ( connect(v4, &v13, 16) < 0 )
goto LABEL_7;
v6 = strncpy((char *)dest, a1, 0x400u);
v7 = _strlen_chk(v6, 0x400u);
if ( _write_chk(v4, dest, v7, 1024) >= 0
&& (memset(dest, 0, sizeof(dest)), v8 = read(v4, dest, 0x400u), close(v4), v8 >= 2) )
{
v2 = dest[0];
}
else
{
LABEL_7:
v2 = 0;
}
}
return v2;
}
这个函数建立了一个socket,连接的地址是 unk_29F0 和 unk_2A61 ,18字节数据的异或。解密后就是 getuid.api.service
,端口是 8080。返回值就是接收到的数据。因为只有前十六位被用到了,这里可以穷举因为只有2^16种可能
穷举代码:
from Crypto.Cipher import AES
def bxor(ba1,ba2):
return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])
for i in range(0, 2**16):
b = i.to_bytes(2, 'little')
barr = b * 8
karr = b"\x34\xF3\x7B\x32\x8D\x0E\x16\x66\xDC\xF8\x63\x07\xDC\x1B\xDB\xBD"
narr = bxor(barr, karr)
#print(barr)
a4 = b"A%C*F-JaNdRgUkXp"
fa4 = bxor(narr, a4)
data = b"\x97\xD6\x30\x3E\x85\x5E\xDE\xA5\x84\x83\xF6\x92\xC2\x1D\x92\x0F\x9D\xCE\x2B\x72\x4D\xC8\xF7\x7E\x14\x56\x34\x1C\x64\x90\x89\x8E\x51\x5C\xA7\x94\x4A\x20\xCA\x63\xFA\x4D\xAF\xE7\xDE\xF3\x68\xCF"
cipher = AES.new(fa4, AES.MODE_ECB)
de = cipher.decrypt(data)
if de[:2] == b"FL":
print(i)
print(de)
正确的数据就是 16192,解密后是 FLAG6w9z$C&F)J@NcQfTjWnZr4u7x!A%
,所以flag就是 DSO-NUS{464C41473677397A24432646294A404E635166546A576E5A7234753778214125}
我们也可以改下手机的hosts文件,建立下服务器来验证下这个答案:
su
echo "127.0.0.1 getuid.api.service" >> /system/etc/hosts
echo -ne "\x40\x3f" | nc -l -p 8080
点击下登录键,登录成功!
这时候logcat里面也会输出flag
03-03 00:41:52.653 25023 25023 D ctflevel3: CONGRATS! The last flag is 464C41473677397A24432646294A404E635166546A576E5A7234753778214125, you have completed this challenge