RE(CPP)

之前做的cpp的题还是太少了,一遇到这种抽象的就看都看不懂,遂决定要多做一下这些题。写这一篇文章的目的在于想要提高自己对cpp中反编译的代码的理解程度。然后笔者感觉之前的文章好像都很省略,这次准备多写一点文字和图,来讲解清楚。


那先用一道题引入吧。这道题是moectf2023年的

GUI

先通过查找字符串找到主函数

image-20240311180850575

主函数大致就是这样了

静态加动调两步法:

  1. 静态分析发现:在MessageBoxW(hWndParent, Text, L"hint", 0);的位置是正确位置,然后上面的比较是一个函数的返回值,传入了v7和v6,
  2. 不能通过直接的观察发现v7和v6的生成,但是通过动态调试可以发现v7在sub_B00A0A((int)v7, (int)v8);生成,v6在same_op(a91);生成,以及flag是在GetWindowTextW(DlgItem, flag, 1024);位置生成
  3. 通过观察函数名,发现flag传入了same_op,a91也传入了。

那么看看a91,它是一个字符串,可以先提取出来,之后可能有用。

1
[0x000F, 0x003E, 0x0030, 0x0027, 0x0013, 0x0001, 0x007D, 0x0070, 0x0070, 0x0003, 0x007D, 0x0038, 0x000E, 0x007A, 0x0023, 0x007C, 0x000B, 0x001A, 0x003C, 0x007D, 0x0039, 0x007F, 0x003C, 0x004D, 0x004D, 0x004D, 0x0029]

然后程序肯定会读取flag,所以动态调试在same_op(flag)的位置下断点。

打开位置看一下,发现这个是在stack中。再尝试下断点。

[read and write]

然后放开程序,继续跑。

如果断到断点了,不小心切出去了,可以在reg窗口eip的位置点箭头就会跳转回去。

第一次断:

image-20240311182131729

根据这个函数名,很明显是在取len,第二次断到memmove函数,又是内部函数,然后再跑一次,出错了QAQ,这说明什么呢?

我猜想下面两种可能:

一个是用的二级指针,一个是copy的之后就没用了

所以就走不通了,那么这里就补充一手奇怪的cpp知识:

每个类实例化的时候都有一个构造函数/赋值函数对其赋值,所以如果遇到两个本来是一样类型的值进了同一个函数,可以大胆假设它们变成了一个类型,而这个题a91和flag都是这样,同时a91经过函数后v6就被赋值了,可以猜测v6就是a91变成类之后的地址,而flag之后还进行了一次加密,最后的值应该是从v8到v7,只有进入这个函数查看的时候,会发现v4和v3的值的含义

image-20240313203446714

虽然不知道a2是什么,但是动调之后都会发现v4和v3是指针,v4指向字符串第一个,v3指向最后一个,那么后面的循环就是加密了。

具体加密语句是

1
2
3
4
start = sub_180956(a2);
end = sub_18017C(a2);
while ( start != end )
sub_1816B7((*start++ - 5) ^ 0x51);

而最后翻看源代码就知道:

1
2
3
4
5
6
7
8
std::wstring EncryptFlag(const std::wstring& input) {
std::wstring encrypted;
for (wchar_t c : input) {
wchar_t encryptedChar = (c - 5) ^ 0x51; // 加一操作
encrypted += encryptedChar;
}
return encrypted;
}

这表明了确实input是一个特殊类型,而源代码里的遍历被解释成两个指针的操作。

那么也可以确定最后的值就是a91,把它逆运算一下得到flag:

1
2
3
4
5
6
7
8
9
s = b'\x39\x3b\x31\x0f\x3e\x30\x27\x13\x01\x7d\x70\x70\x03\x7d\x38\x0e\x7a\x23\x7c\x0b\x1a\x3c\x7d\x39\x7f\x3c\x4d\x4d\x4d\x29'
decoded_s = s.decode('utf-8')
ord_list = [ord(c) for c in decoded_s]
after = ''
for c in ord_list:
this_one = chr((c^0x51)+5)
after += this_one
print(after)
# moectf{GU1&&W1nd0w2_Pr1m3r!!!}

再次观察源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
case WM_COMMAND:
switch (LOWORD(wParam)) {
case 1:
TCHAR buffer[512];
GetWindowText(GetDlgItem(hWnd, 2), buffer, sizeof(buffer));
std::wstring input = buffer;

// 对输入进行加密操作
std::wstring encryptedInput = EncryptFlag(input);

// 正确加密后的 flag
std::wstring correctEncryptedFlag = L"\x39\x3b\x31\xf\x3e\x30\x27\x13\x1\x7d\x70\x70\x3\x7d\x38\xe\x7a\x23\x7c\xb\x1a\x3c\x7d\x39\x7f\x3c\x4d\x4d\x4d\x29";

if (encryptedInput == correctEncryptedFlag) {
MessageBox(hWnd, TEXT("Congratulations! flag is correct!"), TEXT("hint"), MB_OK);
}
else {
MessageBox(hWnd, TEXT("Sorry, flag error."), TEXT("hint"), MB_OK);
}
break;
}
break;

这里的==由于类型不是原始类型,所以运算符被重载了

用ida进入这个函数查看:

image-20240313204606697

在第一个v6等于a2[5]的位置,进入a2看第6位是什么

image-20240313204701212

进入分析后发现,第五个就是我们输入的字符串

而v5的位置是

image-20240313204929328

正确的加密之后的值,这里也能看出和a91一样

而v4我没有确定,v2是我输入的值的加密,那么最后的return就很明显是比较函数了,(函数名不一样是因为中间有跳转)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool __cdecl sub_18A7B0(int a1, int a2, int a3, int a4)
{
__CheckForDebuggerJustMyCode(&unk_258024);
return a2 == a4 && !sub_180654(a1, a3, a2);
}
int __cdecl sub_18E010(unsigned __int16 *a1, unsigned __int16 *a2, int a3)
{
__CheckForDebuggerJustMyCode(&unk_25801A);
while ( 1 )
{
if ( !a3 )
return 0;
if ( *a1 != *a2 )
break;
++a1;
++a2;
--a3;
}
if ( *a1 >= (int)*a2 )
return 1;
else
return -1;
}

那么大概这个题的逻辑就是这样。


搞完上面这个题,3天已经过去了QAQ,但是差不多理解的就是,cpp里面函数特别抽象,所以要是没有符号表,硬推超级难搞,还是猜吧…

simpleCPP

这道题有符号表,太好了,根据符号表就可以直接看出函数的作用

比如

1
2
3
4
5
6
7
8
9
v4 = sub_1400019C0(std::cout, "I'm a first timer of Logic algebra , how about you?", envp);
std::ostream::operator<<(v4, sub_140001B90);
//就是
//cout << "I'm a first timer of Logic algebra , how about you?"<<endl;
sub_1400019C0(std::cout, "Let's start our game,Please input your flag:", v5);
sub_140001DE0(std::cin, Block);
//就是
//cout "Let's start our game,Please input your flag:"<<endl;
// cin << Block;

十分方便

这种题就可以直接分析了。

mop好像会堆栈不平衡,于是用改标志位跳过。

分析一下:

先是对输入异或,然后每8位顺序读取合成一个变量,最后对4个变量执行方程上的操作

所以直接逆向,先z3,然后得结果

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
from z3 import *

r1 = BitVec('r1', 64)
r2 = BitVec('r2', 64)
r3 = BitVec('r3', 64)
j = BitVec('j', 64)

v22 = r2 & r1
v23 = r3 & ~r1
v24 = ~r2
v25 = r3 & v24
v26 = r1 & v24
v28 = r2
v29 = r3
flag = r3 & v24 & r1 | r3 & (r2 & r1 | r2 & ~r1 | ~(r2 | r1))

s = Solver()

s.add(r3 & ~r1 == 0x11204161012)
s.add(flag == 0x8020717153E3013)
s.add(r3 & ~r1 | r2 & r1 | r3 & v24 | r1 & v24 == 0x3E3A4717373E7F1F)
s.add(((r3 & ~r1 | r2 & r1 | r3 & v24 | r1 & ~r2) ^ j) == 0x3E3A4717050F791F)
s.add((r3 & ~r1 | r2 & r1 | r2 & r3) == (~r1 & r3 | 0xC00020130082C0C))

while s.check() == sat:
m = s.model()
print("Solution found:")
print("r1 = ", m[r1])
print("r2 = ", m[r2])
print("r3 = ", m[r3])
print("j = ", m[j])
# 添加新的约束条件来排除当前的解,一定要是Or,可能出现有一个值不完全约束的情况
s.add(Or(r1 != m[r1], r2 != m[r2], r3 != m[r3], j != m[j]))

一开始我写的是s.add(And(r1 != m[r1], r2 != m[r2], r3 != m[r3], j != m[j])),只有一个解但是怎么都出不来,最后发现原来是用Or,但是最后还是出不来,因为r2的情况太多了

1
2
3
4
5
6
7
8
9
from prism import *

key = [0x69, 0x5F, 0x77, 0x69, 0x6C, 0x6C, 0x5F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x5F, 0x69, 0x73, 0x5F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5F, 0x6F, 0x72, 0x5F, 0x6E, 0x6F, 0x74]

last = [0x3e3a460533286f0d,0x0c00020130082c0c,0x08020717153e3013,0x32310600]
tryi = hextbyte(last,True)

pl(pxor(tryi,key),"NSSCTF")
# NSSCTF{We1l_D0ndeajoa_Slgebra_am_i}

这个还是不对,看了wp发现比赛中间给了hint把中间修改即可e!P0or_a

1
NSSCTF{We1l_D0ne!P0or_algebra_am_i}

写了两道cpp,其实内核还是分析函数作用,然后进行算法逆向,下一周尝试写一点cpp代码然后看看反编译后的样子

RE其它内容

EZ加密器

这题超抽象,全是混淆,可以说根本不知道在干什么,但是由于输入的长度很少,基本上爆破就可以解决。

主函数是一个检查函数+base64加密函数+DES加密函数+最后的转hex字符

一个一个分析

image-20240313215958869

怕是分析不出来什么。。所以就进入了每个函数把所有逻辑复制下来然后爆破

第一个函数

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
//int main() {
// int len; // eax
// unsigned int v3; // r11d
// int v4; // edx
// int v5; // r8d
// int v6; // ecx
// int v7; // r8d
// int v8; // r9d
// int v9; // ecx
// char* p_code; // rcx
// unsigned int this_code; // r8d
// int v12; // eax
// int v13; // edx
// int v14; // r9d
// int v15; // edx
// int v16; // r10d
// unsigned int v17; // edx
//
// // len = strlen(code);
// for (int q = 0; q <= 30; q++)
// {
// len = q;
// cout << "this try: " << len;
// v4 = -7;
// v5 = 1;
// do
// {
// v6 = v5;
// v7 = v4 & v5;
// v8 = v4;
// v4 ^= v6;
// v5 = 2 * v7;
// } while (v5);
// if (v6 != v8)
// {
// do
// {
// v9 = len;
// len ^= v4;
// v4 = 2 * (v4 & v9);
// } while (v4);
// }
// /*code[6] = 0;
// p_code = code;*/
// v3 = len == 0;
// cout << "and result " << v3 << endl;
// }
//
//
// return 0;
//}

验证码是6位

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
//#include <iostream>
//#include <string>
//
//void find_password() {
// std::string chars = "0123456789";
// int v12, v13, v14, v15, v16, v17;
// bool v3;
//
// for (int i = 0; i < chars.size(); ++i) {
// for (int j = 0; j < chars.size(); ++j) {
// for (int k = 0; k < chars.size(); ++k) {
// for (int l = 0; l < chars.size(); ++l) {
// for (int m = 0; m < chars.size(); ++m) {
// for (int n = 0; n < chars.size(); ++n) {
// std::string password = { chars[i], chars[j], chars[k], chars[l], chars[m], chars[n] };
// const char* p_code = password.c_str();
// v3 = true;
//
// do {
// int this_code = *p_code;
// v12 = -49;
// v13 = 1;
// do {
// v14 = v13;
// v15 = v12 & v13;
// v16 = v12;
// v12 ^= v14;
// v13 = 2 * v15;
// } while (v13);
//
// if (v14 != v16) {
// do {
// v17 = this_code;
// this_code ^= v12;
// v12 = 2 * (v12 & v17);
// } while (v12);
// }
//
// if (this_code >= 0xA) {
// v3 = false;
// break;
// }
//
// ++p_code;
// } while (*p_code);
//
// if (v3) {
// std::cout << "Found a password: " << password << std::endl;
// }
// }
// }
// }
// }
// }
// }
//}
//
//int main() {
// find_password();
// return 0;
//}

验证码是数字

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
//int main(void) {
//
//
//
// int len; // eax
// int v3; // edx
// int v4; // r8d
// int v5; // r9d
// int v6; // r8d
// int v7; // r10d
// int v8; // r8d
// unsigned int ans; // r11d
// __int64 v10; // r10
// int v11; // ecx
// int v12; // edx
// int v13; // eax
// int v14; // r8d
// int v15; // edx
// int v16; // r9d
// int v17; // edx
// char v19[42]; // [rsp+2Eh] [rbp-2Ah] BYREF
//
// for (int p = 1; p <= 100; p++)
// {
//
// len = p;
// cout << "this try: " << len;
// v3 = -41;
// v4 = 1;
// do
// {
// v5 = v4;
// v6 = v3 & v4;
// v7 = v3;
// v3 ^= v5;
// v4 = 2 * v6;
// } while (v4);
// if (v5 != v7)
// {
// do
// {
// v8 = len;
// len ^= v3;
// v3 = 2 * (v3 & v8);
// } while (v3);
// }
// ans = 0;
// cout << "and result: " << len << endl;
// }
//}


//#include <iostream>
//#include <string>
//#include <cstring>
//
//__int64 check(__int64 a1) {
// // Your function here
//}
//
//void find_flag() {
// std::string flag(40, '0'); // Initialize a flag string of length 40 with '0'
// int v12, v13, v14, v15, v16, v17;
// bool ans;
//
// // Set the specific positions to "DASCTF{}"
// std::string initial = "DASCTF{}";
// for (int i = 0; i < 8; ++i) {
// flag[i] = initial[i];
// }
//
// // Check the flag
// if (check((__int64)flag.c_str())) {
// std::cout << "The flag: " << flag << " satisfies the condition." << std::endl;
// }
// else {
// std::cout << "The flag: " << flag << " does not satisfy the condition." << std::endl;
// }
//}
//
//int main() {
// find_flag();
// return 0;
//}

flag的长度40和开头结尾要求

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
#include <iostream>
#include <cstring>
#include <bitset>


int main(void) {
char* p_lastflag; // r8
char* p_str2; // rcx
char* end; // r9
char v6; // si
unsigned __int8 left; // di
int v8; // eax
int v9; // edx
int v10; // edi
int v11; // eax
int v12; // edi
int v13; // ebp
int v14; // edi
int v15; // r12d
int v16; // edi
char v17; // di
int v18; // eax
int v19; // edx
int v21; // esi
int v22; // eax
int v23; // esi
int v24; // edi
int v25; // esi
int v26; // ebp
int v27; // esi
char* saved_flag; // [rsp+20h] [rbp-D8h] BYREF
__int64 slen; // [rsp+28h] [rbp-D0h]
char afterbase[8]; // [rsp+38h] [rbp-C0h] BYREF
char Str2[184]; // [rsp+40h] [rbp-B8h] BYREF
slen = 1;
p_lastflag = new char[1];
for (char k = 0; k < 256; k++)
{
*p_lastflag = k;
if ((int)slen <= 0)
{
end = Str2;
}
else
{
p_str2 = Str2;
end = &Str2[2 * (int)slen];
do
{
v6 = *p_lastflag;
left = (unsigned __int8)*p_lastflag >> 4;
v8 = left;
if (left <= 9u)
{
v9 = left ^ 0x30;
if (!left)
v9 = '0';
}
else
{
v9 = 'A';
do
{
v10 = v9 & v8;
v9 ^= v8;
v8 = 2 * v10;
} while (2 * v10);
v11 = -11;
v12 = 1;
do
{
v13 = v12;
v14 = v11 & v12;
v15 = v11;
v11 ^= v13;
v12 = 2 * v14;
} while (v12);
if (v13 != v15)
{
do
{
v16 = v9 & v11;
v9 ^= v11;
v11 = 2 * v16;
} while (2 * v16);
}
}
*p_str2 = v9;
p_str2 += 2;
v17 = v6 & 0xF;
v18 = v6 & 0xF;
if ((v6 & 0xFu) > 9)
{
v19 = 65;
do
{
v21 = v19 & v18;
v19 ^= v18;
v18 = 2 * v21;
} while (2 * v21);
v22 = -11;
v23 = 1;
do
{
v24 = v23;
v25 = v22 & v23;
v26 = v22;
v22 ^= v24;
v23 = 2 * v25;
} while (v23);
if (v24 != v26)
{
do
{
v27 = v19;
v19 ^= v22;
v22 = 2 * (v22 & v27);
} while (v22);
}
}
else
{
v19 = v17 ^ 0x30;
if (!v17)
v19 = 48;
}
*(p_str2 - 1) = v19;
++p_lastflag;
} while (p_str2 != end);
}
*end = 0;
std::cout << std::hex << k << ": " << Str2[0] << " , " << Str2[1] << std::endl;
}
}

最后的逻辑是一位值转换为2位的hex字符串

然后base64那部分算是一眼叮,然后后面那个des没见过全靠signsrch直接分析出来,然后直接解:

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
from Crypto.Cipher import DES
import base64
import itertools
import struct

raw_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
new_table = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/='
dictionary_decode = str.maketrans(new_table, raw_table)
dictionary_encode = dict(zip(dictionary_decode.values(),dictionary_decode.keys()))

# 已知的 DES 加密结果
encrypted_text = bytes.fromhex('0723105D5C12217DCDC3601F5ECB54DA9CCEC2279F1684A13A0D716D17217F4C9EA85FF1A42795731CA3C55D3A4D7BEA')

# 尝试所有可能的 6 位数字组合
for i in itertools.product(range(10), repeat=6):
key = ''.join(map(str, i))
# print(key)
keyt = base64.b64encode(key.encode()).decode() # MTIzMTIzMTIz base64encode(v) 正常的123123进行base64以后的值
keytt = keyt.translate(dictionary_encode)
key = keytt.encode()
# print(key)
# 创建 DES 对象
des = DES.new(key, DES.MODE_ECB)
# 尝试解密
try:
decrypted_text = des.decrypt(encrypted_text)

if (b"DASCTF" in decrypted_text):
print('找到了!密钥是:', key)
print('明文是:', decrypted_text)
break
except:
pass # 如果解密失败,就尝试下一个密钥

CRYPTO

先从简单的练起吧。

bigRSA

1
2
3
4
5
6
7
8
n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n1)
c = pow(c, e, n2)

print("c = %d" % c)

它用了两个n,尝试莫不互素:gcd一下,出来了最大公倍数,那就直接解了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a = GCD(n1,n2)
# print(a)

n1_ = n1 //a
n2_ = n2 // a
# print(n1_)
# print(n2_)

d1 = pow(e,-1,(a-1)*(n2_-1))
c1 = pow(c,d1,n2)
d2 = pow(e,-1,(a-1)*(n1_-1))
c2 = pow(c1,d2,n1)
print(long_to_bytes(c2))
# b'SangFor{qSccmm1WrgvIg2Uq_cZhmqNfEGTz2GV8}'

bad_e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
p = getPrime(512)
q = getPrime(512)
e = 65537

print(p) # 6853495238262155391975011057929314523706159020478084061020122347902601182448091015650787022962180599741651597328364289413042032923330906135304995252477571
print(q) # 11727544912613560398705401423145382428897876620077115390278679983274961030035884083100580422155496261311510530671232666801444557695190734596546855494472819

with open("flag.txt","r") as fs:
flag = fs.read().strip()

m = bytes_to_long(flag.encode())
c = pow(m,e,p*q)
print(c) # 63388263723813143290256836284084914544524440253054612802424934400854921660916379284754467427040180660945667733359330988361620691457570947823206385692232584893511398038141442606303536260023122774682805630913037113541880875125504376791939861734613177272270414287306054553288162010873808058776206524782351475805

已知p,q,e,c,直接解

6,解不出来,说是e是phi的因子,不能得出m,那就分解一下phi

发现p-1是e的倍数,但是q-1不是,根据同余的消去律可消去两边的最大公因数

相当于mod((q-1)),就出来了

baby_e

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import getPrime,bytes_to_long

p,q = getPrime(2048),getPrime(2048)
e = 7
n = p*q
m = bytes_to_long(open('flag.txt','rb').read().strip())
c = pow(m,e,n)
print("c = ",c)
print("n = ",n)

# c = 147693154873835354725007152781732424355869776162377337823960431913672366269917723916891506269449726723757821517328874729037838600793748824028829185409932536014732765063216715033843955453706710187792772702199448156372644163429786386035008302836467605094954587157232829525150652611067567669525072625329634860065850520051628272535479197120008981979404760445193750864902244921407742155742716289495581989134730376783828846663464819337418977287363028738701414486788851136608957124505485242331701209645216580641917007780811842757125048746184068597664780265422321550909392419865169775282217442331295071069272774722564587602419768461231775480847018941840911357926330143045826277813722919121117172763493242590521245640828462665947672485094793188432098216701511715232654611338293295459889814699850788048985878279440740712956248569068077253790198036918598519191892836075254345518967666166925163908185663991353344555402397055977817370082929420443034626201745027965444069777059760865359310439815816749939498993014457995041394803598825093836045546578310632172636478575946653375857640993393714607308326474003446154152048840071034349831168612740218034679021240949747357214453636633636662650940968576792518622437627529244515229173
# n = 553409369582823237678532685244026647155180191225879439432235077135813123637186465008813830373646133388592395760175777499266561095087891764348044063111935877931069321764391883899483374576303169645488542398590564148654412004383012178107972880058460460806768779452529433458826925606225797078653905380530651390617109384086518728626571028089036812787671647095695947167204428442727185744172445701874820612799168887428075695751162763647868386879374037826876671079326544820609721731078985096813307183878793033824330869698508952853770794414757655681370862323768018291030331209143189638496644361618184164228294031490537429556439588954274708598530042700988138862000054458742762198052079867259365645914383561162796796952346445529346145323567650621600171442575319262718389389870407629339714751583360252884338116164466349449862781112019462555743429653595045695696967783338371470032332852204294900011651434678829104876529439166176589508898757122660322523937330848536715937381297551894198974459004139082562228022412335520195652419375915216074658463954339332593244483927157329404652516225481116614815221154229491846087288087715884363786672244655901308480290011237244562251084095684531716327141154558809471185132979704992609461470501119328696999713829

e很小

$c = m^e + kn$

$m = (c - kn)**(1/7)$

尝试低加密指数攻击

1
2
3
4
5
6
7
8
9
10
from gmpy2 import iroot
import libnum
k = 0
while 1:
res = iroot(c+k*n,e)
if(res[1] == True):
print(libnum.n2s(int(res[0]))) #转为字符串
break
k=k+1
# b'moectf{SMaLL_3xPon3nt_Mak3_rSa_w3ak!_!lP0iYlJf!M3rux9G9Vf!JoxiMl903lllA}'

狂飙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
from flag import flag
from Crypto.Util.number import *
from Crypto.Cipher import AES
flag = b'19209389120572313'
m = 88007513702424243702066490849596817304827839547007641526433597788800212065249
key = os.urandom(24)
key = bytes_to_long(key)
n=m % key
flag += (16 - len(flag) % 16) * b'\x00'
iv = os.urandom(16)
aes = AES.new(key,AES.MODE_CBC,iv)
enc_flag = aes.encrypt(flag)

print(n)
print(enc_flag)
print(iv)


#103560843006078708944833658339172896192389513625588
#b'\xfc\x87\xcb\x8e\x9d\x1a\x17\x86\xd9~\x16)\xbfU\x98D\xfe\x8f\xde\x9c\xb0\xd1\x9e\xe7\xa7\xefiY\x95C\x14\x13C@j1\x9d\x08\xd9\xe7W>F2\x96cm\xeb'
#b'UN\x1d\xe2r<\x1db\x00\xdb\x9a\x84\x1e\x82\xf0\x86'

通过n = m % key来求key

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
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from itertools import combinations

# 已知的值
enc_flag = b'\xfc\x87\xcb\x8e\x9d\x1a\x17\x86\xd9~\x16)\xbfU\x98D\xfe\x8f\xde\x9c\xb0\xd1\x9e\xe7\xa7\xefiY\x95C\x14\x13C@j1\x9d\x08\xd9\xe7W>F2\x96cm\xeb'
iv = b'UN\x1d\xe2r<\x1db\x00\xdb\x9a\x84\x1e\x82\xf0\x86'

# 已知的因子
factors = [3,37,439,3939851, 265898280367,5036645362649,342291058100503482469327892079792475478873]

# 遍历所有可能的组合
for r in range(1, len(factors) + 1):
for subset in combinations(factors, r):
# 计算可能的 key
possible_key = reduce(lambda x, y: x*y, subset)
# 检查 key 的长度是否为 24 字节
# 使用找到的 key、iv 和 enc_flag 解密 flag
try:
aes = AES.new(long_to_bytes(possible_key), AES.MODE_CBC, iv)
flag = aes.decrypt(enc_flag)
if flag.startswith(b'flag'):
print('Found possible flag:', flag)
except:
continue