跳转至

CRYPTO

easy_pad

经典的 padding oracle attack,详见

paddint_oracle.py
def padding_oracle_attack(encrypt_msg):
IV = encrypt_msg[:16]
ciphertext = encrypt_msg[16:]
block_size = 16
blocks = [
    ciphertext[i : i + block_size] for i in range(0, len(ciphertext), block_size)
]
full_plaintext = b""

for block_index in range(len(blocks)):
    if block_index == 0:
        attack_IV = bytearray(IV)
    else:
        attack_IV = bytearray(blocks[block_index - 1])

    current_block = blocks[block_index]
    plaintext = bytearray(16)
    for byte_index in range(block_size - 1, -1, -1):
        padding_value = block_size - byte_index

        for i in range(block_size - 1, byte_index, -1):
            attack_IV[i] = IV[i] ^ plaintext[i] ^ padding_value
        # the char set of plaintext
        plaintext_set = b"0123456789abcdef"
        for pf in plaintext_set:
            attack_IV[byte_index] = IV[byte_index] ^ pf ^ padding_value
            attack_cipher = attack_IV + current_block
            if padding_decide(attack_cipher):
                plaintext[byte_index] = pf
                print("plaintext: ", plaintext)
                break
    IV = current_block
    full_plaintext += plaintext
return full_plaintext

交互脚本:

easy_pad_solver.py
from pwn import *
from Crypto.Util.Padding import pad, unpad
from padding_oracle import padding_oracle_attack

# 提交message
def handOutMsg(message):
    print("hand out ing .............")
    con.sendafter(b"Quit", b"2\n")
    con.sendafter(b"Give me message", message.encode() + b"\n")

# padding
def sendAttackCiphertext(attack_ciphertext):
    # 将attack_ciphertext 由 bytearray 转为可发送的数据类型
    attack_ciphertext = attack_ciphertext.hex().encode()
    try:
        con.sendafter(b"2. Quit", b"1\n")
        con.sendafter(b"Give me ciphertext", attack_ciphertext + b"\n")
        respon = con.recvuntil(b"1. Decrypt").decode()
        return respon
    except EOFError:
        print("Connection closed unexpectedly")
        return None

# padding valid decide
def padding_decide(attack_ciphertext):
    times = 16
    responses = [sendAttackCiphertext(attack_ciphertext) for _ in range(times)]
    # 超过一半的返回值为False,则返回True
    if sum(1 for resp in responses if "False" in resp) > times / 2:
        return True
    elif sum(1 for resp in responses if "True" in resp) > times / 2:
        return False
    else:
        raise ValueError("padding_decide error")


context.log_level = "debug"
con = connect("127.0.0.1", 61600)

encrypt_msg = con.recvuntil(b"\n")[:-1]  # remove the last '\n'
encrypt_msg = bytes.fromhex(encrypt_msg.decode())
print(encrypt_msg)
# exit()

recovered_bytes = padding_oracle_attack(encrypt_msg)
print("recovered_bytes", recovered_bytes)
recovered_message = recovered_bytes.decode("ascii")
print("recovered_message: ", recovered_message)
handOutMsg(recovered_message)

con.recvall(timeout=10)
con.close()

FIB I

考察斐波那契数列知识,参考 Properties of Fibonacci Numbers & OI-wiki

三个部分依次参考:

part1

part2

k 看作二进制编码,分别用该递推公式求的 2 的幂倍的 a,然后使用 part1 相加

part3

其中 \((-1)^n\) 用下面的公式获得:

代码:

交互代码:

fib1.py
import re
from pwn import *
from fib1_func import fib_plus, fib_multi, fib_minus


def extract_info(data):
    p = int(re.search(r"p = (\d+)", data).group(1))
    k = int(re.search(r"k = (\d+)", data).group(1))
    fib_a = tuple(map(int, re.search(r"fib\(a, p\) = \((\d+), (\d+)\)", data).groups()))
    fib_b = tuple(map(int, re.search(r"fib\(b, p\) = \((\d+), (\d+)\)", data).groups()))
    return p, k, fib_a, fib_b


def getAns(res, p):
    return res[0] * p + res[1]


context.log_level = "debug"
con = connect("127.0.0.1", 57033)
for _ in range(10):
    data = con.recvuntil(b"fib(a + b, p) =").decode()
    p, k, fib_a, fib_b = extract_info(data)

    # part 1
    res = fib_plus(fib_a, fib_b, p)
    ans = getAns(res, p)
    con.sendline(str(ans).encode())

    # part 2
    data = con.recvuntil(b"fib(k * a, p) =").decode()
    res = fib_multi(k, fib_a[0], fib_a[1], p)
    ans = getAns(res, p)
    con.sendline(str(ans).encode())

    # part 3
    data = con.recvuntil(b"if fib(a + c, p) = (0, 1), fib(c, p) = ").decode()
    # extract fib(a+c, p)
    fib_apc = tuple(
        map(int, re.search(r"fib\(a \+ c, p\) = \((\d+), (\d+)\)", data).groups())
    )
    fib_c = fib_minus(fib_apc, fib_a, p)
    # fib_c = fib_a
    ans = getAns(fib_c, p)
    con.sendline(str(ans).encode())
con.recvall()
con.close()

函数代码:

fib1_func.py
# part 1
def fib_plus(fib_a, fib_b, p):
    fa, fap1 = fib_a
    fb, fbp1 = fib_b
    # fapb = fa*fbp1 + fam1*fb
    fam1 = fap1 - fa
    fapb = (fa * fbp1 + fam1 * fb) % p
    # fap1pb = fap1*fbp1 + fa*fb
    fapbp1 = (fap1 * fbp1 + fa * fb) % p

    return fapb, fapbp1


# part 2
import sys
sys.setrecursionlimit(1500)  # 将递归深度限制设置为 1500

def fib_2power(n, fa, fap1, p):
    if n == 1:
        return fa, fap1
    # print(n)
    c, d = fib_2power(n >> 1, fa, fap1, p)

    c2 = (c * (2 * d - c)) % p
    d2 = (c * c + d * d) % p

    if n & 1:
        return d2, (c2 + d2) % p
    else:
        return c2, d2


def fib_multi(k, fa, fap1, p):
    from math import log2

    # 将 k 转为 2 进制数,并分别求 fib_2power(k) 的值,使用 fib_plus() 合并
    k_bin = bin(k)[2:][::-1]
    k_len = len(k_bin)
    res = (0, 1)
    for i in range(k_len):
        if k_bin[i] == "1":
            res = fib_plus(res, fib_2power(2**i, fa, fap1, p), p)
    return res


# Part 3
def fib_minus(fib_a, fib_b, p):
    fa, fap1 = fib_a
    fb, fbp1 = fib_b
    fam1 = fap1 - fa
    fbm1 = fbp1 - fb
    factor = fbp1 * fbm1 - fb * fb
    famb = (factor * (fa * fbm1 - fam1 * fb)) % p
    fambp1 = (factor * (fap1 * fbm1 - fa * fb)) % p

    return famb, fambp1

[!FLAG]

ZJUCTF{fibonacci_sequence_mod_n_is_a_kind_of_group}

ezxor

若明文比特为 \(x_{0}x_{1}x_{2}x_{3}\dots\) 翻转前的 cipher 比特为 \(y_{0}y_{1}y_{2}y_{3}\dots\) ,那么由于异或的交替性,有 \(x_{i}=y_{i-1} \oplus y_{i} (1\leq i<len(cipher))\) ,所以我们可以靠 cipher 很快解出明文;

ezxor_solver.py
ciphertext = "01100...01010110"

# print(bin(ord("R"))[2:].zfill(8)) # 01010010
plaintext_bin = "0"
for i in range(len(ciphertext) - 1):
    plaintext_bin += str(int(ciphertext[i]) ^ int(ciphertext[i + 1]))

# 8bits 一组,转换为字符
plaintext = ""
for i in range(0, len(plaintext_bin), 8):
    plaintext += chr(int(plaintext_bin[i : i + 8], 2))

print(plaintext)
# Romn{e!é^ the mundane worl¤     slike a starqy sky admiredàfrom a&bustling city.[ÊUCF{Tell_do>e!_Wel£¯me_to_YJUCTF_2<24!}

# ZJUCTF{Well_done!_Welcome_to_ZJUCTF_2024!}

翻转了怎么办?猜吧,一猜一个准:

[!FLAG]

ZJUCTF{Well_done!_Welcome_to_ZJUCTF_2024!}

shad0wtime

紧张刺激的拼手速环节

shad0wtime_solver.py
from pwn import *

context.log_level = "debug"
conn = remote("localhost", 59279)
conn.recvuntil(b"Enter your option: ")
conn.sendline(b"sign_time")

signature_data = conn.recvuntil(b"Enter your option: ").decode()
signature_dict = eval(signature_data.split("\n")[0])


msg = signature_dict["msg"]
r = signature_dict["r"]
s = signature_dict["s"]

conn.sendline(b"verify")

conn.sendline(msg.encode())
conn.sendline(r[2:].encode())  # 去掉'0x'前缀
conn.sendline(s[2:].encode())  # 去掉'0x'前缀

result = conn.recvall().decode()
print(result)

conn.close()

[!FLAG]

ZJUCTF{w31l_you_a!re4d7_kn0w_T0__name__ [[vaRIabLes]] _properly}