第二届熵密杯 WP

admin 2024年9月10日13:08:36评论95 views字数 22250阅读74分10秒阅读模式

-联合战队|共同成长-

第二届熵密杯 WP

第二届熵密杯  WP

此次赛事由中国密码学会、宁夏回族自治区密码管理局、中国科学院大学密码学院支持,国家信息技术安全研究中心联合密码科技国家工程研究中心主办,采取线下赛方式,通过模拟仿真重要行业领域密码应用典型场景,预置密码算法模式配置、密钥管理、密码协议、密码服务、网络安全等方面密码应用安全缺陷和网络安全漏洞,综合检验参赛队伍在密码应用安全缺陷识别、密码算法功能编程调用、网络协议数据解析、网络和系统配置核查、源代码审计等方面的实战技能。

WriteUP
熵密杯

初始谜题3

爆破了一下e、尝试解矩阵方程得到s然后拿来解密 思路不太严谨,但是因为答案交对了所以不再深究

from sage.all import *
import sympy as sp

n = 16
q = 251  # 模数

A=sp.Matrix([[14723727163107149136159711904410210920823714], [124778782281092232514042214144156221126163], [2713011170209482401501162822521815480116], [7423723517371822650118640156207304849], [2487352491531812442102392231981071965365135], [1711522399716823294184100471431471131757938], [132469523020139137310117220766511149237], [1382119113818095691208321522311279193175246], [2451911742487226108118199133217017716367103], [2219268720132461711020101551943765248], [146226103616218516713714624414624710218108100], [73109188121192857037191214154305083656], [88175891602017515099214157825614811830], [13025432511315728108107240161881194975], [24320223212820719321794910313955205019610], [9719124713982119115521641951242201672148156]])

b=sp.Matrix([[1], [205], [220], [195], [79], [84], [137], [199], [11], [135], [23], [215], [137], [69], [185], [142]])

c1=sp.Matrix([[22124811118922621298237177241172421821686186], [209150184182115711854850901441281072327], [1535492324598183551701491131023889110], [2451412509717921212285175479212197161241248], [1452138112121160151751242052241507923124160], [84128143246213224175235864420511875142176119], [302032141055110271206103918519712022772174], [222659818229292472331022135020820565210], [24391954261105357511295833837118207], [44152253614232754716223020249103141], [3088188161117149582043738381620118211918], [1429319517511414817425161186234159155224170148], [2123101106109241105173921513388158122210216], [23622820889318819215865178246242104130168226], [2432337182115110895315731645118171134109], [119107812511035220908415315184225971607]])

c2=sp.Matrix([[82], [77], [135], [145], [7], [51], [36], [204], [73], [123], [200], [36], [142], [185], [108], [111]])

def decrypt(c1, c2, s):
    m_dec = (c2 - c1 * s) % q
    m_rec = m_dec.applyfunc(lambda x: round(2 * x / q) % 2)  # 还原消息
    m_bin = ''.join([str(bit) for bit in m_rec])  # 将SymPy矩阵转换为二进制字符串
    m_rec_int = int(m_bin, 2)  # 将二进制字符串转换为整数
    return m_rec_int

from tqdm import trange
Ainv = A.inv_mod(q)
for e in trange(2**16):
    try:
        e = sp.Matrix(list(map(int, bin(e)[2:].zfill(16))))
        s = Ainv * (b - e)
        m = decrypt(c1, c2, s)
        print(s)
        print(m)
        print(hex(m))
        break
    except Exception as e:
        print(e)
        continue

初始谜题1

用第一块已知明文计算得到k然后解密

from sympy import Mod, Integer
from sympy.core.numbers import mod_inverse

def decrypt_message(encrypted_message, key):
    num_blocks = len(encrypted_message) // 32
    blocks = [encrypted_message[i * 32:(i + 1) * 32for i in range(num_blocks)]

    decrypted_blocks = []

    k = key

    # 解密每个分组
    for block in blocks:
        block_int = int.from_bytes(block, byteorder='big')
        key_inv = mod_inverse(k, MODULUS)
        decrypted_block_int = Mod(block_int * key_inv, MODULUS)
        decrypted_blocks.append(decrypted_block_int)
        k += 1  # 密钥自增1

    # 将解密后的分组连接成最终的明文
    decrypted_message = b''.join(
        int(block_int).to_bytes(16, byteorder='big'for block_int in decrypted_blocks
    )

    # 去除前缀
    if decrypted_message.startswith(MSG_PREFIX.encode('utf-8')):
        decrypted_message = decrypted_message[len(MSG_PREFIX):]

    return decrypted_message.rstrip(b'x00').decode('utf-8')

# 模数
N_HEX = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
MODULUS = Integer(int(N_HEX, 16))
MSG_PREFIX = "CryptoCup message:"

c = 'c19ab1f9004f4146bfebc26651ffa839baaad49375cfba3b6a64e86b6447a1fe296be2a43dc5a91a7faf3b6b339ec36ec4cb656b98dd609b8982a6bf81484255c004ce89ae0c6969883d389d66bacd69b87cff758c68df1eefc2c2b66e9e520b1816195a55fd6759c6c23f945c5c32e1fc5da4e192c832033658fb71fefd6287'
c = bytes.fromhex(c)

num_blocks = len(c) // 32
blocks = [c[i * 32:(i + 1) * 32for i in range(num_blocks)]
block = MSG_PREFIX[:16].encode()
k = int.from_bytes(blocks[0], byteorder='big') * mod_inverse(int.from_bytes(block, byteorder='big'), MODULUS)
print(k)
msg = decrypt_message(c, k)
print(msg)

初始谜题2

sm3 hash extend attack

from functools import reduce
import struct
BIT_BLOCK_H = [0x000x800xC00xE00xF00xF80xFC0xFE0xFF]
BIT_BLOCK_L = [0x00x010x030x070x0F0x1F0x3F0x7F0xFF]
BIT_EACH = [1248163264128256]
BIT_EACH_32 = [12481632641282565121024204840968192163843276865536131072262144,
               5242881048576209715241943048388608167772163355443267108864134217728268435456,
               536870912107374182421474836484294967296]

IV = [193777419112260932413882523753666478592284263647637232452238177296132969243214]

def rotate_left(a, k):
    k %= 32
    high, low = divmod(a, BIT_EACH_32[32 - k])
    return high + low * BIT_EACH_32[k]


T_j = [0x79cc4519] * 16 + [0x7a879d8a] * 48
T_j_rotate_left = [rotate_left(Tj, j) for j, Tj in enumerate(T_j)]


def PUT_UINT32_BE(n):
    return [int((n >> 24) & 0xff), int((n >> 16) & 0xff), int((n >> 8) & 0xff), int(n & 0xff)]


def CF(V_i, B_i):
    W = [(B_i[ind] * BIT_EACH_32[24]) + (B_i[ind + 1] * BIT_EACH_32[16]) + (B_i[ind + 2] * BIT_EACH_32[8]) + (
        B_i[ind + 3]) for ind in range(0644)]
    for j in range(1668):
        high_W3_15, low_W3_15 = divmod(W[-3], BIT_EACH_32[17])
        high_W13_7, low_W13_7 = divmod(W[-13], BIT_EACH_32[25])
        # P_1
        X = W[- 16] ^ W[- 9] ^ (high_W3_15 + low_W3_15 * BIT_EACH_32[15])
        high_P1_15, low_P1_15 = divmod(X, BIT_EACH_32[17])
        r_l_15 = high_P1_15 + low_P1_15 * BIT_EACH_32[15]
        high_P1_23, low_P1_23 = divmod(X, BIT_EACH_32[9])
        r_l_23 = high_P1_23 + low_P1_23 * BIT_EACH_32[23]
        # return X ^ (rotate_left(X, 15)) ^ (rotate_left(X, 23))
        W.append(X ^ r_l_15 ^ r_l_23 ^ (high_W13_7 + low_W13_7 * BIT_EACH_32[7]) ^ W[- 6])
        # W.append(P_1(W[- 16] ^ W[- 9] ^ (high_W3_15 + low_W3_15 * BIT_EACH_32[15])) ^ (
        #         high_W13_7 + low_W13_7 * BIT_EACH_32[7]) ^ W[- 6])
    W_1 = [W[j] ^ W[j + 4for j in range(64)]
    A, B, C, D, E, F, G, H = V_i
    for j in range(016):
        high_A12, low_A12 = divmod(A, BIT_EACH_32[20])
        r_l_12 = high_A12 + low_A12 * BIT_EACH_32[12]
        high, low = divmod((r_l_12 + E + T_j_rotate_left[j]) & 0xFFFFFFFF, BIT_EACH_32[25])
        SS1 = high + low * BIT_EACH_32[7]
        SS2 = SS1 ^ r_l_12
        # Wj = (B_i[ind] * BIT_EACH_32[24]) + (B_i[ind + 1] * BIT_EACH_32[16]) + (B_i[ind + 2] * BIT_EACH_32[8]) + (B_i[ind + 3])
        # FF
        TT1 = ((A ^ B ^ C) + D + SS2 + W_1[j]) & 0xFFFFFFFF
        # GG
        TT2 = ((E ^ F ^ G) + H + SS1 + W[j]) & 0xFFFFFFFF
        high_B9, low_B9 = divmod(B, BIT_EACH_32[23])
        high_F19, low_F19 = divmod(F, BIT_EACH_32[13])
        high, low = divmod(TT2, BIT_EACH_32[23])
        r_l_9 = high + low * BIT_EACH_32[9]
        high, low = divmod(TT2, BIT_EACH_32[15])
        r_l_17 = high + low * BIT_EACH_32[17]
        A, B, C, D, E, F, G, H = TT1, A, high_B9 + low_B9 * BIT_EACH_32[9] & 0xffffffff, C, (
                TT2 ^ r_l_9 ^ r_l_17) & 0xffffffff, E, high_F19 + low_F19 * BIT_EACH_32[19] & 0xffffffff, G
    for j in range(1664):
        high_A12, low_A12 = divmod(A, BIT_EACH_32[20])
        r_l_12 = high_A12 + low_A12 * BIT_EACH_32[12]
        high, low = divmod((r_l_12 + E + T_j_rotate_left[j]) & 0xFFFFFFFF, BIT_EACH_32[25])
        SS1 = high + low * BIT_EACH_32[7]
        SS2 = SS1 ^ r_l_12
        # FF
        TT1 = (((A & B) | (A & C) | (B & C)) + D + SS2 + W_1[j]) & 0xFFFFFFFF
        # GG
        TT2 = (((E & F) | ((~ E) & G)) + H + SS1 + W[j]) & 0xFFFFFFFF
        high_B9, low_B9 = divmod(B, BIT_EACH_32[23])
        high_F19, low_F19 = divmod(F, BIT_EACH_32[13])
        high, low = divmod(TT2, BIT_EACH_32[23])
        r_l_9 = high + low * BIT_EACH_32[9]
        high, low = divmod(TT2, BIT_EACH_32[15])
        r_l_17 = high + low * BIT_EACH_32[17]
        A, B, C, D, E, F, G, H = TT1, A, high_B9 + low_B9 * BIT_EACH_32[9] & 0xffffffff, C, (
                TT2 ^ r_l_9 ^ r_l_17) & 0xffffffff, E, high_F19 + low_F19 * BIT_EACH_32[19] & 0xffffffff, G
    return [A ^ V_i[0], B ^ V_i[1], C ^ V_i[2],
            D ^ V_i[3], E ^ V_i[4], F ^ V_i[5], G ^ V_i[6], H ^ V_i[7]]


# def CFs(V_i):


def digest(msg, state=(IV, 0)):
    msg = str2bytes(msg)
    cur_v, cur_len = state
    len1 = len(msg) + cur_len
    msg.append(0x80)
    reserve1 = len1 % 64 + 1
    range_end = 56 if reserve1 <= 56 else 120
    msg.extend([0] * (range_end - reserve1))
    bit_length = len1 * 8
    msg.extend(struct.pack(">Q", bit_length))
    B = (msg[i:i + 64for i in range(0, len(msg), 64))
    y = reduce(CF, B, cur_v)
    b = bytearray()
    [b.extend(PUT_UINT32_BE(each)) for each in y]
    return bytes(b)


def str2bytes(msg: str, encoding='utf-8'):
    """字符串转换成byte数组"""
    msg_bytearray = msg.encode(encoding) if isinstance(msg, str) else msg
    return list(msg_bytearray)


def byte2str(msg, decode='utf-8'):
    """byte数组转字符串"""
    return msg.decode(decode) if isinstance(msg, (bytes, bytearray)) else msg


def hex2byte(msg):
    """16进制字符串转换成byte列表"""
    if not isinstance(msg, str):
        raise ValueError('message must be string')
    ml = len(msg)
    if (ml & 1) != 0:
        msg = '0' + msg
    return list(bytes.fromhex(msg))
from SM3 import digest as hash_func
import struct


H = hash_func


def attack(pub, apd):
    """
    forge based on pub
    :param pub: public info
    :param apd: data appended bt attacker
    :return: forgery
    """


    def create_pad(len1):
        pad = [0x80]
        reserve1 = len1 % 64 + 1
        range_end = 56 if reserve1 <= 56 else 120
        pad.extend([0] * (range_end - reserve1))
        bit_length = len1 * 8
        pad.extend(struct.pack(">Q", bit_length))
        return bytes(pad)

    pub_msg, pub_hsh, sec_len = pub
    pad = create_pad(sec_len + len(pub_msg))

    msg = pub_msg + pad + apd

    cur_v = int.from_bytes(pub_hsh, byteorder="big")
    cur_v = [(cur_v >> ((7 - i) * 32)) & 0xFFFFFFFF for i in range(8)]

    cur_len = sec_len + len(pub_msg) + len(pad)
    state = cur_v, cur_len
    sig = H(apd, state)
    return msg, sig

counter = 0x55c4c7ef
token = 'ba4f92230b5d4c71c1670955d64fe9e6110fec33558caf64db751c3bc60f45cc'
AppendData, GeneratedHash = attack((
    counter.to_bytes((counter.bit_length() + 7) // 8'big'), 
    bytes.fromhex(token), 
    32), 
(counter + 1).to_bytes((counter.bit_length() + 7) // 8'big'))
print(AppendData.hex())
print(GeneratedHash.hex())

gitea服务器

写出解密算法后爆破key

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

#define ROUND 16

//S-Box 16x16
int sBox[16] =
        {
                210412,
                13914,
                71186,
                501513
        };
int inv_sBox[16] = {1340521211810619315714};

// 将十六进制字符串转换为 unsigned char 数组
void hex_to_bytes(const char* hex_str, unsigned char* bytes, size_t bytes_len) {
    size_t hex_len = strlen(hex_str);
    if (hex_len % 2 != 0 || hex_len / 2 > bytes_len) {
        fprintf(stderr"Invalid hex string length.n");
        return;
    }

    for (size_t i = 0; i < hex_len / 2; i++) {
        sscanf(hex_str + 2 * i, "%2hhx", &bytes[i]);
    }
}


// 派生轮密钥
void derive_round_key(unsigned int key, unsigned char *round_key, int length) {

    unsigned int tmp = key;
    for(int i = 0; i < length / 16; i++)
    {
        memcpy(round_key + i * 16,      &tmp, 4);   tmp++;
        memcpy(round_key + i * 16 + 4,  &tmp, 4);   tmp++;
        memcpy(round_key + i * 16 + 8,  &tmp, 4);   tmp++;
        memcpy(round_key + i * 16 + 12, &tmp, 4);   tmp++;
    }
}


// 比特逆序
void reverseBits(unsigned char* state) {
    unsigned char temp[16];
    for (int i = 0; i < 16; i++) {
        unsigned char byte = 0;
        for (int j = 0; j < 8; j++) {
            byte |= ((state[i] >> j) & 1) << (7 - j);
        }
        temp[15 - i] = byte;
    }
    for (int i = 0; i < 16; i++) {
        state[i] = temp[i];
    }
}


void sBoxTransform(unsigned char* state) {
    for (int i = 0; i < 16; i++) {
        int lo = sBox[state[i] & 0xF];
        int hi = sBox[state[i] >> 4];
        state[i] = (hi << 4) | lo;
    }
}

void inv_sBoxTransform(unsigned char* state) {
    for (int i = 0; i < 16; i++) {
        int lo = inv_sBox[state[i] & 0xF];
        int hi = inv_sBox[state[i] >> 4];
        state[i] = (hi << 4) | lo;
    }
}


void leftShiftBytes(unsigned char* state) {
    unsigned char temp[16];
    for (int i = 0; i < 16; i += 4) {
        temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
        temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
        temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
        temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
    }
    for (int i = 0; i < 16; i++)
    {
        state[i] = temp[i];
    }
}

void inv_leftShiftBytes(unsigned char* state) {
    unsigned char temp[16];
    for (int i = 0; i < 16; i += 4) {
        // temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
        // temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
        // temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
        // temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
        temp[i + 0] = (state[i + 3] >> 3) | (state[i + 2] << 5);
        temp[i + 1] = (state[i + 0] >> 3) | (state[i + 3] << 5);
        temp[i + 2] = (state[i + 1] >> 3) | (state[i + 0] << 5);
        temp[i + 3] = (state[i + 2] >> 3) | (state[i + 1] << 5);
    }
    for (int i = 0; i < 16; i++)
    {
        state[i] = temp[i];
    }
}


// 轮密钥加
void addRoundKey(unsigned char* state, unsigned char* roundKey, unsigned int round) {
    for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 8; j++) {
            state[i] ^= ((roundKey[i + round * 16] >> j) & 1) << j;
        }
    }
}

// 加密函数
void encrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
    unsigned char roundKeys[16 * ROUND] = {}; //

    // 生成轮密钥
    derive_round_key(key, roundKeys, 16 * ROUND);

    // 初始状态为16字节的口令
    unsigned char state[16]; // 初始状态为16字节的密码
    memcpy(state, password, 16); // 初始状态为密码的初始值

    // 迭代加密过程
    for (int round = 0; round < ROUND; round++)
    {
        reverseBits(state);
        sBoxTransform(state);
        leftShiftBytes(state);
        addRoundKey(state, roundKeys, round);
    }

    memcpy(ciphertext, state, 16);
}

void decrypt(unsigned char* ciphertext, unsigned int key, unsigned char* plaintext) {
    unsigned char roundKeys[16 * ROUND] = {}; //

    // 生成轮密钥
    derive_round_key(key, roundKeys, 16 * ROUND);

    // 初始状态为16字节的口令
    unsigned char state[16]; // 初始状态为16字节的密码
    memcpy(state, ciphertext, 16); // 初始状态为密码的初始值

    // 迭代加密过程
    for (int round = ROUND - 1; round >= 0; round--)
    {
        addRoundKey(state, roundKeys, round);
        inv_leftShiftBytes(state);
        inv_sBoxTransform(state);
        reverseBits(state);
    }

    memcpy(plaintext, state, 16);
}

int main() {
    unsigned char ciphertext[16]; // 16字节的状态
    unsigned char plaintext[16]; // 16字节的状态
    const char hex_ct[] = "99F2980AAB4BE8640D8F322147CBA409";
    hex_to_bytes(hex_ct, ciphertext, 16);
    for (unsigned int k = 0xFA000000; k <= 0xFFFFFFFF; ++k) {
        if ((k & 0xFFFFFF) == 0) {
            printf("%02Xn", k);
        }
        decrypt(ciphertext, k, plaintext);
        if ((plaintext[0] == 112) && (plaintext[1] == 119) && (plaintext[2] == 100) && (plaintext[3] == 58)) {
            printf("%02Xn", k);
            printf("%sn", plaintext);
            break;
        }
    }
}

// key = 0xFAB7C4D9
// pwd:%@PRjd8)k5TV

数据库管理系统服务器flag3

使用相同的公私钥进行注册,分别为cain1和cain2

pub:
04 b3 21 7d 88 4b c1 75 e6 ba 6b 36 0e b0 e6 d4 39 6e ae a7 25 c3 d6 6e 87 bf a5 be b6 c0 d3 45 6b a5 19 94 45 c5 4b 56 60 2a a6 00 25 e1 90 7b fd 26 b3 0e 86 7d b6 c5 8a 03 42 63 ae 4a 2e 27 c2
prv:
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

在fuzz过程中发现使用,用户名cain1,私钥,cain2的证书可以实现登陆成功

阅读源码发现登陆验证的逻辑存在问题,仅检测是否存在请求登录用户名、证书是否验证、证书用户名是否存在,并没有检测证书用户名和请求登录用户名是否相等。

数据库管理系统flag4

iv与key是线性同余产生的,由于只知道输出的高位,所以可以考虑用hnp求解内部状态 然后即可恢复key,进而解密得到流量包,找到flag4

from sage.all import *

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

R = PolynomialRing(Zmod(MASK+1), 'x')
x = R.gens()[0]
f = x
As, Bs = list(), list()
for _ in range(4):
    As.append(ZZ(f[1]))
    Bs.append(ZZ(f[0]))
    for i in range(ITERATIONS):
        f = MULTIPLIER*f + ADDEND
    print(f)

iv = '90fc5cf2e2f47488a257fd51e0ae615b'
iv = bytes.fromhex(iv)
ivs = [iv[i: i+4for i in range(0, len(iv), 4)]
ivs = [int(_.hex(), 16for _ in ivs]

A = matrix(ZZ, 13)
B = matrix(ZZ, 13)
for i in range(3):
    iv0, a0, b0 = ivs[0], As[0], Bs[0]
    ivi, ai, bi = ivs[i+1], As[i+1], Bs[i+1]
    inv = inverse_mod(a0, 2**64)
    A[0, i] = ZZ((ai)*inv % 2**64)
    B[0, i] = ZZ((bi*a0 - b0*ai + iv0*2**32*ai - ivi*2**32*a0)*inv % 2**64)
L = block_matrix(ZZ, [
    [1, A, 0],
    [02**640],
    [0, B, 2**64]
]).LLL()
for row in L:
    if abs(row[-1]) == 2**64:
        print([_.nbits() for _ in row])
        e0 = abs(row[0])
        iv0 = ivs[0]
        s0 = iv0*2**32 + e0
        for i in range(4):
            si = (As[i]*s0 + Bs[i]) % 2**64
            print(si >> 32, ivs[i])
        print(s0)
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT

MULTIPLIER = 6364136223846793005
ADDEND = 1
MASK = 0xffffffffffffffff
ITERATIONS = 1000

global_seed = 10447327433143939557

def genRandom():
    global global_seed
    # print("global_seed", hex(global_seed))
    for _ in range(ITERATIONS):
        global_seed = (global_seed * MULTIPLIER + ADDEND) & MASK
    return (global_seed >> 32) & 0xffffffff
def HexStringToBytes(hex_str):
    return bytes.fromhex(hex_str)

# bytes转16进制字符串
def BytesToHexString(byte_seq):
    return byte_seq.hex()

def genSM4KeyOrIV():
    return HexStringToBytes(''.join(f'{genRandom():08x}' for _ in range(4)))
def SM4Decrypt(cipher_bytes, key_bytes, iv_bytes):
    sm4 = CryptSM4()
    sm4.set_key(key_bytes, SM4_DECRYPT)
    return sm4.crypt_cbc(iv_bytes, cipher_bytes)

x = [genRandom() for _ in range(3)]
print(x)

iv_bytes = bytes.fromhex('90fc5cf2e2f47488a257fd51e0ae615b')
key_bytes = genSM4KeyOrIV()
print("key hex:", BytesToHexString(key_bytes))
with open('cipherText.dat''rb'as f3:
    cipher2_bytes = f3.read()
    plain2_bytes = SM4Decrypt(cipher2_bytes,key_bytes,iv_bytes)
with open('plainText.pcapng''wb'as f4:
    f4.write(plain2_bytes)

协同签名服务器

关键代码在于server产生signature时,对随机数k2、k3进行了复用 此时可以直接通过签名计算出私钥

while(BN_is_zero(k2)){
        if (
            !BN_rand_range(k2,n) ||
            !BN_copy(k3, k2)
            )
            { error(); return res; }
 }
from sage.all import *
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123

{
    "r""17E44712AF13BCA10BEE11BBD2074F5F590FA28B59D95EE577D7179449FDBA0A",
    "s2""F3AA79E792D9B54BD66F8C1E648B142404F9DE5838C6F1789B1270E4A3E6A43D",
    "s3""D58154A727C52464F9480BD937590EBA68053411E6375FCD1947360A6D2E0729"
}
r = 0x17E44712AF13BCA10BEE11BBD2074F5F590FA28B59D95EE577D7179449FDBA0A
s2 = 0xF3AA79E792D9B54BD66F8C1E648B142404F9DE5838C6F1789B1270E4A3E6A43D
s3 = 0xD58154A727C52464F9480BD937590EBA68053411E6375FCD1947360A6D2E0729

d2 = ZZ((s3 - s2) * inverse_mod(r, n) % n)
print(hex(d2)[2:])

总经理签名提交

题目实现了client、server协同签名

对于client端,产生(P1, Q1, e, r1, s1)作为签名内容 其中:

对于server端,接收并验证client发来的签名,并进一步产生(r, s2, s3)作为签名内容 其中:

最后,client接收server发来的签名,产生(s)作为签名内容 其中:

审计cpp代码,题目的关键漏洞在于server端在产生签名时重用了随机数内容,即k2 = k3

第二届熵密杯  WP

这将导致利用server端的签名,即可计算出私钥d2,即

对于题目给出的流量包,我们已经能够计算d2的内容 进一步地,考虑结合已知数据与d2的内容计算私钥d1,有下列式子

可以发现此时未知量只有d1,因此在GFn上解线性方程即可得到私钥d1 至此,所有私钥的值已经得到,对题目给出的摘要e产生一组合法的签名即可

注意最后需要拼接r+s进行提交,而不是直接提交s,我觉得这是题目描述的问题

from sage.all import *

a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
x = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
y = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
E = EllipticCurve(GF(p), [a, b])
G = E(x, y)

s = 0xcb524f49515c9a7387210ddcdbf1f32aad1c8806f01a362c62a5d6a5466da158

r = 0x8A6BB033033E79683E81FE36D6394262D451A3DB9D1A0C489D51543D22E67BC4
s2 = 0xB54A6668F644EC08D925552D45F66E348762B460693E7A68CBB0FDF38327DB45
s3 = 0xB50FAE013594F79192898FF7FC0A84D931B1EC56EF9174159023ACF1C708180D

e = 0xeaf0adee014bd35a12180bbc99292e3acf895203aa97f8dbbb760da04da844f6
r1 = 0x125fd6eb66351ca49073a6e55be1fa40cfd6662f80452a6bcea3b25bd69b6b26
s1 = 0x47baaef61c7a3c4c239fc2634ec25a2059d937026c6e0b72df1463fbba5b3a05

d2 = ZZ((s3 - s2) * inverse_mod(r, n) % n)

'''
s1*k1-e = d1^(-1) * r1
r1 = d1*(s1*k1-e)
r1 = d1*k1 * s1 - d1*e
s = d1*k1*s2 + d1*s3 -r
s*s1 = d1*k1*s1 * s2 + d1*s3*s1 - r*s1
s*s1 = (r1+d1*e)*s2 + d1 * s3*s1 - r*s1
'''

R = PolynomialRing(GF(n), 'x')
x = R.gens()[0]
f = (r1 + x*e)*s2 + x*s3*s1 - r*s1 - s*s1
ans = f.roots()

d1 = 90919127323695568397119051689582862352296983775157729258730148362152821090405
d2 = 75133153874808200698750375741973887146735262423059242244009334005845482114914
e = 0x9e810778a6b177c6aa1799365977adfbeef605c19b5ea917527d1541c1339019

k1 = 233
P = inverse_mod(d1, n) * G
Q = k1*G
r1 = ZZ(Q.xy()[0])
s1 = ZZ(inverse_mod(k1, n) * (e + inverse_mod(d1, n) * r1) % n)

k2 = 17
k3 = 71
R = k2*G + k3*Q
x1 = ZZ(R.xy()[0])
r = ZZ((e + x1) % n)
s2 = ZZ(d2 * k3 % n)
s3 = ZZ(d2 * (r+k2) % n)

s = (d1*k1*s2 + d1*s3 - r) % n
print(s)
print(hex(r)[2:])
print(hex(s)[2:])

原文始发于微信公众号(N0wayBack):第二届熵密杯 WP

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月10日13:08:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第二届熵密杯 WPhttps://cn-sec.com/archives/3140637.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息