2024VishwaCTF

记录近期做的一些赛题,包括VishwaCTF,osu,GHCTF,pearlCTF

VishwaCTF

poly fun

题目描述:

Its a simple symmetric key encryption, I am sure you will be able to solve it (what do you mean the key looks weird)

task.py

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
import numpy as np
import random

polyc = [4,3,7]
poly = np.poly1d(polyc)


def generate_random_number():
while True:
num = random.randint(100, 999)
first_digit = num // 100
last_digit = num % 10
if abs(first_digit - last_digit) > 1:
return num


def generate_random_number_again():
while True:
num = random.randint(1000, 9999)
if num % 1111 != 0:
return num


def transform(num):
number = random.randint(1, 100000)
org = number
number *= 2
number += 15
number *= 3
number += 33
number /= 6
number -= org
if number == 13:
num1 = random.randint(1, 6)
num2 = random.randint(1, 6)
number = num1 * 2
number += 5
number *= 5
number += num2
number -= 25
if int(number / 10) == num1 and number % 10 == num2:
number = generate_random_number()
num1 = int(''.join(sorted(str(number), reverse=True)))
num2 = int(''.join(sorted(str(number))))
diff = abs(num1 - num2)
rev_diff = int(str(diff)[::-1])
number = diff + rev_diff
if number == 1088:
org = num
num *= 2
num /= 3
num += 5
num *= 4
num -= 9
num -= org
return num
else:
number = generate_random_number_again()
i = 0
while number != 6174:
digits = [int(d) for d in str(number)]
digits.sort()
smallest = int(''.join(map(str, digits)))
digits.reverse()
largest = int(''.join(map(str, digits)))
number = largest - smallest
i += 1

if i <= 7:
org = num
num *= 2
num += 7
num += 5
num -= 12
num -= org
num += 4
num *= 2
num -= 8
num -= org
return num
else:
org = num
num **= 4
num /= 9
num += 55
num *= 6
num += 5
num -= 23
num -= org
return num
else:
org = num
num *= 10
num += 12
num **= 3
num -= 6
num += 5
num -= org
return num
else:
org = num
num += 5
num -= 10
num *= 2
num += 12
num -= 20
num -= org
return num


def encrypt(p,key):
return ''.join(chr(p(transform(i))) for i in key)


key = open('key.txt', 'rb').read()
enc = encrypt(poly,key)
print(enc)

enc_key

1
☞➭⥄⫣Ⲋ⸹⿰ㆯ㍶☞⒗☞☞☞➭☞⥄☞⫣☞Ⲋ☞⸹☞⿰☞ㆯ☞㍶➭⒗➭

enc_flag

1
u5FUKxDUxH9y8yxvfaaU+GSXDwvJS6QxlN/3udOEzpU6fIVUExjDLsB3LKqUTz/x

花近一小时琢磨后发现这个transform函数并无作用,就是说transform(number) = number

key的每位字符进行了多项式运算:

$enckey_i = 4\times key^2 + 3\times key + 7$

解这个方程即可获得key的每位字符

解出来后key = 12345678910111213141516171819202

根据题目描述的,这是个简单的对称加密,于是找了个AES的解密网站AES在线加密解密工具 - MKLab在线工具

Intellectual Heir

task.py

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
# my secret to hide the combination of my safe in fornt of all without anyone getting a clue what it is ;)

#some boring python function for conversion nothing new
def str_to_ass(input_string):
ass_values = []
for char in input_string:
ass_values.append(str(ord(char)))
ass_str = ''.join(ass_values)
return ass_str

input_string = input("Enter the Combination: ")
result = str_to_ass(input_string)
msg = int(result)

#not that easy, you figure out yourself what the freck is a & z
a =
z =

f = (? * ?) #cant remember what goes in the question mark
e = #what is usually used

#ohh yaa!! now you cant figure out $h!t
encrypted = pow(msg, e, f)
print(str(encrypted))

#bamm!! protection for primes
number =
bin = bin(number)[2:]

#bamm!! bamm!! double protection for primes
bin_arr = np.array(list(bin), dtype=int)
result = np.sin(bin_arr)
result = np.cos(bin_arr)
np.savetxt("file1", result)
np.savetxt("file2", result)

file.txt

1
4400037514278889258479265625258024039636437755883377709505596356049534358755375772484057042989024750972247184288820831886430459963472328358741858934783775986591400972020736548834642094922678189447202173710409868474198821576627330424767999152339702779346380

file1.txt

1
2
3
4
5
6
5.403023058681397650e-01
1.000000000000000000e+00
5.403023058681397650e-01
1.000000000000000000e+00
5.403023058681397650e-01
......

file2.txt

1
2
3
4
5
6
7
8.414709848078965049e-01
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
8.414709848078965049e-01
......

file1.txtfile2.txt内容蛮多的,就不贴全了

str_to_ass()函数的作用就是把输入的字符变成ASCII码,再转成str,eg:输入A,则str_to_ass('A') = 65

a,z其实不用管,看了后面encrypted知道这大概是RSA加密,然后猜f应该就是模,e说了是最常用的,所以是65537

然后下面给了两组三角函数的值,分别放在了两个文件里面。

只需要知道np.sin()这个函数的作用就是算bin_arr里面每个元素的正弦值,比如bin_arr = [1 0],那么返回的就是[sin 1, 0]

不知道也没关系,自己跑一下脚本也可以看出规律

同理np,cos()也就明白了

file.txt放的就是c,file1.txt放的是np.cos(),file2.txt放的是np.sin()

这时候我们只需要根据两个文件的内容来恢复number

file1.txt来说,如果此时的值是0,就说明当前二进制的值是0,否则当前二进制为1

file2.txt同理,此时的值为1的时候,当前二进制的值是0,否则当前二进制的值是1

通过两个文件,可以得到两个素数,而且都是426bit,再根据c的大小是850bit,说明思路并没有问题,然后解密即可

不过需要注意的是最后得到的m应该做一个对str_to_ass()的逆向

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
from Crypto.Util.number import *
import gmpy2

c = 4400037514278889258479265625258024039636437755883377709505596356049534358755375772484057042989024750972247184288820831886430459963472328358741858934783775986591400972020736548834642094922678189447202173710409868474198821576627330424767999152339702779346380

number = ""
lines = open("file1.txt",'rb').readlines()
for i in range(len(lines)):
data = eval(lines[i])
if data == 1:
number += '0'
else:
number += '1'

number1 = ""
lines = open("file2.txt",'rb').readlines()
for i in range(len(lines)):
data = eval(lines[i])
if data == 0:
number1 += '0'
else:
number1 += '1'

p = int(number,2)
q = int(number1,2)
n = p*q
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(m)
# 8948859564825195843551958748828435899579709551
msg = str(m)
flag = "VishwaCTF{"
for i in range(0,len(msg),2):
mm = int(msg[i:i+2])
flag += chr(mm)
flag += "}"
print(flag)
# VishwaCTF{Y0U_@R3_T#3_W0RT#Y_OF_3}

BitBane

task.cpp

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
142
143
144
145
146
147
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

int createTopping(int curr, int idx, int &not_remainder)
{
int temp = 0;
int num = 1;
num = num << 1;
while (curr)
{
int remainder = curr % idx;
if (remainder)
{
temp = temp * 10 + remainder;
curr = curr - remainder;
}
else
{
num = num | 1;
curr = curr / idx;
}
num = num << 1;
}
temp = temp << 1;
temp = temp | 1;
not_remainder = temp;
return num | 1;
}

int createBase(int not_remainder)
{
int num = 0;
for (int i = 0; i < 30; ++i)
{
if (not_remainder)
{
num = num | (not_remainder & 1);
not_remainder = not_remainder >> 1;
}
num = num << 1;
}
return num;
}

int create(int curr, int idx)
{
int not_remainder = 0;
int topping = createTopping(curr, idx, not_remainder);
int base = createBase(not_remainder);
int num = base | topping;
return num;
}

bool checkValidity(int num)
{
for (int i = 2; i * i < num; ++i)
{
if (num % i == 0)
return false;
}
return true;
}

void extraSecurity(vector<int> &encryption)
{
int n = encryption.size();
for (int i = 0; i < n; ++i)
{
int idx = i + 2;
if (checkValidity(idx))
{
encryption[i] = ~encryption[i];
}
}
}

void encode(vector<int> &encryption, const string &data, string &key)
{
int len = data.length();
for (int i = 0; i < len; ++i)
{
int curr = data[i];
int idx = (i % 8) + 2;
int num = create(curr, idx);
encryption.push_back(num);
}
}

void applyKey(vector<int> &encryption, string &key)
{
int n = key.size();
for (int i = 0; i < n; ++i)
{
int curr = key[i];
int cnt = 0;
int cpy = curr;
while (cpy)
{
if (cpy & 1)
++cnt;
cpy = cpy >> 1;
}
curr = curr << (i + 10);
while (cnt--)
{
curr = curr << 1;
curr = curr ^ 1;
}
int k = encryption.size();
for (int j = 0; j < k; ++j)
{
encryption[j] = encryption[j] ^ curr;
}
}
}

void writeToFile(const vector<int> &encryption)
{
ofstream outfile("Encrypted.txt");
string data;
for (auto ele : encryption)
{
data += to_string(ele);
data += " ";
}
outfile << data;
outfile.close();
}

int main()
{
fstream file;
file.open("Flag.txt");
string data;
file >> data;
file.close();
vector<int> encryption;
string key = "VishwaCTF";
encode(encryption, data, key);
applyKey(encryption, key);
extraSecurity(encryption);
writeToFile(encryption);
return 0;
}

这道题有点逆向的感觉

先看主函数,先把flag进行了加密,然后用applyKey对密文进行处理,然后再用extraSecurity对密文再次处理

在看extraSecurity这个函数之前,先看一下checkValidity(x)这个函数,作用就是判断x是否是素数

然后就很好理解extraSecurity(encryption)这个函数的作用就是,是根据目前索引值i再加2的值,即i+2是否是素数,决定是否对encryption[i]取反。这个我们很容易就可以逆回去。

再看完applyKey这个函数,具体细节我就不说了,说不太清楚。总之可以发现,既然我们有了key,完全可以把原来的encryption恢复,(可以自己测试一下)。

最后就是encode()函数,看了很久发现没什么可以利用的点

最后想了想,因为有了key,完全可以通过爆破的方式来求解flag,前面的理解完全都不用看了。

exp.py

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
def create_topping(curr, idx):
temp = 0
num = 1
num = num << 1
while curr:
remainder = curr % idx
if remainder:
temp = temp * 10 + remainder
curr = curr - remainder
else:
num = num | 1
curr = curr // idx
num = num << 1
temp = temp << 1
temp = temp | 1
not_remainder = temp
return num | 1, not_remainder

def create_base(not_remainder):
num = 0
for i in range(30):
if not_remainder:
num = num | (not_remainder & 1)
not_remainder = not_remainder >> 1
num = num << 1
return num

def create(curr, idx):
not_remainder = 0
topping, not_remainder = create_topping(curr, idx)
base = create_base(not_remainder)
num = base | topping
return num

def check_validity(num):
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
return False
return True

def extra_security(encryption):
n = len(encryption)
for i in range(n):
idx = i + 2
if check_validity(idx):
encryption[i] = ~encryption[i]

def encode(encryption, data, key):
len_data = len(data)
for i in range(len_data):
curr = ord(data[i])
idx = (i % 8) + 2
num = create(curr, idx)
encryption.append(num)

def apply_key(encryption, key):
n = len(key)
for i in range(n):
curr = ord(key[i])
cnt = 0
cpy = curr
while cpy:
if cpy & 1:
cnt += 1
cpy = cpy >> 1
curr = curr << (i + 10)
while cnt > 0:
curr = curr << 1
curr = curr ^ 1
cnt -= 1
k = len(encryption)
for j in range(k):
encryption[j] = encryption[j] ^ curr

def write_to_file(encryption):
with open("Encrypted.txt", "w") as outfile:
data = " ".join(str(ele) for ele in encryption)
outfile.write(data)

from tqdm import *
if __name__ == "__main__":
enc = [-1934298443,-1728250251,-2103640211,-1153630219,1775435890,-1670578291,2009268234,-2009268235,1950549658,-1992754035,1673724026,-1398997107,1405288466,1824718858,2131951730,-1765998643,1934298490,-1330315635,2063794322,1889730674,1124270194,-2059599891,1086521394,-1858273331,1909484170,1229390194,1757609994,-1275265139,1201864818,-1892876403,1673723922,2009268234,1950549402,1229390194,1947140466,-1942159371,1849884786,1703084146,1824718858,-1665335347,1909484170,-1229390195,1757609994,1170407434,1124270194,-1918042227,2038628466,-1982005363,1950549834,1124270322,1782775922,-1738735731,1768095858,1842544754,2127757426]
enc[23] = ~enc[23]
enc[47] = ~enc[47]
key = "VishwaCTF"
know = "VishwaCTF{"
for j in range(len(enc) - len(know)):
for i in range(33,128):
encryption = []
encode(encryption,know + chr(i),key)
apply_key(encryption,key)
extra_security(encryption)
if (enc[10+j] == encryption[10+j]):
know += chr(i)
break
# else:
# print(f"第{10+j}处出错")
print(know)
# VishwaCTF{BIT5_3NCRYPT3D_D3CRYPTED_M1ND5_D33PLY_TE5T3D}

爆破的时候出了点小问题,慢慢调试了之后找到问题在于enc[23]enc[47]没取反,所以对enc[23],enc[47]进行了手动取反

Lets smother the King!

题目描述:

In my friend circle, Mr. Olmstead and Mr. Ben always communicate with each other through a secret code language that they created, which we never understand. Here is one of the messages Mr. Ben sent to Mr. Olmstead, which I somehow managed to hack and extract it from Ben’s PC. However, it’s encrypted, and I don’t comprehend their programming language. Besides being proficient programmers, they are also professional chess players. It appears that this is a forced mate in a 4-move chess puzzle, but the information needs to be decrypted to solve it. Help me out here to solve the chess puzzle and get the flag.

Flag format: VishwaCTF{move1ofWhite_move1ofBlack_move2ofWhite_move2ofBlack_move3ofWhite_move3ofBlack_move4ofWhite}.

Note: Please use proper chess notations while writing any move.

code.txt

1
D'`A_9!7};|jE7TBRdQbqM(n&JlGGE3feB@!x=v<)\r8YXtsl2Sonmf,jLKa'edFEa`_X|?UTx;WPUTMqKPONGFjJ,HG@d'=BA@?>7[;4z21U54ts10/.'K+$j(!Efe#z@a}vut:[Zvutsrqj0ngOe+Lbg`edc\"CBXW{[TSRvP8TMq4JONGFj-IBGF?c=a$@9]=6|:3W10543,P*/.'&%$Hi'&%${z@a}vut:xqp6nVrqpoh.fedcb(IHdcba`_X|V[ZYXWPOsMLKJINGkKJI+G@dDCBA#"8=6Z4z21U5ut,P0)(-&J*)"!E}e#"!x>|uzs9qvo5srkpingf,Mchg`_%cbaZBXW{>=YXWPOs65KPImMLEDIBf)(D=a$:?>7<54X210/43,Pqp(',+*#G'gf|B"y?}v^tsr8vuWsrqpi/POkdibgf_%]b[Z_X|?>TYRQu8TMqQPIHGkEJIBA@dD&B;@?8\<5{3W70/.-Q10/on,%Ij('~}|B"!~}_u;y[Zponm3qpoQg-kjihgfeG]#aCBXW{[ZYX:Pt7SRQJONMLEiIHA@?cCB$@9]~<;:921U543,10/.-&J*j('~}${A!~}vuzs9Zponm3qpihmf,jihgfeG]#n

密文很怪,没什么线索。根据题目描述搜了下BenOlmstead,分开搜也没有线索,合起来搜以后了解到Malbolge这个编程语言

然后找个在线网站运行一下malbolge在线运行,在线工具,在线编译IDE_w3cschool

得到结果

再结合一下GPT得知,这是一个残局,这些符号的信息如下:

然后再找个网站分析一下残局,寻找白棋4步绝杀的方法

国际象棋云库查询 (chessdb.cn)

使用的时候把在线计算关了,会快一点。

得到解法之后拼接flag即可

VishwaCTF{Nc7+_Kb8_Na6+_Ka8_Qb8+_Rxb8_Nc7#}

GHCTF

2024九省联考

task.py

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
from Crypto.Util.number import *
from random import *
from flag import flag

m = bytes_to_long(flag)

def getKey():
p = getPrime(1024)
q = getPrime(1024)
n = p*q
assert m < n
_g = randint(1,n)
_a = randint(1,n)
_b = randint(1,n)
return _g,_a,_b,p,n

def encrypt():
key = getKey()
_key = pow(key[0], key[1] * (key[3]-1), key[4])
c = (pow(_key, key[2], key[4]) * m) % key[4]
return key[4], _key, c

if __name__ == '__main__':
print(encrypt())
"""
(18179834236782025892165859358541969039672768622078317899558535972829779066590034272465041741258879770213528640616887797155080398928088158585694106907734126514213361875345507975960317351812797992725951327122851713302327820458710931182497244211627793166445441450354787055708273445176257841426295561697059359851693234672902342001564298222123930677021693824135290857682548806726262690296776413730423712555689778608531366304345258560702517364961150273435113114487840186421965955626002395362990009595593448407759619600184788649602049313701058708439962867646774483596360223764988749726613678029307186833464512585242569435003, 11847655277251446942383940882912368688156808473955941053063218251376227576794849789637061751561030423224338717881358695503734129213448747206540310077968159679087902861554101729530885006978457916005370151390141770528828726044070599163879940393777653050010384591813315879288161460396096268260181418863020260300992421872071273292678599559844166740721617200084689717099280979932276914367828947937478359933181441706805653179826272658999406454166746306193757296165968606398592219586135127604969713571323480832108309240510220649110629512108142189299294329845348146274638537031635599971704959419196082418167981085622698281250, 14415195091596957208690057717270843038813723273773797640804955509618976877138721476691508292484187601673314627535732234517837698631950684940570225175917508902812451340933203438247448893168609525426014319195770233401480816194489198464476480610373345480444974658239730190879678428967798637043230820661470572128997114218312981971051639788050332764374932384836724719743407759997343303062259374222621789562479046933232021478087932233308027796973486804634154183554333108258875955181959785332882009189939917648988868671919046698887438054678239786487480196578700573286352849560893479759745211457149133610748492277912237022560)
"""

$$
\because key \equiv g^{a\times (p-1)} \mod n
$$

可以推出
$$
key \equiv g^{a\times (p-1)} \mod p
$$

$$
key \equiv g^{a\times (p-1)} \mod q
$$

通过费马小定理可以知道
$$
key \equiv g^{a\times (p-1)}\equiv 1 \mod p
$$

$$
\therefore key - 1 = kp
$$

gcd(key-1,p) = p
$$
c \equiv key^b \times m\mod n
$$
同理可推出
$$
c \equiv key^b \times m\mod p
$$

$$
c \equiv key^b \times m\mod q
$$

在mod p下,我们有
$$
c \equiv key^b \times m \equiv 1 \times m \mod p
$$

$$
\therefore m \equiv c \mod p
$$

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Util.number import *
import gmpy2
from tqdm import *

n,y1,y2 = (18179834236782025892165859358541969039672768622078317899558535972829779066590034272465041741258879770213528640616887797155080398928088158585694106907734126514213361875345507975960317351812797992725951327122851713302327820458710931182497244211627793166445441450354787055708273445176257841426295561697059359851693234672902342001564298222123930677021693824135290857682548806726262690296776413730423712555689778608531366304345258560702517364961150273435113114487840186421965955626002395362990009595593448407759619600184788649602049313701058708439962867646774483596360223764988749726613678029307186833464512585242569435003, 11847655277251446942383940882912368688156808473955941053063218251376227576794849789637061751561030423224338717881358695503734129213448747206540310077968159679087902861554101729530885006978457916005370151390141770528828726044070599163879940393777653050010384591813315879288161460396096268260181418863020260300992421872071273292678599559844166740721617200084689717099280979932276914367828947937478359933181441706805653179826272658999406454166746306193757296165968606398592219586135127604969713571323480832108309240510220649110629512108142189299294329845348146274638537031635599971704959419196082418167981085622698281250, 14415195091596957208690057717270843038813723273773797640804955509618976877138721476691508292484187601673314627535732234517837698631950684940570225175917508902812451340933203438247448893168609525426014319195770233401480816194489198464476480610373345480444974658239730190879678428967798637043230820661470572128997114218312981971051639788050332764374932384836724719743407759997343303062259374222621789562479046933232021478087932233308027796973486804634154183554333108258875955181959785332882009189939917648988868671919046698887438054678239786487480196578700573286352849560893479759745211457149133610748492277912237022560)

kp = y1 - 1
p = gmpy2.gcd(kp,n)
q = n // p

m = y2 % p
print(long_to_bytes(m))
# NSSCTF{F3RmM4t's_Li7tLe_The0Rem_1s_S0_Funny!}

2023四省联考

task.py

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
from Crypto.Util.number import *
from secret import flag1, flag2

p = getPrime(256)
a = getPrime(256)
b = getPrime(256)
E = EllipticCurve(GF(p),[a,b])
m = E.random_point()
G = E.random_point()
k = getPrime(256)
K = k * G
r = getPrime(256)
c1 = m + r * K
c2 = r * G
cipher_left = bytes_to_long(flag1) * m[0]
cipher_right = bytes_to_long(flag2) * m[1]

print(f"p = {p}")
print(f"a = {a}")
print(f"b = {b}")
print(f"k = {k}")
print(f"E = {E}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"cipher_left = {cipher_left}")
print(f"cipher_right = {cipher_right}")
"""
p = 89483595401193659714875788802968429205691837099576262739461780495250135533277
a = 64826237317229947176929808786908866900942344000188828024006529547932535008343
b = 78534056584783370803046009116134540449521103461992771640264791547574821609373
k = 67913488585481211548108713418125579002164994747094092682155324503393614823971
E = Elliptic Curve defined by y^2 = x^3 + 64826237317229947176929808786908866900942344000188828024006529547932535008343*x + 78534056584783370803046009116134540449521103461992771640264791547574821609373 over Finite Field of size 89483595401193659714875788802968429205691837099576262739461780495250135533277
c1 = (20170213458584797544543993897296811751081832389059185129461453627621072513038 : 27952719726918348295869968868334378954262663325210512999629622439578788756554 : 1)
c2 = (76989745919392442231791141013473547805494699378782297471380010885452647846211 : 47918018207526659936015900897811268310416123040265940227150505093449710748656 : 1)
cipher_left = 86744083522644883993519202607829403371478208823454264731598682010623698941787
cipher_right = 56539907781612875080666281009932166966795966968474267210348249516329832411261
"""

抓住关键即可
$$
\because c_1 = m + r\times K = m + r\times k \times G
$$

$$
c_2 = r\times G
$$

$$
\therefore m= c_1 - k\times c_2
$$

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Util.number import *
p = 89483595401193659714875788802968429205691837099576262739461780495250135533277
a = 64826237317229947176929808786908866900942344000188828024006529547932535008343
b = 78534056584783370803046009116134540449521103461992771640264791547574821609373
k = 67913488585481211548108713418125579002164994747094092682155324503393614823971
E = EllipticCurve(GF(p),[a,b])
c1 = E(20170213458584797544543993897296811751081832389059185129461453627621072513038,27952719726918348295869968868334378954262663325210512999629622439578788756554)
c2 = E(76989745919392442231791141013473547805494699378782297471380010885452647846211,47918018207526659936015900897811268310416123040265940227150505093449710748656)
cipher_left = 86744083522644883993519202607829403371478208823454264731598682010623698941787
cipher_right = 56539907781612875080666281009932166966795966968474267210348249516329832411261

M = c1 - k*c2
m1 = cipher_left // M[0]
m2 = cipher_right // M[1]
print(long_to_bytes(int(m1)) + long_to_bytes(int(m2)))

# flag{d0_y0u_L1ke_YiXiXi?_dbad24c05a32}

Crypto1921

task.py

1
.---- ...-- .---- .---- -..-. ----- -.... .---- ..... -..-. ----- ...-- ...-- ---.. -..-. ...-- .---- ..--- --... -..-. ....- ....- ...-- -.... -..-. ----- ..--- ...-- ....- -..-. ..--- ..... ----. ---.. -..-. .---- ---.. ----- --... -..-. -.... ....- ..--- ....- -..-. .---- -.... ...-- ...-- -..-. ...-- .---- ..... ----. -..-. ----- ...-- -.... ..--- -..-. ..... --... .---- ....- -..-. ...-- ----. ----. ..--- -..-. ----- .---- ...-- ---.. -..-. ..--- ..... ---.. ----. -..-. --... ....- ..... -.... -..-. ----- ....- ....- .---- -..-. ----- ....- ...-- ...-- -..-. .---- ...-- .---- .---- -..-. -.... .---- ..... ...-- -..-. ----- ....- -.... --... -..-. ----- -.... ...-- --... -..-. ..--- ..--- ...-- ..--- -..-. ..--- -.... ---.. -.... -..-. ----- ----. --... -.... -..-. ..--- ---.. --... .---- -..-. ..--- -.... ...-- ----. -..-. ....- ---.. ....- ..--- -..-. .---- -.... ...-- ...-- -..-. ----- ----- ..... ----. -..-. .---- -.... ...-- ...-- -..-. .---- -.... ..... ...-- -..-. ----- ----- ..... ----. -..-. ----- ...-- -.... ----- -..-. ----- ....- ...-- ...-- -..-. .---- -.... ...-- ...-- -..-. ----- ...-- -.... ..--- -..-. ....- ....- ...-- ..--- -..-. ----- ..... ..... ....- -..-. ....- ---.. ---.. ..... -..-. ----- ----- ----- ..... -..-. .---- -.... ...-- ...-- -..-. .---- -.... ..... ...-- -..-. .---- -.... ...-- ...-- -..-. ----- ----- ----- ..... -..-. ----- ....- ...-- ...-- -..-. ----- ...-- -.... ..--- -..-. ....- ....- ...-- ..---

摩斯解码得到

1
1311/0615/0338/3127/4436/0234/2598/1807/6424/1633/3159/0362/5714/3992/0138/2589/7456/0441/0433/1311/6153/0467/0637/2232/2686/0976/2871/2639/4842/1633/0059/1633/1653/0059/0360/0433/1633/0362/4432/0554/4885/0005/1633/1653/1633/0005/0433/0362/4432

中文电码查询得到

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
1311:子
0615:卿
0338:兄
3127:法
4436:租
0234:借
2598:望
1807:志
6424:路
1633:幺
3159:洞
0362:六
5714:号
3992:疑
0138:似
2589:有
7456:马
0441:列
0433:分
1311:子
6153:请
0467:前
0637:去
2232:探
2686:查
0976:坐
2871:标
2639:东
4842:经
1633:幺
0059:二
1633:幺
1653:度
0059:二
0360:八
0433:分
1633:幺
0362:六
4432:秒
0554:北
4885:纬
0005:三
1633:幺
1653:度
1633:幺
0005:三
0433:分
0362:六
4432:秒
121°28′16″ -> 121.4711111111°
31°13'6" -> 31.2183333333°

压缩包密码即121.471E,31.218N

打开得到信息

1
2
3
4
5
這次會議遭到破壞,恐怕是存在著敵特的監視。請你幫助我們擬一封電報,發送給XXX,內容為:
上海已不安全,請即刻前往嘉興。
(题目背景为架空历史)

压缩包2的密码为发送的电报(摩斯电码形式)的md5,请参考你所截获到的电报进行构建。

上海已不安全,請即刻前往嘉興。进行编码

转为0006/3189/1571/0008/1344/0356/6153/0613/0466/0467/1766/0857/5281

进行摩斯编码得到

1
----- ----- ----- -.... -..-. ...-- .---- ---.. ----. -..-. .---- ..... --... .---- -..-. ----- ----- ----- ---.. -..-. .---- ...-- ....- ....- -..-. ----- ...-- ..... -.... -..-. -.... .---- ..... ...-- -..-. ----- -.... .---- ...-- -..-. ----- ....- -.... -.... -..-. ----- ....- -.... --... -..-. .---- --... -.... -.... -..-. ----- ---.. ..... --... -..-. ..... ..--- ---.. .----

再md5得到43f756680ebbcc619bc7b9a4c69e18ab,即为压缩包密码

flag:NSSCTF{Wh3Re_7herE_1s_OppreSsi0n_Ther3_iS_REs1st4nce!}

Crypto1939

cipher.txt

1
2
3
4
5
6
7
Che-chil-be-tah-ola
Atcah
Con-nil-kol-lih
Tabaha
Yo-ih

密码为全小写,各个单词之间用_连接

密文是Navajo

网上找码表即可

1
2
3
4
5
Che-chil-be-tah-ola				-> 		major
Atcah -> 应是Astah ->Transport Plane
Con-nil-kol-lih -> 应是Coh-nil-kol-lih ->Blinker
Tabaha -> regiment
Yo-ih -> section

压缩包密码为major_transport_blinker_regiment_section

NSSCTF{W0Rds_SomeTimes_N3ec1_L1vEs_To_DeFend!}

Crypto2042

给出c = 2280855825153079369164514405327170396436620371664464526496429415733055203155893358484242996086505609777681080427906338470140692619928813296029361848636902023401977358243759630394370381376771421271028169492126347929997983519619222735581769019133156193638407197605823449074124544232663174637158818370904094998560600412703805652546502801423946150024740398302185572802694514470006408627417645654400501165391976205173956917696904810606259304153658230235808250378495226246727449749944446621780268923502478344984402162063011322533642498211989023200258638304752854056875616723925602248635708248865548095231743243612632966212

以及破损私钥文件

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
-----BEGIN BREAK PEM PRIVATE-----
MIIEowIBAAKCAQEAw6JUixKmoIZjLyR1Qc/D/3mfTC3YvqKienLM7Nt/83UqpYeg
9rOw02xLtIqgBdVyVkI+MknQdB5tB1W/bo95M8JjmNxi5rcEzXK4A5HiKtNxK9hC
dE1GIt78XcT1FLoM86zxFonkDQ8LxHXPbU22+Ex8ov269pS/BEwZuKohqbIUU4iZ
UltNYW2kaVsavAZw4NyQcQCQoijprdrQZLV6XjC/3+uX6baLaZS+NWHm1M16UHde
IF9B1gNuifO0xnuAWxRENjBIXw/3W8OVvRuRUppY2WYgeEG31fgfOCDoXhWVbqAD
6fJA9JWdkOPDqUAsqFCr2VKnORa2X9V9HdO5lwIDAQABAoIBAQD/////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////AoGBAP//////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////AoGBAP//
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////AoGAZFBey/5KFQ1nOL6xe00r
ehGXxxNQF/7oym0kzI6BBi4awYZ4FCtuGiiAfkcsOS0UhlhFn1gEemnl2f2EF78M
THN5zVM5FSY5Cf8XwJT/fEytuWdCQke7JxVYOLNsEJeViicsasXeWeSL1nPYS3a5
+T4027LUdjikIbu508dTT6UCgYAY+t0pv5nJ93J3773ZZYkLSCG+zXZBSgUtcCoQ
GTBa4Qcr3MLeeUGDkb4qNIBdqWsQR0GiIXLkoIxcmOr66Rs+Hl60MD0kWQA6uz5/
aJOhZc8pO0vGVG3+RbKA4xTxZshopp4CVdCrM20SqXXa3OEqr48Ec1aIES/ymZO8
GuaOwQKBgB4ZWvOfPzCFOselDYyjz2Tub1RY3YrvI1do3jl42nhy/1Aoc6G3XLi4
AIg8gxJGx36aPjpxBfe1HTicBhtUThBxq/A2/Ed1TwxFDvDPjhDc1Js/LL0qxhaP
Z/xYCgGGZftjQqPTy+S62ppYVcTxjDON6vNBssatGUIzuGglC2Lf
-----END BREAK PEM PRIVATE-----

能提取出n,e,dp,dq,inv,关于提取数据可以参考NSSCTF Round16 | DexterJie’Blog

然后用dp泄露的思路进行求解

Exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
import gmpy2

c = 2280855825153079369164514405327170396436620371664464526496429415733055203155893358484242996086505609777681080427906338470140692619928813296029361848636902023401977358243759630394370381376771421271028169492126347929997983519619222735581769019133156193638407197605823449074124544232663174637158818370904094998560600412703805652546502801423946150024740398302185572802694514470006408627417645654400501165391976205173956917696904810606259304153658230235808250378495226246727449749944446621780268923502478344984402162063011322533642498211989023200258638304752854056875616723925602248635708248865548095231743243612632966212
n = int("c3 a2 54 8b 12 a6 a0 86 63 2f 24 75 41 cf c3 ff 79 9f 4c 2d d8 be a2 a2 7a 72 cc ec db 7f f3 75 2a a5 87 a0 f6 b3 b0 d3 6c 4b b4 8a a0 05 d5 72 56 42 3e 32 49 d0 74 1e 6d 07 55 bf 6e 8f 79 33 c2 63 98 dc 62 e6 b7 04 cd 72 b8 03 91 e2 2a d3 71 2b d8 42 74 4d 46 22 de fc 5d c4 f5 14 ba 0c f3 ac f1 16 89 e4 0d 0f 0b c4 75 cf 6d 4d b6 f8 4c 7c a2 fd ba f6 94 bf 04 4c 19 b8 aa 21 a9 b2 14 53 88 99 52 5b 4d 61 6d a4 69 5b 1a bc 06 70 e0 dc 90 71 00 90 a2 28 e9 ad da d0 64 b5 7a 5e 30 bf df eb 97 e9 b6 8b 69 94 be 35 61 e6 d4 cd 7a 50 77 5e 20 5f 41 d6 03 6e 89 f3 b4 c6 7b 80 5b 14 44 36 30 48 5f 0f f7 5b c3 95 bd 1b 91 52 9a 58 d9 66 20 78 41 b7 d5 f8 1f 38 20 e8 5e 15 95 6e a0 03 e9 f2 40 f4 95 9d 90 e3 c3 a9 40 2c a8 50 ab d9 52 a7 39 16 b6 5f d5 7d 1d d3 b9 97".replace(" ",""),16)
e = 65537
dp = int("64 50 5e cb fe 4a 15 0d 67 38 be b1 7b 4d 2b 7a 11 97 c7 13 50 17 fe e8 ca 6d 24 cc 8e 81 06 2e 1a c1 86 78 14 2b 6e 1a 28 80 7e 47 2c 39 2d 14 86 58 45 9f 58 04 7a 69 e5 d9 fd 84 17 bf 0c 4c 73 79 cd 53 39 15 26 39 09 ff 17 c0 94 ff 7c 4c ad b9 67 42 42 47 bb 27 15 58 38 b3 6c 10 97 95 8a 27 2c 6a c5 de 59 e4 8b d6 73 d8 4b 76 b9 f9 3e 34 db b2 d4 76 38 a4 21 bb b9 d3 c7 53 4f a5".replace(" ",""),16)
dq = int("18 fa dd 29 bf 99 c9 f7 72 77 ef bd d9 65 89 0b 48 21 be cd 76 41 4a 05 2d 70 2a 10 19 30 5a e1 07 2b dc c2 de 79 41 83 91 be 2a 34 80 5d a9 6b 10 47 41 a2 21 72 e4 a0 8c 5c 98 ea fa e9 1b 3e 1e 5e b4 30 3d 24 59 00 3a bb 3e 7f 68 93 a1 65 cf 29 3b 4b c6 54 6d fe 45 b2 80 e3 14 f1 66 c8 68 a6 9e 02 55 d0 ab 33 6d 12 a9 75 da dc e1 2a af 8f 04 73 56 88 11 2f f2 99 93 bc 1a e6 8e c1".replace(" ",""),16)
inv = int("1e 19 5a f3 9f 3f 30 85 3a c7 a5 0d 8c a3 cf 64 ee 6f 54 58 dd 8a ef 23 57 68 de 39 78 da 78 72 ff 50 28 73 a1 b7 5c b8 b8 00 88 3c 83 12 46 c7 7e 9a 3e 3a 71 05 f7 b5 1d 38 9c 06 1b 54 4e 10 71 ab f0 36 fc 47 75 4f 0c 45 0e f0 cf 8e 10 dc d4 9b 3f 2c bd 2a c6 16 8f 67 fc 58 0a 01 86 65 fb 63 42 a3 d3 cb e4 ba da 9a 58 55 c4 f1 8c 33 8d ea f3 41 b2 c6 ad 19 42 33 b8 68 25 0b 62 df".replace(" ",""),16)

def dp_leak(dp,c,n,e):
for i in range(1,e):
t = (dp * e - 1) % i
if t == 0:
p = (dp * e - 1) // i + 1
if n % p == 0:
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)))

dp_leak(dp,c,n,e)
# NSSCTF{(115.36517E, 39.30839N)}

osu

第一道题

task.py

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
from Crypto.Util.number import isPrime,bytes_to_long
import random
import os

def getWYSIprime():
while True:
digits = [random.choice("727") for _ in range(272)]
prime = int("".join(digits))
if isPrime(prime):
return prime

# RSA encryption using the WYSI primes
p = getWYSIprime()
q = getWYSIprime()
n = p * q
e = 65537
flag = bytes_to_long(os.getenv("FLAG",b"osu{fake_flag_for_testing}"))
ciphertext = pow(flag,e,n)
print(f"n = {n}")
print(f"e = {e}")
print(f"ciphertext = {ciphertext}")
"""
n = 2160489795493918825870689458820648828073650907916827108594219132976202835249425984494778310568338106260399032800745421512005980632641226298431130513637640125399673697368934008374907832728004469350033174207285393191694692228748281256956917290437627249889472471749973975591415828107248775449619403563269856991145789325659736854030396401772371148983463743700921913930643887223704115714270634525795771407138067936125866995910432010323584269926871467482064993332990516534083898654487467161183876470821163254662352951613205371404232685831299594035879
e = 65537
ciphertext = 2087465275374927411696643073934443161977332564784688452208874207586196343901447373283939960111955963073429256266959192725814591103495590654238320816453299972810032321690243148092328690893438620034168359613530005646388116690482999620292746246472545500537029353066218068261278475470490922381998208396008297649151265515949490058859271855915806534872788601506545082508028917211992107642670108678400276555889198472686479168292281830557272701569298806067439923555717602352224216701010790924698838402522493324695403237985441044135894549709670322380450
"""

从低位往高位爆破即可,因为题目生成的素数只含7,2,所以每次爆破只需要2个数字,爆破起来蛮快的

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
from Crypto.Util.number import *
import itertools
import gmpy2

n = 2160489795493918825870689458820648828073650907916827108594219132976202835249425984494778310568338106260399032800745421512005980632641226298431130513637640125399673697368934008374907832728004469350033174207285393191694692228748281256956917290437627249889472471749973975591415828107248775449619403563269856991145789325659736854030396401772371148983463743700921913930643887223704115714270634525795771407138067936125866995910432010323584269926871467482064993332990516534083898654487467161183876470821163254662352951613205371404232685831299594035879
e = 65537
ciphertext = 2087465275374927411696643073934443161977332564784688452208874207586196343901447373283939960111955963073429256266959192725814591103495590654238320816453299972810032321690243148092328690893438620034168359613530005646388116690482999620292746246472545500537029353066218068261278475470490922381998208396008297649151265515949490058859271855915806534872788601506545082508028917211992107642670108678400276555889198472686479168292281830557272701569298806067439923555717602352224216701010790924698838402522493324695403237985441044135894549709670322380450

def findp(p,q):
l = len(p)
if l == 272 and n % int(p) == 0:
q = n // int(p)
print(f"p = {p}")
print(f"q = {q}")
else:
table = ["2","7"]
for i,j in itertools.product(table,repeat=2):
pp = int(i + p)
qq = int(j + q)
if pp * qq % 10**l == n % 10**l:
findp(i+p,j+q)

# findp("7","7")
p = 27777727727777722777277277777272772727772722777777277777277772227772772772227272727777772772772727227772777277727777777222777777277727772272272777772722727722777727277727727777777772772777772777277772222727227777777222727777772772272727777222777777277777727272772272272277
q = 77777772777772222722727222227777272777772777772277727772722777777777227277727272772727277277272772727227272772772222277777772727727772722772777272727777272722772777277777277777277777727777277277777277777272772772777777727772727277727777772772777772272772272222227777777227
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(ciphertext,d,n)
print(long_to_bytes(m))
# osu{h4v3_y0u_3v3r_n0t1c3d_th4t_727_1s_pr1m3?}

第二道题

task.py

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
from Crypto.Util.number import getPrime # https://pypi.org/project/pycryptodome/
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from random import randrange
from math import floor

def lcg(s, a, b, p):
return (a * s + b) % p

p = getPrime(floor(72.7))
a = randrange(0, p)
b = randrange(0, p)
seed = randrange(0, p)
print(f"{p = }")
print(f"{a = }")
print(f"{b = }")

def get_roll():
global seed
seed = lcg(seed, a, b, p)
return seed % 100
out = []
for _ in range(floor(72.7)):
out.append(get_roll())
print(f"{out = }")

flag = open("flag.txt", "rb").read()
key = bytes([get_roll() for _ in range(16)])
iv = bytes([get_roll() for _ in range(16)])
cipher = AES.new(key, AES.MODE_CBC, iv)
print(cipher.encrypt(pad(flag, 16)).hex())

'''
p = 4420073644184861649599
a = 1144993629389611207194
b = 3504184699413397958941
out = [39, 47, 95, 1, 77, 89, 77, 70, 99, 23, 44, 38, 87, 34, 99, 42, 10, 67, 24, 3, 2, 80, 26, 87, 91, 86, 1, 71, 59, 97, 69, 31, 17, 91, 73, 78, 43, 18, 15, 46, 22, 68, 98, 60, 98, 17, 53, 13, 6, 13, 19, 50, 73, 44, 7, 44, 3, 5, 80, 26, 10, 55, 27, 47, 72, 80, 53, 2, 40, 64, 55, 6]
enc = '34daaa9f7773d7ea4d5f96ef3dab1bbf5584ecec9f0542bbee0c92130721d925f40b175e50587196874e14332460257b'
'''

由$X_{n+1} \equiv aX_n + b \mod p$

拆成高低位来写,则有
$$
H_{n+1}\times 100 + L_{n+1} \equiv a(H_n\times 100 + L_n) +b \mod p
$$
化简得
$$
H_{n+1} \equiv aH_n + (aL_n + b -L_{n+1})\times 100^{-1} \mod p
$$
于是有
$$
H_2 \equiv aH_1 + (aL_1 + b - L_2)\times 100^{-1} \mod p
$$

$$
H_3 \equiv aH_2 + (aL_2+b-L_3) \times 100^{-1} \mod p
$$


$$
H_3 \equiv a^2H_1 + (a(aL_1+b-L_2) + (aL_2+b-L_3)) \times 100^{-1} \mod p
$$
写到这里是为了后面更好理解$B_i$如何计算

回到题目,简单来写就是
$$
H_{n+1} \equiv A_nH_1 + B_n \mod p
$$
即$H_{n+1} = A_mH_1 + B_n +k_np$

构造格

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
from Crypto.Cipher import AES
import gmpy2

p = 4420073644184861649599
a = 1144993629389611207194
b = 3504184699413397958941
out = [39, 47, 95, 1, 77, 89, 77, 70, 99, 23, 44, 38, 87, 34, 99, 42, 10, 67, 24, 3, 2, 80, 26, 87, 91, 86, 1, 71, 59, 97, 69, 31, 17, 91, 73, 78, 43, 18, 15, 46, 22, 68, 98, 60, 98, 17, 53, 13, 6, 13, 19, 50, 73, 44, 7, 44, 3, 5, 80, 26, 10, 55, 27, 47, 72, 80, 53, 2, 40, 64, 55, 6]
enc = '34daaa9f7773d7ea4d5f96ef3dab1bbf5584ecec9f0542bbee0c92130721d925f40b175e50587196874e14332460257b'

L = [0] + out
n = len(out)
A = [1]
B = [0]
for i in range(1,n):
A.append(a*A[i-1] % p)
B.append((a*B[i-1] + (a*L[i]+b-L[i+1])*gmpy2.invert(100,p))%p)

A = A[1:]
B = B[1:]

Ge = Matrix(ZZ,n+1,n+1)
for i in range(len(A)):
Ge[i,i] = p
Ge[-2,i] = A[i]
Ge[-1,i] = B[i]

Ge[-2,-2] = 1
Ge[-1,-1] = p // 100

M = Ge.LLL()[0]
H1 = M[-2]
L1 = out[0]
seed1 = H1 * 100 + L1

seed = ((seed1 - b)*gmpy2.invert(a,p))%p
# print(seed)
# 1972400400839421538435
def lcg(s, a, b, p):
return (a * s + b) % p

def get_roll():
global seed
seed = lcg(seed, a, b, p)
return seed % 100

temp = []
for _ in range(floor(72.7)):
temp.append(get_roll())
# print(temp == out)
# True
key = bytes([get_roll() for _ in range(16)])
iv = bytes([get_roll() for _ in range(16)])
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = cipher.decrypt(bytes.fromhex(enc))
print(flag)
# osu{w0uld_y0u_l1k3_f1r5t_0r_53c0nd_p1ck}

pearlCTF

3 pies

task.py

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
#!/usr/bin/env python3

from Crypto.Util.number import getPrime, bytes_to_long

with open('flag.txt', 'rb') as f:
flag = f.read()


n1 = getPrime(512)*getPrime(512)
n2 = getPrime(512)*getPrime(512)
n3 = getPrime(512)*getPrime(512)

e=3

m = bytes_to_long(flag)

c1 = pow(m,e,n1)
c2 = pow(m,e,n2)
c3 = pow(m,e,n3)

with open('encrypted-messages.txt', 'w') as f:
f.write(f'n1: {n1}\n')
f.write(f'e: {e}\n')
f.write(f'c1: {c1}\n\n')
f.write(f'n2: {n2}\n')
f.write(f'e: {e}\n')
f.write(f'c2: {c2}\n\n')
f.write(f'n3: {n3}\n')
f.write(f'e: {e}\n')
f.write(f'c3: {c3}\n')

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
import gmpy2


n1 = 125267411676839013904356880992044234494446196964982422223130579882047339346910691451497681975351838034684254305738613386927222900898672184001345811471784343779083336010063097729870079645284178978512325038316112509718505547104307526489798594871208559607331790920412305711830820739308995357441030646151241475357
e = 3
c1 = 53377681151597930200174280269480737905892580547675095951568028531545776989476273786562435486230550919422086944133253611872983670236114054374565938184593173194919064517779661178744278071496565181181705071524501841159717567250259220092464925447795412484629687708208662079791459184303259833667333882817260906165

n2 = 101985110329687359982214188967281711679876126442294375297547334583432698756724057183438691227371260175904715854057793173086301783390154807726779286131084537704721881438398569476214173211311977143694032174701007005033830070482491565424683664984059187439768982994371382763048098663670188786016786612348042190633
e = 3
c2 = 86370003324603283962938004647941072863866893771153362222202759619566185050496089684606274416415418388916028237984708280964054009059814813483639010674182298294505525549842057730933691736372086557397211586739691237738757897947336698446258197604918828646265244195686107866422922575275382813594250335044143485624

n3 = 83259448903366278561128205003734328779222118906091604625605804813528274055482582431201682767294594942491788720967344243567819654813240542076250030802111361571504667752481579915864184180358691091092122509649590043074189547962292835856503625214027405901620103615424259796442446412031011575671410630232956892267
e = 3
c3 = 25601241268900087228853235319569275926328919786631787991019848828558430219449358810095537362492238844266084660904521793373698736119824512458196492049138821633273765102576368573691391116632126183996786969554104441242376959688329346567745607825277943462236901478944551669406261301309719409165457168678763092118

C = crt([n1,n2,n3],[c1,c2,c3])[0]

m = gmpy2.iroot(C,3)[0]
print(long_to_bytes(m))
# This is your destination: "https://pastes.io/1yjswxlvl2

进入这个网站得到一串信息:

1
2
You think this is Gibbrish, but it's something different...
/9j/4AAQSkZJRgABAQAAAQABAAD/4QECRXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAsAAAABsBBQABAAAAuAAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAVgAAAAAAAAAHAACQBwAEAAAAMDIzMQGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgAwABAAAA9AEAAAOgAwABAAAA9AEAAIaSBwA5AAAAwAAAAAAAAABgAAAAAQAAAGAAAAABAAAAQVNDSUkAAAB4cjpkOkRBRi1vcWVMV1pVOjUsajo5OTQ5OTkwMzAwOTcxNzQzOSx0OjI0MDMwNTEwAP/hBPBodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADx4OnhtcG1ldGEgeG1sbnM6eD0nYWRvYmU6bnM6bWV0YS8nPgogICAgICAgIDxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogICAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgICAgICAgeG1sbnM6ZGM9J2h0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICAgICAgICA8ZGM6dGl0bGU+CiAgICAgICAgPHJkZjpBbHQ+CiAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz5mbGFnX2l0X2lzIC0gMTwvcmRmOmxpPgogICAgICAgIDwvcmRmOkFsdD4KICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgoKICAgICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogICAgICAgIHhtbG5zOkF0dHJpYj0naHR0cDovL25zLmF0dHJpYnV0aW9uLmNvbS9hZHMvMS4wLyc+CiAgICAgICAgPEF0dHJpYjpBZHM+CiAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSdSZXNvdXJjZSc+CiAgICAgICAgPEF0dHJpYjpDcmVhdGVkPjIwMjQtMDMtMDU8L0F0dHJpYjpDcmVhdGVkPgogICAgICAgIDxBdHRyaWI6RXh0SWQ+YjY3YWFiZTgtNDQxMS00NmY1LWJlODQtNWM0OTk4YmYwMTg3PC9BdHRyaWI6RXh0SWQ+CiAgICAgICAgPEF0dHJpYjpGYklkPjUyNTI2NTkxNDE3OTU4MDwvQXR0cmliOkZiSWQ+CiAgICAgICAgPEF0dHJpYjpUb3VjaFR5cGU+MjwvQXR0cmliOlRvdWNoVHlwZT4KICAgICAgICA8L3JkZjpsaT4KICAgICAgICA8L3JkZjpTZXE+CiAgICAgICAgPC9BdHRyaWI6QWRzPgogICAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgoKICAgICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogICAgICAgIHhtbG5zOnBkZj0naHR0cDovL25zLmFkb2JlLmNvbS9wZGYvMS4zLyc+CiAgICAgICAgPHBkZjpBdXRob3I+QWRhcnNoIFBhcmloYXI8L3BkZjpBdXRob3I+CiAgICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CgogICAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgICAgICAgeG1sbnM6eG1wPSdodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvJz4KICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkNhbnZhPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgICAgCiAgICAgICAgPC9yZGY6UkRGPgogICAgICAgIDwveDp4bXBtZXRhPv/bAEMABgQFBgUEBgYFBgcHBggKEAoKCQkKFA4PDBAXFBgYFxQWFhodJR8aGyMcFhYgLCAjJicpKikZHy0wLSgwJSgpKP/bAEMBBwcHCggKEwoKEygaFhooKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKP/AABEIAfQB9AMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APf6KMUYpGIUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUYoxQAUUUUALRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAlFFFAC0UUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAJRRRQAtFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACUUUUALRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFc38QPFlr4L8MzaxexPOiSJEsSMFZ2Y9AT7An8K6Svmr9q3xCZ9V0nw9A/wC6tUN3OB3kfhB+Cgn/AIFTSuweh6h4W+Mng7X2SI6j/Zty3Aivx5QJ9A/3f1r0NHWRVeN1dGGQynII9iOtfniqM7KiKWZjgKO5Pavu74d+H18LeCtH0f8A5a28C+cfWVvmf/x4kfhTkrCTudFRRRUjCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooASiiigBaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigBGZVUs7BEUEsx6KO5NfB/j/Xz4n8Zatq+T5VzOxhB7RjhB/3yBX1h8d9f/sH4a6o0b7Li9AsoiDz8/DY/4CGr4vA4q4LqTJnofwE8Nf8ACUfE/SrWRC1tak3k/HAVMEZ+rFRX2lcQtBKUfr1B9a8d/Y+8NC08N6r4gmjxNfSi3iY/884+uPqxP/fIr369tVuI8Hhh90+lKWrNIx905+ilkRo3KOMMO1JUkhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAJRRRQAtFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRTJpY4IZJp3EcMal5HPRVAyT+ABoA+Y/wBqjxGb3xJp2gQt+40+Lz5cd5pOgP0QD/vo14jbwS3NxFBboZJ5XEcaDqzE4A/M1qeMNafxH4o1PV5M/wCl3DSKD2XPyj8Biu5/Zv8ADn/CQ/FLT2kTdbacpvpcjj5cBB/30R+Va7Indn2T4B8PR+FPB2kaJEQfsVukbsP43xl2/FiTXQUCiszoKl9aLcJkcSDof6VhSI0blXGGFdRVW9tFuE9HHRqCXG5gUU+aJ4XKyDB/nTKRmFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUhoAXNJmiigAooooAWiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArzP9oXX/wCxPhrfQRvtuNSYWS467W5f/wAdBH416ZXyt+1F4i/tHxla6LA+YNLh/eY6GaTDH8l2D86cVdgzxevr/wDZE8NDTvBd9rkyYuNUn2ox6+VHkD82LH8q+R7CzuNRv7ays0Ml1cyrDEg/idiAB+Zr9G/CGiQ+G/DOmaPbY8qyt0hB/vEDk/icn8aufYKa6mxRRRUGwUUUUARXEEc6bZFyPXuKxbuykg5Hzp6jt9a36QjIoE1c5alrautOjlOY/kb26Gsy4s5oOWTK+q80iGiCiiigkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKSlpKACiiigAooooAWiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigCC+uorGyuLu5YJBbxNNIx7KoLE/kDXwN4i1WXXNe1DVJyfMvJ3mOe2TkD8BgV9VftJeIDpHw6lsYX23GqyrbcdfLHzP8AmAB/wKvkStILqTI9d/Zg8O/258T7e7kTdb6VEbtiRwG+6n6kn/gNfbo6V4f+yb4ZXSPh9Lq00eLrV5zLuI58lPlQfnvP417hUyd2bQVkFFFFIoKKKKACiiigAxRiiigCtPZQTZLJhvVeKzptMkXmNg49Dwa2qKBNJnLyI0bbXUqfQ0ldNJGrrh1DD3FUZ9MjfmIlD+YpEuJj0VZmsZ4v4dw9V5qt0POaCAooooAKKKKACiiigAooooAKKKKACiiigAooooAKQ0tIaACiiigAooooAWiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiqesahFpWk3uoXLBYLSF53J9FBP8ASgD5Y/aZ8RDVvHq6XA2bfSIhC3oZmwzn8AVX8DXlujadcaxq9lptiu66vJkgiH+0zAD+dJq1/Nquq3moXJzNdTPM592JJ/nXr37Kfh3+1viM2pypug0mAzAnoJHyq/puP4VrsiUryPsDw7pUGh6HYaXaDFvaQJCnHUKMZrRoHSiszoCiiigAooooAKKKKACiiigAooooAKMUUUAGKhntoph+8QE+vepqKAMqfS+8L/g3+NZ00TwvskGDXSOQqknoK5u4lM07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F1GzucTMRmGA8gYByxHrjJ71d8beH7rXbnw1JaSQIumatFfzeaxG6NUkUhcA5bLjrgdeaB3LPiS41HSoWuvD3h5NWvZ2AmVbiO2YgD5SzN19Paub+DDwNo2tEmSLV5dVnudTtJI/LNtcPgmMDJyoULhsndyfYWdR07xppevajd+HLrTtT06+ZZfsmqTSo1q4UKRGyhhsOM7SBg9O9R+GdOufBmn65rviaRr7VdUuluLlNLtpJVTgIkcaAFmAHcj69M0CKvwTvfL8OPoX2a5Y6ZcXEf24Jm1uMzuf3Un8WM4PHBBr0euP+EWnXmlfDvSLXUreS2ugJZXhkGGTfK7gMOxwwyK7CgGFFFFAhKKKKACiiigAooooApaTpdlpMEsOnW6wRSyvO6qTy7nLNz6nmrtFFABRRRQAUUUUAFZ+uaNp2vadJYazZw3lo5BMUq5GR0I9D7jmtCigDl9G8A+GNG1CO+sNJjW8iBEc0sjzNHn+6XY7fwxXUUUUAFFFFABRRRQAVh+IPCmieIrm1n1uwS8e1OYhIzbByDyoO1uQOoPStyigAooooAUUUUZoAKKM0ZoAKKM0ZoAKKM0ZoAKKM0ZoAKKM0UAFFGaM0AFFGaM0AFFGaM0AFFGaM0AFFGaM0AFFGaM0AFFGaM0AFMniSeCSGVd0cilGGcZBGDT80ZoAwvDXhLQ/DLTvomnx20k+BLJuZ3cDoCzEnA9M4rdozRmgAoozRmgAoozRmgAoozRmgAoozRmgAoozRmgBKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z

放入CyberChef

Baby’s Message Out

task.py

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
#!/usr/bin/env python3
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
from flag import FLAG
from math import log
import sys

n = pow(10, 5)
sys.setrecursionlimit(n)

def nextPrime(p):
if isPrime(p):
return p
else:
return nextPrime(p + 61)

p = getPrime(256)
q = nextPrime(nextPrime(17*p + 1) + 3)
r = nextPrime(29*p*q)
s = nextPrime(q*r + p)
t = nextPrime(r*s*q)

n = p*q*r*s*t
e = 65537
m = bytes_to_long(FLAG.encode())
c = pow(m, e, n)
print(f"c: {c}")
print(f"n: {n}")

将$q,r,s,t$暂且设为

$q = 17p+1,r = 29p\times q,s = q\times r + p,t = r\times s\times q$

可以得到
$$
n = 4933820698627921p^{13} + 2321797975824904p^{12} + 479194612657574p^{11} + 56652867922228p^{10} + 4196277199301p^9 + 199402260580p^8 + 5936228776p^7 + 101222760p^6 + 756900p^5
$$
把n的系数化为1,然后开13次方,可以得到一个在实际的p附近波动的值,然后再进行爆破即可恢复p

爆破范围的选定可以自己生成数据进行测试。

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
from Crypto.Util.number import *
import gmpy2
from tqdm import *
import sys

sys.setrecursionlimit(pow(10,5))

def nextPrime(p):
if isPrime(p):
return p
else:
return nextPrime(p + 61)

c = 1838979268925267202615818395622122484893202706167722156383681963142079180161531237936585341507802530813847301831886857107840552940103096671213346624172927405178565892385733216702672851783423891229382579221604596673156151519463795808010572632138076246765227545034269340790242411584582692558549837221897037971538915332866933837117883228953143242965510280921618063635225603250391037969273250144174802927111408106890496901519737395485115788436384012937788617630381389196078818501905993385084557887716148781944416313908553565974546239241287340505018036547080504009483360838747251073440799502791555942879229132369697126614944991347408598924472158561247485575276852356316968119522601927483600978106155875300324966339458896992260918634629607740522754923275555209240690069838566642421234035117257380757518902549668151860260985363114235340791362277047923494577018035035653075155240898800910887230020812289524262582877697129108304758256250595391379019020972909027192107727799235384374670082518555887502478560056865020860127002
n = 485889862688353809145356849681869098056742135790325154633816315764783289109350404808839464043297429168130293002483316664607770068196944445394490388957147529192164743228508084885030734914365245916125607677185307000076761646830755541107131137865984020648243428286711853728051263128076845261298240617545417811431124904304446607845470628505361436115651109768237103000946120781642652665829238385087624612205525857191218811570122228639908283598707567056620352822139985271817003262919597858495812801663331842855143930997258260164422858382713240717739568744168349527025412634588705503126870149875268600539407786323924132037483972529603572727892740460450963315253482630345996561505957763886776070413535287129043703855880444326909520084249767518737040222995586717034060176093473694948961503759770499124924886087468497100053775745634680192678426965540847613838559522315570932873854027804486662281273550134766570419630624914801192395060568099367828616501877701994364906741451132770474154689411119296104818089636891131318495955929

T = n // 4933820698627921
temp = gmpy2.iroot(T,13)[0]

for i in trange(10000):
p = temp - i
if isPrime(p) and n % p == 0:
print(i)
q = nextPrime(nextPrime(17*p + 1) + 3)
r = nextPrime(29*p*q)
s = nextPrime(q*r + p)
t = nextPrime(r*s*q)
phi = (p-1)*(q-1)*(r-1)*(s-1)*(t-1)
e = 65537
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
flag = long_to_bytes(m)
if b"pearl" in flag:
print(flag)
break
# pearl{7h1s_0n3_w4s_f0r_7h3_k1ds}

Bit War

task.c

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
#include <stdio.h>
#include <stdlib.h>
int main(){
char str[100];
int length = 8;
int rounds = 1000;
char * end;
unsigned int buffer = 0;
unsigned int bufferMask = 0xff;
unsigned int leftMask = 0x80; //10000000
unsigned int rightMask = 0x1; //00000001
while(scanf("%s", str)){
buffer = strtol(str, &end, 2);
unsigned int input = buffer;
for(int i = 0; i < rounds; i++){
unsigned int new = ((buffer & leftMask) >> 7) ^ (buffer & rightMask);
buffer = ((buffer << 1) & bufferMask) ^ new;
}
int inputBits[length];
int bufferBits[length];
int i = length;
while(i){
if(input & 0x1) inputBits[i-1] = 1;
else inputBits[i-1] = 0;
if(buffer & 0x1) bufferBits[i-1] = 1;
else bufferBits[i-1] = 0;
input = input >> 1;
buffer = buffer >> 1;
i--;
}
for(int i = 0; i < length; i++){
printf("%d", bufferBits[i]);
}
printf(" ");
}
}

output

1
output:01001000 11010111 11010001 11001011 01011010 01000110 11011101 01001110 01001010 01110000 11010001 01110000 01011010 11010101 01001010 11001011 11000011

代码实现的是一个LFSR的作用,至于具体过程,我不太说的明白,值得注意的是,我们输入的数据得是2进制。

不过题目给出output,根据output的长度可以判断flag的长度并不长,于是想到了爆破的方法

exp.py

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

def lfsr(m):
length = 8
rounds = 1000
bufferMask = 0xff
leftMask = 0x80
rightMask = 0x1

while True:
buffer = int(m, 2)
input_val = buffer

for i in range(rounds):
new = ((buffer & leftMask) >> 7) ^ (buffer & rightMask)
buffer = ((buffer << 1) & bufferMask) ^ new

inputBits = []
bufferBits = []

for i in range(length):
inputBits.append(input_val & 0x1)
bufferBits.append(buffer & 0x1)
input_val >>= 1
buffer >>= 1

bufferBits.reverse()
c = ""
for Bit in bufferBits:
c += str(Bit)
return c

output = "01001000 11010111 11010001 11001011 01011010 01000110 11011101 01001110 01001010 01110000 11010001 01110000 01011010 11010101 01001010 11001011 11000011"
c = output.split(" ")

flag = ""
for i in trange(len(c)):
for j in range(32,128):
cc = lfsr(bin(j)[2:])
if cc == c[i]:
flag += chr(j)
print(flag)
# pearl{its_a_lfsr}

syntelestis

task.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import getPrime, inverse
from random import randint
from secret import flag

p = getPrime(256)
print(f"p = {p}")
k = list()
flag = flag.hex().encode()
for i in range(len(flag) - 20):
g = []
h = []
for j in range(0, len(flag), 2):
a, b = flag[j], flag[j + 1]
m, n = randint(0, p - 1), randint(0, p - 1)
c, d = m * a, n * b
e, f = pow(inverse(c, p) + inverse(d, p), 2, p), (m ** 2 * inverse(c, p) * n ** 2 * inverse(d, p)) % p
g += [m, n]
h.append(e * inverse(f, p) % p)
g.append(sum(h) % p)
k.append(g)

print(f"k = {k}")

首先进行推导
$$
e \equiv (c^{-1}+d^{-1})^2 \mod p
$$

$$
f \equiv m^2c^{-1}n^2d^{-1} \mod p
$$

$$
f^{-1} \equiv m^{-2}cn^{-2}d \mod p
$$

$$
h \equiv ef^{-1} \equiv (c^{-2}+d^{-2}+2c^{-1}d^{-1})(m^{-2}cn^{-2}d) \equiv m^{-2}c^{-1}n^{-2}d + m^{-2}cn^{-2}d^{-1} +2m^{-2}n^{-2}
$$

把c,d用a,b表示,即可得到h关于a,b的式子
$$
h \equiv m^{-3}a^{-1}n^{-1}b + m^{-1}an^{-3}b^{-1} + 2m^{-2}n^{-2} \mod p
$$
再写成g和a,b的式子

题目一共给出54组这样的数据,于是想到用格来做,不过这里存在$a^{-1},b^{-1}$,比较麻烦。

在鸡块师傅的指导下,我们引入$t = ord(‘0’)\times ord(‘1’)\times … \times ord(‘f’)$,然后两边同时乘上t可以得到

首先尝试用一组数据构造格:

结果不出所料,没有规约成功

进行优化,把$\sum_{i=1}^{37}2m_i^{-2}n_i^{-2}t$记为一个变量R,此时格为

不过还是没有规约成功,把所有数据利用上,此时格为

利用这个格便可规约成功。有个值得注意的点是,最后两行的$R_i$和$g_i$可以合并为1个变量,这样可以把格的规模从$130\times 130$变为$129\times 129$

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
#sage
from gmpy2 import invert

p = 112484851204745228541474305508055656664633080619278978956579531639763617075417
k = [[...]]

t = 1
for i in range(10):
t *= ord('0')+i
for i in range(6):
t *= ord('a')+i

n = 54 + 76
Ge = Matrix(ZZ,n,n)
for i in range(54):
g = k[i][-1]
R = 0
for j in range(0,74,2):
m,n = k[i][j],k[i][j+1]
minv = invert(m,p)
ninv = invert(n,p)
R += 2*minv^2*ninv^2*t
Ge[54+j,i] = minv^3*ninv
Ge[54+j+1,i] = minv*ninv^3
Ge[-2,i] = R
Ge[-1,i] = g
Ge[i,i] = p
for i in range(74):
Ge[54+i,54+i] = 1
Ge[-2,-2] = 1
Ge[-1,-1] = 1
for lines in Ge.LLL():
line = list(lines)
if line[:54] == [0]*54:
print(line)

得到一系列$\frac{tb_i}{a_i},\frac{ta_i}{b_i}$以后,采取枚举的方式即可求得flag的16进制形式,再进行转化即可

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
from gmpy2 import invert
import itertools

p = 112484851204745228541474305508055656664633080619278978956579531639763617075417

t = 1
for i in range(10):
t *= ord('0')+i
for i in range(6):
t *= ord('a')+i

table = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']

x = [132589502497094545512529920000, 174081269554562066048352000000, 149112036951786496978790400000, 154791989943541981199769600000, 137858298313915817961523200000, 167428070755259693950771200000, 138114065101140151575552000000, 167118018772379583406417920000, 278530031287299305677363200000, 82868439060684090945331200000, 270703567598234697088081920000, 85264295292030399697152000000, 137858298313915817961523200000, 167428070755259693950771200000, 292384869893357075599564800000, 78941666621534027812300800000, 157883333243068055624601600000, 146192434946678537799782400000, 157450034215299772796129280000, 146594753309104897724928000000, 281343465946766975431680000000, 82039754670077250035877888000, 137592879949815094399795200000, 167751041570759809101139200000, 157552340930189506241740800000, 146499561910852232206924800000, 142988679163533333395865600000, 161420813586957552153926400000, 284156900606234645185996800000, 81227479871363613896908800000, 163391544940405424599756800000, 141264035006955628716748800000, 292384869893357075599564800000, 78941666621534027812300800000, 286970335265702314940313600000, 80431132029487500035174400000, 157768758980917788530534400000, 146298602292318827224473600000, 292384869893357075599564800000, 78941666621534027812300800000, 140671732973383487715840000000, 164079509340154500071755776000, 143325916614390723333120000000, 161040999907929416737093632000, 146400909007208560670085120000, 157658508275829795666432000000, 163841194874881944516096000000, 140876346403162954607063040000, 151925471611254166733107200000, 151925471611254166733107200000, 292384869893357075599564800000, 78941666621534027812300800000, 143485167632851157470156800000, 160862264058975000070348800000, 142988679163533333395865600000, 161420813586957552153926400000, 281343465946766975431680000000, 82039754670077250035877888000, 132589502497094545512529920000, 174081269554562066048352000000, 278530031287299305677363200000, 82868439060684090945331200000, 151925471611254166733107200000, 151925471611254166733107200000, 154687752913276969764618240000, 149212516761053199470016000000, 145967609979440277841612800000, 158126511268856377620172800000, 143638627705185757638574080000, 160690402665749599429248000000, 163391544940405424599756800000, 141264035006955628716748800000, 276228130202280303151104000000, 83559009386189791703208960000]
m = ""
for i in range(0,len(x),2):
for j,k in itertools.product(table,repeat=2):
a = ord(j)
b = ord(k)
if (t*b*invert(a,p) % p == x[i] % p) and (t*a*invert(b,p) % p == x[i+1]%p):
m += chr(a)
m += chr(b)
break

print(m)
# 706561726c7b615f35796d5068306e595f6f465f62527537005f63306d706c00783174597d
flag = bytes.fromhex(m)
print(flag)

得到的结果是pearl{a_5ymPh0nY_oF_bRu7\x00_c0mpl\x00x1tY}

进行猜测:

1
2
3
4
5
6
7
8
9
pearl{a_5ymph0nY_oF_bRu7e_c0mplex1tY}
pearl{a_5ymph0nY_oF_bRu7e_c0mplEx1tY}
pearl{a_5ymph0nY_oF_bRu7e_c0mpl3x1tY}
pearl{a_5ymph0nY_oF_bRu7E_c0mplEx1tY}
pearl{a_5ymph0nY_oF_bRu7E_c0mplex1tY}
pearl{a_5ymph0nY_oF_bRu7E_c0mpl3x1tY}
pearl{a_5ymph0nY_oF_bRu73_c0mpl3x1tY}
pearl{a_5ymph0nY_oF_bRu73_c0mplex1tY}
pearl{a_5ymph0nY_oF_bRu73_c0mplEx1tY}

正确的结果是pearl{a_5ymPh0nY_oF_bRu73_c0mpl3x1tY}

在鸡块师傅的指正下,得到pearl{a_5ymPh0nY_oF_bRu7\x00_c0mpl\x00x1tY}这个结果后正确的思路如下:

出现\x00,说明改组$\frac{ta_i}{b_i} = \frac{tb_i}{a_i}$,满足这个情况的a,b有$a = ord(‘0’),b=ord(‘0’)$…$a=ord(‘f’),b=ord(‘f’)$

也就是说,\x00的位置只可能是\x00,\x11,",3,D,U,f,w,\x88,\x99,\xaa,\xbb,\xcc,\xdd,\xee,\xff

这样的话,很明显结果就是pearl{a_5ymPh0nY_oF_bRu73_c0mpl3x1tY}

Security++

secure.py

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
from flag import flag, key
from base64 import b64encode
from enc import encrypt_block, pad


def encrypt(data: bytes):
pt = data + flag
pt = pad(pt)
block_count = len(pt) // 16
encText = b''
for i in range(block_count):
if i % 2 == 0:
encText += encrypt_block(pt[i * 16:i * 16 + 16], key[0:16])
else:
encText += encrypt_block(pt[i * 16:i * 16 + 16], key[16:])
return encText


def main():
while 1:
msg = input("\nEnter plaintext: ").strip()
res = encrypt(msg.encode())
print(b64encode(res).decode())


if __name__ == '__main__':
main()

enc.py

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
from copy import copy

s_box = (
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
)


def bytes2matrix(text):
return [list(text[i:i + 4]) for i in range(0, len(text), 4)]


def matrix2bytes(m):
plaintext = b''
for row in range(4):
for j in range(4):
plaintext += chr(m[row][j]).encode('latin-1')
return plaintext


def pad(text: bytes):
if len(text) % 16 != 0:
pad_len = ((len(text) // 16) + 1) * 16 - len(text)
else:
pad_len = 0
text = text.ljust(len(text) + pad_len, b'.')
return text


def sub_bytes(state, sbox):
for i in range(len(state)):
for j in range(4):
state[i][j] = sbox[state[i][j]]
return state


def shift_rows(s):
s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
return s


def gmul(a, b):
if b == 1:
return a
tmp = (a << 1) & 0xff
if b == 2:
return tmp if a < 128 else tmp ^ 0x1b
if b == 3:
return gmul(a, 2) ^ a


def mix_col(col):
temp = copy(col)
col[0] = gmul(temp[0], 2) ^ gmul(temp[1], 3) ^ gmul(temp[2], 1) ^ gmul(temp[3], 1)
col[1] = gmul(temp[0], 1) ^ gmul(temp[1], 2) ^ gmul(temp[2], 3) ^ gmul(temp[3], 1)
col[2] = gmul(temp[0], 1) ^ gmul(temp[1], 1) ^ gmul(temp[2], 2) ^ gmul(temp[3], 3)
col[3] = gmul(temp[0], 3) ^ gmul(temp[1], 1) ^ gmul(temp[2], 1) ^ gmul(temp[3], 2)
return col


def add_round_key(state, round_key):
for i in range(len(state)):
for j in range(len(state[0])):
state[i][j] = state[i][j] ^ round_key[i][j]
return state


def encrypt_block(ptext: bytes, key):
cipher = ptext
rkey = bytes2matrix(key)

for i in range(10):
ctext = bytes2matrix(cipher)
ctext = sub_bytes(ctext, s_box)
ctext = shift_rows(ctext)
temp = copy(ctext)
for j in range(4):
column = [temp[0][j], temp[1][j], temp[2][j], temp[3][j]]
column = mix_col(column)
for k in range(4):
ctext[k][j] = column[k]
ctext = add_round_key(ctext, rkey)
cipher = matrix2bytes(ctext)
return cipher

靶机:dyn.ctf.pearlctf.in 30015

花了很长时间看代码,后来在鸡块师傅指导下,可以用逐字节爆破的方式进行求解,具体过程如下:

先输入空,可知flag长度为32

然后输入000000000000000,即15个0。

此时明文分块为
$$
000000000000000m_1 \quad m_2m_3…m_{17} \quad m_{18}…m_{32}.
$$
最后那个点是填充

我们再输入000000000000000 + X,X为我们要爆破的字符

此时明文分块为
$$
000000000000000X \quad m_1m_2…m_{16} \quad m_{17}…m_{32}
$$
只需要对比第一块密文是否相同,我们就可以知道X是什么字符,也就是flag的第一个字符

接下来,我们爆破第二位

这个时候输入00000000000000,即14个0

此时明文分块为:
$$
00000000000000m_1m_2 \quad m_3m_4…m_{18} \quad m_{19}…m_{32}..
$$
最后两个点是填充

我们再输入00000000000000m1 + X,上一步已经把m1求出来了

此时明文分块为
$$
00000000000000m_1X \quad m_1m_2 …m_{16} \quad m_{17}…m_{32}
$$
还是对比第一块密文即可判断出flag的字符,依次爆破出flag的前16位

接下来爆破第17位

此时我们需要输入0000000000000000000000000000000,即31个0

此时明文分块为
$$
0000000000000000 \quad 00000000000000m_1 \quad m_2…m_{17} \quad m_{18}…m_{32}.
$$
再输入flag[-15:] + X + 000000000000000,就是flag倒数15个,X是要爆破的字符
$$
m_2…m_{16}X \quad 000000000000000m_1 \quad m_2…m_{17} \quad m_{18}…m_{32}.
$$
这个时候我们只需要把第一块密文和第三块密文进行对比,即可依次爆出所有字符

exp.py

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
from pwn import *
from tqdm import *
import base64


sh = remote("dyn.ctf.pearlctf.in",30015)

flag = ""
for i in trange(16):
sh.recvuntil(b"Enter plaintext: ")
sh.sendline(("0"*(15-i)).encode())
c = sh.recvline().strip()
enc_flag = base64.b64decode(c)
for j in range(33,128):
msg = ("0"*(15-i)) + flag + chr(j)
sh.recvuntil(b"Enter plaintext: ")
sh.sendline(msg.encode())
cc = sh.recvline().strip()
cipher = base64.b64decode(cc)
if cipher[:16] == enc_flag[:16]:
flag += chr(j)
print(f"flag:{flag}")
break

for i in trange(16):
sh.recvuntil(b"Enter plaintext: ")
sh.sendline(("0"*(31-i)).encode())
c = sh.recvline().strip()
enc_flag = base64.b64decode(c)
for j in range(33,128):
msg = flag[-15:] + chr(j) + "0"*(15-i)
sh.recvuntil(b"Enter plaintext: ")
sh.sendline(msg.encode())
cc = sh.recvline().strip()
cipher = base64.b64decode(cc)
if cipher[:16] == enc_flag[32:48]:
flag += chr(j)
print(f"flag:{flag}")
break
# pearl{n0t_sn34ky_A3S_3ncrypt10n}
-------------已经到底啦!-------------