try_decrypt_me

apk文件 - Android逆向 挺好 正好我也正在转型移动安全

我习惯上是用jadx来反编译(界面美观, 一目了然呀)

但是这题 实话说 jeb比较友好

我用jadx卡半天 没找到密文

jeb直接跟踪, 并且该做的某些操作都自动化完成了(比如 替换操作等等)

笔者建议的话 先用jedx看逻辑

然后jeb 而且 jeb还可以跟踪 (动调)

对比一下

image-20230327113004121

image-20230327113120438

主要就是 AES加密

然后注意 CBC模式 这儿有个base64

如果工具解 先base64解密一下

image-20230327113225487

贴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
from base64 import b64decode, b64encode
from Crypto.Cipher import AES

encData = "BxLHc1KruiH31I94W171oal+9olDzgBIjnK/J1Db0IUyi+MbI38+nw62ejCPShRB"
key1 = "r3v3rs3car3fully"
# 在线网站求出MD5 key2 = "reversehavemagic"
key2 = "c368938a22ddbd9c77721fd203ac2c9c"

# 网上找的
def aes_decrypt(key, text):
"""
解密 :偏移量为key[0:16];先base64解,再AES解密,后取消补位
:param encrypted_text : 已经加密的密文
:return:
"""
iv = "r3v3rs3car3fully"
encrypted_text = b64decode(text)
cipher = AES.new(key=key.encode(), mode=AES.MODE_CBC, IV=iv.encode())
decrypted_text = cipher.decrypt(encrypted_text)
return decrypted_text.decode('utf-8')


if __name__ == '__main__':
# key的长度需要补长(16倍数),补全方式根据情况而定,此处我就手动以‘0’的方式补全的32位key
# key字符长度决定加密结果,长度16:加密结果AES(128),长度32:结果就是AES(256)
text = "BxLHc1KruiH31I94W171oal+9olDzgBIjnK/J1Db0IUyi+MbI38+nw62ejCPShRB"
key = "c368938a22ddbd9c77721fd203ac2c9c"

decrypt_result = aes_decrypt(key, text)
print(decrypt_result)

# NKCTF{nI_k@i_sHi_zhu_j1an_il_Jie_RE_le}

CyberChef 直接跑 更快

image-20230327114628137

PMKF

file验证 + 迷宫

运行的 回显

image-20230329190320600

拖入ida分析

image-20230329191050593

image-20230329191120362

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
int sub_401090()
{
char Val; // [esp+4h] [ebp-15Ch]
char Vala; // [esp+4h] [ebp-15Ch]
char Valb; // [esp+4h] [ebp-15Ch]
int v4; // [esp+20h] [ebp-140h]
int v5; // [esp+24h] [ebp-13Ch]
int k; // [esp+2Ch] [ebp-134h]
int i; // [esp+30h] [ebp-130h]
int v8; // [esp+30h] [ebp-130h]
int j; // [esp+30h] [ebp-130h]
int v10; // [esp+30h] [ebp-130h]
char v11; // [esp+34h] [ebp-12Ch]
DWORD NumberOfBytesRead; // [esp+3Ch] [ebp-124h] BYREF
unsigned __int8 Buffer; // [esp+43h] [ebp-11Dh] BYREF
char v14[16]; // [esp+44h] [ebp-11Ch] BYREF
char v15[256]; // [esp+54h] [ebp-10Ch] BYREF

memset(v15, 0, sizeof(v15));
ReadFile(hObject, &Buffer, 1u, &NumberOfBytesRead, 0);
if ( Buffer != 5 )
{
sub_401690("Wrong!\n", Val); // 读取文件第一个byte 必须为5 否则Wrong
CloseHandle(hObject);
exit(0);
}
ReadFile(hObject, v15, Buffer, &NumberOfBytesRead, 0);// 第二次读取 v15个字节 - 必须和405100相等 否则wrong
for ( i = 0; i < Buffer; ++i )
{
if ( v15[i] != byte_405100[i] ) // nkman
{
sub_401690("Wrong!\n", Vala);
CloseHandle(hObject);
exit(0);
}
}
v8 = 0;
v11 = 0;
while ( v8 < Buffer ) // v11 下文用到 是一个key
v11 += v15[v8++];
ReadFile(hObject, v14, 0x10u, &NumberOfBytesRead, 0);
v5 = 18;
for ( j = 0; j < 16; ++j ) // 读取v14(0x10字节)
// 经异或 数组v14 经过下面算法 走迷宫
v14[j] ^= v11;
v10 = 0;
v4 = 1;
while ( v10 < 16 )
{
for ( k = 6; k >= 0; k -= 2 )
{
switch ( ((unsigned __int8)v14[v10] >> k) & 3 )
{
case 0:
v5 -= 18;
break; // 很明显
// 0 上
// 1 右
// 2 下
// 3 左
case 1:
++v5;
break;
case 2:
v5 += 18;
break;
case 3:
--v5;
break;
default:
break;
}
if ( aN[v5] == '*' || aN[v5] == ' ' ) // 不能碰“墙” !
{
v4 = 0;
break;
}
if ( aN[v5] == 'K' ) // K是终点 aN是迷宫 N是起点
{
sub_401690("Congratulations! you found it!\n", Valb);
break;
}
}
++v10;
}
CloseHandle(hObject); // closefile
return v4;
}

主逻辑就是这样 然后的发现没看到Error的回显

hObject 交叉引用 发现了 要在C盘根目录 创建文件nk.ctf

image-20230329191223687

迷宫是这个

1
2
3
4
5
6
7
8
9
10
11
******************
N...*****...*....*
**.*****..*...**.*
...*****.*****.*.*
.**....*..*...*..*
....**.*.*..*...**
*.**...*.*.*******
..*****..*......**
.*......**.****.**
...*****...*K...**
******************

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
a = '1122332212232211011111010000010112110111222323303323221111122333'  # 迷宫
b = []
# 暴破出 算法部分 -- 后来知道就是4转16进制而已
for i in range(0, len(a), 4):
for x in range(0, 256):
# t = (x ^ 0x15) & 0xff
v1 = str((x >> 0) & 3)
v2 = str((x >> 2) & 3)
v3 = str((x >> 4) & 3)
v4 = str((x >> 6) & 3)
tmp = v4 + v3 + v2 + v1
if tmp == a[i:i + 4]:
b.append(x)
print(tmp)
# 逆推 异或的部分
for i in range(len(b)):
b[i] ^= 0x15

for i in b:
print(str(hex(i))[2:], end=' ')
# 将其写入nk.ctf中
# 4f ef 7e b0 0 44 15 4 70 0 be a9 ee b0 43 aa

最后写入文件 用010插入

image-20230329191615350

运行 OK

image-20230329190234252

babyrust - rust/subprocess

什么鬼 ? 我又没学过 rust

我是请教了别的师傅

用python的subprocess模块

进行暴破的

又学到一招 嘿嘿

Orz Orz

拖入ida64 分析 然后 我靠

image-20230329192020361

自己看了看 发现确实能看懂个差不多

但我这嫌麻烦 直接暴破了

image-20230329192736704

image-20230329193011621

image-20230329192810390

image-20230329192859143

贴exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import subprocess

encData = ")&n_qFb'NZXpj)*bLDmLnVj]@^_H"

dict = {}
if __name__ == '__main__':
asc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
lst = []
for i in range(len(asc)):
p = subprocess.Popen(['babyrust.exe'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
a = p.communicate((asc[i].encode()))[0]
dict[a.decode()[134:135]] = asc[i]
print(dict)
for i in encData:
print(dict[i], end="")
# NKCTF{WLcomE_NOWayBaCk_RuST}

image-20230329193130787

not_a_like - RSA/RC4

查壳 有壳 但upx -d脱不掉

image-20230329195431975

这是因为特征码被删了

image-20230329200119731

打开die 改一下

image-20230329195840474

然后再拖一下 OK了

image-20230329195934376

脱完壳 运行不了

但是可以从静态反汇编看出是pyinstaller打包的python程序

(当然, 也可以猜一猜 bushi Orz)

python pyinstxtractor.py xx.exe解包

image-20230329200337933

很明显需要 添加pyc文件头对文件进行修复

stuct

1
55 0D 0D 0A 00 00 00 00 70 79 69 30 10 01 00 00

image-20230329200719067

然后在线反编译

使用https://tool.lu/pyc/反编译pyc文件得到源码

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
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.8

import libnum
import base64
import hashlib
from ctypes import *

def encrypt(text):
data_xor_iv = bytearray()
sbox = []
j = 0
x = y = k = 0
key = '911dcd09ad021d68780e3efed1aa8549'
for i in range(256):
sbox.append(i)
for i in range(256):
j = j + sbox[i] + ord(key[i % len(key)]) & 255
sbox[i] = sbox[j]
sbox[j] = sbox[i]
for idx in text:
x = x + 1 & 255
y = y + sbox[x] & 255
sbox[x] = sbox[y]
sbox[y] = sbox[x]
k = sbox[sbox[x] + sbox[y] & 255]
data_xor_iv.append(idx ^ k)
return data_xor_iv

if __name__ == '__main__':
flag = input('%e8%af%b7%e8%be%93%e5%85%a5flag> ')
pub_key = [
0x1B6A7561D99E6FC35BA3C241159424698BF3CAC017CFCE8BB325CC9AF9CBCBDB3997B08D922C8705FC3EEAEF50D60ADAB2757A7204715483A1D612502970595358BCFE9CD11C98CAD293EB921D777F4F910905D79CDCA5C1EC1FBA5DA74DB165F82BBE29EA0B2E597860FC6D2C51C12D46BF11AFA5018496DDFC3474B10B4457L,
0x6C8E1CC5B384DE3B3316C22CF72D9895406298E172B5F4D890BDC04889BB43CD4892689DE701C84ED68B4CBC7193926BCCB0A4F259D2E752FAEF3CD590A793F120D15424AEB3CD53F5D59B5D41D699694ABF4F01532F0F1CE127B07958FB874982E757EF97643335376790BC990CEE9D7F0D05DA90AD62084C88BFA9C9BEB683L]
m = libnum.s2n(flag)
c = str(pow(m, pub_key[0], pub_key[1]))
q = b'EeJWrgtF+5ue9MRiq7drUAFPtrLATlBZMBW2CdWHRN73Hek7DPVIYDHtMIAfTcYiEV87W7poChqpyUXYI3+/zf5yyDOyE9ARLfa5qilXggu60lmQzFqvFv+1uOaeI2hs2wx+QZtxqGZzC0VCVWvbTQ52nA2UdUtnk8VezRMPMfmf7rOqPxDTv/aacLnI3RdLG2TbT52qtN4+naejI7Xe8HLOL765OZKdDBERKwd5ARQ3UL6YPbuOKOQahIFddnIX6rZ7dTNqCUDOjfJbMdrzJVDNjmNlkLNtYFo7M65Wfwj6PV5vvtT33FsmH50/YLEasnlCiJujYOgi2KCdf5msz1dPEvrXDDL6Csnjo+6m/44RzlluzcqMS5ZJFdrHEh68LIqtu+HCO+69Dyq4e22APq8wgN9kU6R8kikXSn/Ej0N/jOvomFCbkHskRl8xP1KgWFW0SMVDlaDCM4EKG812VgDWgSYOUnVhVpz65uOtg4Z8PrPI+BW4398dQYhD24D9EIPgvtmhNrHiEHouB46ElTGQgZBhtn6y9tL1sw=='
v = encrypt(base64.b64encode(c.encode('utf-8')))
v = base64.b64encode(v)
if v == q:
print('You are right!')
input('')
else:
print('winer winer winnie dinner')
print('Do you think the encryption and decryption are the same?')

到这 我就不会了

然后到话 下面呢

请教队里的师傅 帮忙写的

太厉害了 我好菜

Orz Orz

贴一下

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
# coding:utf-8
import gmpy2
from Crypto.Util.number import long_to_bytes
import libnum
def transform(x, y): # 使用辗转相处将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x//y)
x, y = y, x % y
return res

def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i*numerator+denominator
return denominator, numerator # 得到渐进分数的分母和分子,并返回

# 求解每个渐进分数
def sub_fraction(x, y):
res = transform(x, y)
# 将连分数的结果逐一截取以求渐进分数
res = list(map(continued_fraction, (res[0:i] for i in range(1, len(res)))))
return res

def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q
par = gmpy2.isqrt(b*b-4*a*c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b+par)//(2*a), (-b-par)//(2*a)
return x1, x2

def wienerAttack(e, n):
for (d, k) in sub_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e*d-1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e*d-1)//k # 这个结果就是 φ(n)
px, qy = get_pq(1, n-phi+1, n)
if px*qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
# 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
d = gmpy2.invert(e, (p-1)*(q-1))
return d
print("该方法不适用")


#e = 284100478693161642327695712452505468891794410301906465434604643365855064101922252698327584524956955373553355814138784402605517536436009073372339264422522610010012877243630454889127160056358637599704871937659443985644871453345576728414422489075791739731547285138648307770775155312545928721094602949588237119345
#n = 468459887279781789188886188573017406548524570309663876064881031936564733341508945283407498306248145591559137207097347130203582813352382018491852922849186827279111555223982032271701972642438224730082216672110316142528108239708171781850491578433309964093293907697072741538649347894863899103340030347858867705231
#c = 350429162418561525458539070186062788413426454598897326594935655762503536409897624028778814302849485850451243934994919418665502401195173255808119461832488053305530748068788500746791135053620550583421369214031040191188956888321397450005528879987036183922578645840167009612661903399312419253694928377398939392827
pub_key = [0x1B6A7561D99E6FC35BA3C241159424698BF3CAC017CFCE8BB325CC9AF9CBCBDB3997B08D922C8705FC3EEAEF50D60ADAB2757A7204715483A1D612502970595358BCFE9CD11C98CAD293EB921D777F4F910905D79CDCA5C1EC1FBA5DA74DB165F82BBE29EA0B2E597860FC6D2C51C12D46BF11AFA5018496DDFC3474B10B4457,0x6C8E1CC5B384DE3B3316C22CF72D9895406298E172B5F4D890BDC04889BB43CD4892689DE701C84ED68B4CBC7193926BCCB0A4F259D2E752FAEF3CD590A793F120D15424AEB3CD53F5D59B5D41D699694ABF4F01532F0F1CE127B07958FB874982E757EF97643335376790BC990CEE9D7F0D05DA90AD62084C88BFA9C9BEB683]

c=9197325807645612228390676898165339983130548652295654839867942074997683918988965926084503420887591899752302425508517254065925348804993207641876641922956619138627889163346353214827511032968059044881520153145551162531743849668155869037977721861493727920155941109549597908826026808895049130862236739911496237434

d = wienerAttack(pub_key[0], pub_key[1])
m = pow(c, d, pub_key[1])
print(long_to_bytes(m))

#b'flag{chinese_zhenghan}'