NewStarCTF 2024 Week3

本文最后更新于 2024年11月30日 晚上

不行,我好菜啊TT

更一下,自由支配的时间好少(

时隔半个多月,我终于小有时间更新了!!!

simpleAndroid

按照老方法看 .xml 里 的 activity 内容找主函数

经过 CheckRoot.check() 判断后将正确结果返回,然后跳转到 CheckActivity

isValidInput() 来输入的格式进行检测,长度必须为 24 ,形式以 flag{ 开头 } 结尾,然后通过 CheckData 来进行比较

可以看到一个熟悉的 native,说明这个方法属于 native 层,需要去逆 so 文件了

看了下 wp 居然可以直接改后缀为 .zipso 文件!!!那所有 apk 文件都可以吗?!下次遇到试试这样走捷径(bushi)

搜索 CheckData 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  Class = _JNIEnv::FindClass(a1, "com/example/simpleandroid/UseLess");
//寻找 Java 的 UseLess 类
StaticFieldID = _JNIEnv::GetStaticFieldID(a1, Class, "CHAR_DATA", "Ljava/lang/String;");
//通过 GetStaticFieldID 获取到了字段 CHAR_DATA 的 jfileID
v27 = _JNIEnv::NewStringUTF(a1, "BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/A");
//创建的新的 jString 类型字符串
_JNIEnv::SetStaticObjectField(a1, Class, StaticFieldID, v27);
//通过 SetStaticObjectField 把 v27 赋值给 Java 的 CHAR_DATA 字段
//修改了 Java 层的 UseLess 的 CHAR_DATA 字段

StaticMethodID = _JNIEnv::GetStaticMethodID(a1, Class, "func", "(Ljava/lang/String;)Ljava/lang/String;");
//获取 Java 层函数 func 的 jmethodID
v9 = 0;
v25 = _JNIEnv::CallStaticObjectMethod((_DWORD)a1, Class, StaticMethodID, v30, v3, v4, v7[0]);
//通过 CallStaticObjectMethod 将输入的 v30 传入 func 函数
StringUTFChars = _JNIEnv::GetStringUTFChars(a1, v25, 0LL);
//将上面函数调用的返回值赋给了 StringUTFChars
//把输入作为参数传给了 Java 层的 func 函数,得到返回值

回到 jad-gui 看 UseLess 类,流程可以看出来进行 Base64 的变换,因为 so 对于 CHAR_DATA 进行了更改,所以这里是一个换表的 Base64

输入首先经过换表 Base64 的变化,进入 so 加密。首先前后交换位置,再进行循环右移 4 位的操作,最后和data_1 进行比较。

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
v35 = StringUTFChars;	//base64 的结果赋值
v34 = -1LL;
v23 = __strlen_chk(StringUTFChars, -1LL);
v10 = v23;
v22 = v7;
v11 = &v7[-((v23 + 15LL) & 0xFFFFFFFFFFFFFFF0LL)];
v21 = v23;
for ( i = 0; i < (int)v23; ++i )
v11[i] = *(_BYTE *)(StringUTFChars + i);
for ( j = 0; ; ++j )
{
v8 = j;
if ( j >= (int)v23 / 2 )
break;
v5 = v11;
v18 = v11[j];
v11[j] = v11[v23 - j - 1];
v5[v23 - j - 1] = v18;//将 v11[j] 和 v11[v23-j-1] 交换位置
}
v17 = v11;
v16 = v11;
v15 = &v11[v10];
while ( v16 != v15 )
{
v14 = v16;
*v16 = ((int)(unsigned __int8)*v16 >> 4) | (16 * *v16);//循环右移 4 位
++v16;
}
for ( k = 0; k < (int)v23; ++k )
{
if ( (unsigned __int8)v11[k] != data_1[k] )
{
v33 = 0;
v12 = 1;
return v33;
}
}
v33 = 1;
v12 = 1;
return v33;

所以逆向流程就是:循环移动 -> 交换位置 -> base64

data_1 数据提取出来

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import base64

result = ""
enc = [0xB2, 0x74, 0x45, 0x16, 0x47, 0x34, 0x95, 0x36, 0x17, 0xF4,
0x43, 0x95, 0x03, 0xD6, 0x33, 0x95, 0xC6, 0xD6, 0x33, 0x36,
0xA7, 0x35, 0xE6, 0x36, 0x96, 0x57, 0x43, 0x16, 0x96, 0x97,
0xE6, 0x16]

for i in range(len(enc)):
enc[i] = (enc[i] << 4 | enc[i] >> 4) & 0xff

for i in range(len(enc)//2):
tmp = enc[i]
enc[i] = enc[len(enc) - i - 1]
enc[len(enc) - i - 1] = tmp

result = ''.join([chr(a) for a in enc])
CHAR_DATA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
NewStringUTF = "BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/A"
print(base64.b64decode(result.translate(str.maketrans(NewStringUTF, CHAR_DATA))))

>>b'flag{android_is_simple!}'

SecertsOfKawaii

查看 MainActivity 代码,看起来好像被混淆了,乱乱的

可以使用 jeb 去混淆,解析成 Java 代码(怎么这些工具这么强啊啊啊啊)

往下看代码流程,Java 层有一个 RC4 加密,keyrc4k4y,加密后 Base64 一下传到 so 层,值在 so 层检查

so 文件有壳(以后都会记住要先查壳了),脱壳查看

xxtea(讨厌 tea 系列!!!),密钥是 meow~meow~tea~~~

所以整个流程就是: xxtea -> base64 -> rc4

写 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
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
130
131
132
133
134
135
136
137
138
139
140
141
#include<stdio.h>
#include<stdlib.h>
typedef unsigned int uint32_t;

#define DELTA 0xdeadbeef
#define MX (((z >> 5 ^ y << 3) + (y >> 3 ^ z << 2)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))

unsigned char sbox[257]={0};

//初始化 s 盒
void init_sbox(char *key){
unsigned int i,j,k;
int tmp;
for(i=0;i<256;i++){
sbox[i]=i;
}
j=k=0;
for(i=0;i<256;i++){
tmp=sbox[i];
j=(j+tmp+key[k])%256;
sbox[i]=sbox[j];
sbox[j]=tmp;
if(++k>=strlen((char*)key))
k=0;
}
}
//解密rc4
void rc4(char *key, char *data)
{
int i, j, k, R, tmp;

init_sbox(key);

j = k = 0;
for (i = 0; i < strlen((char *)data); i++)
{
j = (j + 1) % 256;
k = (k + sbox[j]) % 256;

tmp = sbox[j];
sbox[j] = sbox[k];
sbox[k] = tmp;

R = sbox[(sbox[j] + sbox[k]) % 256];

data[i] ^= R;
}
}

//解密 tea
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1)
{
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
}
else if (n < -1)
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}

//base解密
char base64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void decodeBase64(char *str, int len, char **in)
{

char ascill[129];
int i,k = 0;
for (i = 0; i < 64; i++)
{
ascill[base64[i]] = k++;
}
int decodeStrlen = len / 4 * 3 + 1;
char *decodeStr= (char *)malloc(sizeof(char) * decodeStrlen);
k = 0;
for (i = 0; i < len; i++)
{
decodeStr[k++] = (ascill[str[i]] << 2) | (ascill[str[++i]] >> 4);
if (str[i + 1] == '=')
{
break;
}
decodeStr[k++] = (ascill[str[i]] << 4) | (ascill[str[++i]] >> 2);
if (str[i + 1] == '=')
{
break;
}
decodeStr[k++] = (ascill[str[i]] << 6) | (ascill[str[++i]]);
}
decodeStr[k] = '\0';
*in = decodeStr;
}
int main()
{
long long secrets[6] =
{//secrets数组
6866935238662214623LL,
3247821795433987330LL,
-3346872833356453065LL,
1628153154909259154LL,
-346581578535637655LL,
3322447116203995091LL
};
btea((unsigned int *)secrets, -12, (unsigned int *)"meow~meow~tea~~~");
char *flag;
decodeBase64((char *)secrets, strlen((char *)secrets), &flag);
rc4("rc4k4y", (char *)flag);
puts(flag);
}

好难好难好难 (((((T__T)))))

PangBai 过家家(3)

发现附件是一个 python 编译的可执行文件,PyInstaller 是一个能把 .py 脚本打包成 .exe 的 python 库。

给它解包,可以使用 pyinstaller Extractor 进行解包。前提:1. 使用 pyinstaller 进行打包。2. 未加密

解包后文件放在以 exe 名字 +_extracted 的文件夹中

找到和程序同名的 NotNormalExe.pyc 这个文件,用的 https://pylingual.io 反编译它,反编译结果如下

就是一个简单的异或,重点是要解包反编译出来源码

exp:

1
2
3
4
5
6
7
8
9
10
enc = [40, 9, 22, 52, 15, 56, 66, 71, 111, 121,
90, 33, 18, 40, 3, 13, 80, 28, 65, 68, 83,
88, 34, 86, 5, 12, 35, 82, 67, 3, 17, 79
]
flag = ''
key = 'NewStar2024'
for i in range(len(enc)):
enc[i] ^= ord(key[i % len(key)])
flag += chr(enc[i])
print(flag)

NewStarCTF 2024 Week3
http://example.com/2024/11/29/Week3/
作者
butt3rf1y
发布于
2024年11月29日
许可协议