密码脚本小子当太多了,脚本写不利索,打打reverse练习一下写脚本的能力

然后reverse里面流密码和块密码挺多的,借此为crypto打下基础

SSH复现

一、Let's go to learn crypto

用idapro打开文件,f5反编译得到

image-20250308161633827

发现里面给了key,但目前还不知道什么加密方法

而后面又有一个base64,就是把明文用base64编码,然后比较base64编码和encflag

那encflag就需要找到Cry_Encrypt函数

image-20250308161803810

打开发现了是AES加密,所以,和逆向没什么关系,直接解密就行了

image-20250308162043119

随便翻一下就找到encflag了,如果翻不到,直接用search搜索也行

1
2
3
4
5
6
7
from base64 import *
from Crypto.Cipher import AES
enc_flag = "KKN+NK5ZWN4xr4kM1+qq+2wJKUEEaiWmITgnvi2VaXfjscLoN2sbUObWbnc45pZr"
key = b'9e015a9f82d367bc'
flag = b64decode(enc_flag)
decryflag = AES.new(key,AES.MODE_ECB).decrypt(flag)
print(decryflag)

二、babyxor

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v4[56]; // [rsp+20h] [rbp-40h] BYREF
int j; // [rsp+58h] [rbp-8h]
int i; // [rsp+5Ch] [rbp-4h]

sub_401710(argc, argv, envp);
puts("Please input your flag:");
sub_40AA80("%s", v4);
for ( i = 0; i <= 44; ++i )
{
v4[i] ^= Str[i % strlen(Str)];
if ( (i & 1) == 0 )
v4[i] ^= 0x12u;
}
for ( j = 0; j <= 44; ++j )
{
if ( v4[j] != byte_40B040[j] )
{
puts("Sorry, try again.");
return 0;
}
}
puts("Congratulation!");
return 0;
}

读取一个v4字符串,将其和Str异或,如果i是偶数,再多异或一个,然后比较byte40B040

逻辑很简单,因为异或是可逆的

Str = "'sshctf&X0R_key'"

1
2
3
4
byte40B040 = 32h, 20h, 32h, 20h, 32h, 20h, 4Fh, 1, 4Dh, 27h, 12h
0, 47h, 17h, 16h, 2Ch, 22h, 53h, 14h, 39h, 5, 2Bh, 7Dh
26h, 25h, 0Eh, 28h, 1Bh, 4, 0, 0Eh, 3Ch, 23h, 3Ch, 6Bh
2Ah, 47h, 24h, 28h, 19h, 4, 48h, 0Fh, 14h, 7, 13h

直接用byte40b040异或str,就得到flag了

1
2
3
4
5
6
7
8
9
10
11
12
13
byte = [
0x32, 0x20,0x32, 0x20, 0x32, 0x20, 0x4F, 0x01, 0x4D, 0x27, 0x12,
0x00, 0x47, 0x17, 0x16, 0x2C, 0x22, 0x53, 0x14, 0x39, 0x05, 0x2B, 0x7D,
0x26, 0x25, 0x0E, 0x28, 0x1B, 0x04, 0x00, 0x0E, 0x3C, 0x23, 0x3C, 0x6B,
0x2A, 0x47, 0x24, 0x28, 0x19, 0x04, 0x48, 0x0F, 0x14, 0x07, 0x13,0x00
]
str = 'sshctf&X0R_key'
for i in range(45):
byte[i] ^= ord(str[i % len(str)])
if (i & 1 == 0):
byte[i] ^= 0x12
flag = "".join(chr(byte[i]) for i in range(45))
print(flag)

三、babycrack

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char Str[44]; // [rsp+20h] [rbp-30h] BYREF
int i; // [rsp+4Ch] [rbp-4h]

sub_401910(argc, argv, envp);
sub_40E970("Welcome to CTF world!\n");
sub_40E970("Can you crack me?\n");
sub_40E970("Give me your input: \n");
sub_40E910("%s", Str);
if ( strlen(Str) == 38 && !strncmp(Str, "SSHCTF{", 7ui64) && Str[37] == 125 )
{
sub_401550(Str, aSshctfkey);
for ( i = 0; i <= 36; ++i )
{
if ( Str[i] != byte_40F040[i] )
goto LABEL_8;
}
sub_40E970("Good job, cracker!\n");
return 0;
}
else
{
LABEL_8:
sub_40E970("Nonono!\n");
return 0;
}
}

根据代码,sub_401910应该相当于cout,然后sub_401550相当于cin,需要比较Str和byte_40F040的前37位

1
2
3
4
5
byte_40F040     db 0C9h, 22h, 4Ah, 3Ch, 6Ch, 62h, 2 dup(0F9h), 6Fh, 0B1h
.data:000000000040F040 ; DATA XREF: main+BD↑o
.data:000000000040F04A db 92h, 49h, 76h, 0F1h, 0BEh, 0CBh, 89h, 71h, 28h, 0A9h
.data:000000000040F054 db 73h, 52h, 0F9h, 0B4h, 5Eh, 0FEh, 0F3h, 84h, 0C7h, 3Dh
.data:000000000040F05E db 0EBh, 89h, 75h, 0BEh, 6Ah, 3Dh, 13h, 1Bh dup(0)

因为1Bh=16+11=27,一共64位,所以前面的就是37位

但是我们发现前面有一个sub_401550(Str, aSshctfkey),这说明Str是经过处理的,我们假设这个函数是Enc(x) 我们查看这个函数,发现就是一个LCG伪随机数

1
2
3
4
5
6
7
8
9
10
11
  srand(0xDEADBEEF);
for ( i = 0; i <= 36; ++i )
{
v3 = rand() % 255;
v2 = (int)*(unsigned __int8 *)(a1 + i) >> (a2[v3 % strlen(a2)] % 8);
*(_BYTE *)(a1 + i) = (*(_BYTE *)(a1 + i) << (8 - a2[v3 % strlen(a2)] % 8)) | v2;
*(_BYTE *)(a1 + i) ^= *(_BYTE *)(i + 1i64 + a1);
if ( ((unsigned __int8)(v3 & *(_BYTE *)(a1 + i)) | ~(v3 | *(unsigned __int8 *)(a1 + i)) | 7) > 63 )
*(_BYTE *)(a1 + i) ^= i & 0x12 | a2[i % strlen(a2)] & 0x3F;
}
}

已经把种子给出来了,我们用C++生成37个种子,就能得到v3

1
2
3
4
5
6
7
8
srand(0xDEADBEEF); 
int v3_list[37];
for (int i = 0; i <= 36; ++i) {
v3_list[i] = rand() % 255;
}
for (int i = 0; i < 37; ++i) {
printf("%d,", v3_list[i]);
}

然后我们把这个函数逆向就行

我们先解析一下正向,不妨记每一位为,对应的随机数是,key长度是l,

shift = key[vi%l] 值得注意的是,python的移位和C语言是不一样的,python就是简单的乘以2,除以2,但是C需要补位,补码等等

因为char就8位二进制,所以我们假设shift%8是3位,那么,左边是右移5位,只剩下3位高位跑到最后3位,前面都是0,然后右边是左移3位,会把前面三位补到最后面,所以两个或运算之后,因为前面都是0,所以或完就是本来的后五位,然后两个后3位是一样的,因为或本身是不变的,所以这个操作相当于循环右移,就是把前三位移到末尾去

如果我们要用python来逆向的话,移位的时候应该与上一个0xFF,这样就和C++效果一样了

然后 然后如果 就要多进行一步操作 大致上就这个流程,逆向的时候,顺序得倒过来,因为这个是环环相扣的

注意str的最后一位应该是125,而不是byte40f040的0

主要是利用异或的可逆性还有移位的可逆性

从第37位开始遍历,因为这个加密没有涉及第38位,所以38位就是125,

先异或回去,判断条件是否成立,如果成立就赋值,否则就不动

然后和125异或,再循环左移,这样就回复第37位了,以此类推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def decrypt(data, a2):
v3_list = [232,90,117,135,98,176,175,24,8,123,84,245,112,135,139,71,23,72,4,144,110,181,55,140,156,151,75,136,53,253,7,32,95,73,116,219,63]
for j in range(len(data)-2,-1,-1):
v3 = v3_list[j]
shift = v3%len(a2)
temp = data[j] ^ ((j&18)|(ord(a2[shift]) & 63)) & 0xFF
if(v3 &temp|~(v3|temp)|7 > 63):
data[j] = temp
data[j] ^= data[j+1] & 0xFF
data[j] = data[j] << (ord(a2[v3 % len(a2)])% 8) | data[j] >> (8-ord(a2[v3 % len(a2)])% 8) & 0xFF

return data

a2 = "SSHCTFkey"
data = [201,34,74,60,108,98,249,249,111,177,146,73,118,241,190,203,137,113,40,169,115,82,249,180,94,254,243,132,199,61,235,137,117,190,106,61,19,125]
data = decrypt(data, a2)
flag = "".join(chr(i%128) for i in data)
print(flag)

b@se64 wp

打开压缩包,获得一个可执行文件,用IDA pro打开

image-20250201163713098

shift+F12打开函数列表,发现start函数

image-20250201163749480

发现调用了140001154()函数

image-20250201163914388

进而找到14000184E函数

image-20250201163937213

是一个比较简单的字符串比较函数,下面那个疑似base64编码

但是解码之后好像没什么含义

image-20250201164017808

发现还有一个1400016D0函数

image-20250201164048310

让GPT审阅一下,其实就是base64进行了偏移的变体

他顺便把解密代码也写了...,于是拿到flag

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
def custom_base64_decode(encoded_str):
decoded_bytes = []

for i in range(0, len(encoded_str), 4):
if i + 4 > len(encoded_str):
break # 防止越界

c1 = ord(encoded_str[i]) - 61
c2 = ord(encoded_str[i+1]) - 61
c3 = ord(encoded_str[i+2]) - 61
c4 = ord(encoded_str[i+3]) - 61

b1 = (c1 << 2) | (c2 >> 4)
b2 = ((c2 & 0xF) << 4) | (c3 >> 2)
b3 = ((c3 & 0x3) << 6) | c4

decoded_bytes.append(b1)
if c3 != 0:
decoded_bytes.append(b2)
if c4 != 0:
decoded_bytes.append(b3)

return bytes(decoded_bytes).decode()

# 硬编码的 `Str2` 值:
encoded_flag = "QcRaMcBdITjEJB>MSRzKIrZ\\SPJ>QbyoPpErTpNkVBzeJB>MSRypZ`JOSSMqSTq="

# 进行解码:
decoded_flag = custom_base64_decode(encoded_flag)
print("FLAG:", decoded_flag)
#FLAG: RedBag1{H4PPY_N3W_Y3AR_2O25_4nd_h4PPY_3v3RYd4Y}