osthing's blog
GitHub RSS
5 min read

密码机一小题

题出自2025熵密杯Flag3,主要学习一下密码机的调用方式以及CFB相关的一些利用方式

ctf crypto writeup wp 熵密杯

本题复现使用密码机为公司内部密码机,源码经过改造,数据也重新生成,因此和原题有出入。

题目

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
//#include "sdf_cryptoapi.h"
#include "bjcaErr.h"
#include "bjcaSDF.h"
#include "sgd_def.h"

void *devHandle = NULL;
void *sessionHandle = NULL;
void *keyHandle = NULL;

const unsigned char encrypt_key_hex[] = "10ad014163a8cc6c864a2f0aa47983ba9b46dac3a3ecfbe9b03344d4bc52c081";

int open_session() {
    int ret = 0;

    //打开设备
    //ret = SDF_OpenDeviceWithPath("./sdt_hsmcrypt.conf", &devHandle);
	ret = SDF_OpenDevice(&devHandle);
    if (ret) {
        printf("SDF_OpenDeviceWithPath error, ret=%08x\n", ret);
        return ret;
    }

    //打开会话
    ret = SDF_OpenSession(devHandle, &sessionHandle);
    if (ret) {
        printf("SDF_OpenSession error, ret=%08x\n", ret);
        SDF_CloseDevice(devHandle);
        return ret;
    }
    return ret;
}

int hex_string_to_char_array(const char *hex, unsigned char *out) {
    size_t hex_len = strlen(hex);
    if (hex_len % 2 != 0) return -1; // 奇数长度报错

    for (size_t i = 0; i < hex_len; i += 2) {
        unsigned int byte;
        if (sscanf(hex + i, "%2X", &byte) != 1) return -1; // 解析失败
        out[i / 2] = (unsigned char) byte;
    }
    return hex_len / 2; // 返回转换后的字节数
}

int import_key() {
    int ret = 0;

    unsigned char encrypt_key[128] = {'\0'};
    const unsigned int key_len = 16;
    const int inner_key_index = 1;
    ret = hex_string_to_char_array(encrypt_key_hex, encrypt_key);
    if (ret < 0) {
        printf("hex_string_to_char_array error, ret=%08x\n", ret);
        return ret;
    }
	printf("encrypt_key:%s\n", encrypt_key);
	for(int i=0; i<32; i++)
	{
		printf("%02x", encrypt_key[i]);
	}
	printf("\n");
	
	ret = SDF_ImportKeyWithKEK(sessionHandle, SGD_SM4_ECB, inner_key_index, encrypt_key, 32, &keyHandle);
	//ret = SDF_ImportKeyWithKEK(sessionHandle, SGD_SM4, inner_key_index, encrypt_key, key_len, &keyHandle);
    if (ret) {
        printf("SDF_ImportKeyWithKEK error, ret=%08x\n", ret);
        return ret;
    }
    return 0;
}

int init() {
    int ret = 0;
    ret = open_session();
    if (ret) {
        printf("open_session error, ret=%08x\n", ret);
        return ret;
    }
    ret = import_key();
    if (ret) {
        printf("import_key error, ret=%08x\n", ret);
        return ret;
    }
    return 0;
}

int close_session() {
    //关闭会话
    int ret = 0;
    ret = SDF_CloseSession(sessionHandle);
    if (ret) {
        printf("SDF_CloseSession error, ret=%08x\n", ret);
        SDF_CloseDevice(devHandle);
        return ret;
    }

    //关闭设备
    ret = SDF_CloseDevice(devHandle);
    if (ret) {
        printf("SDF_CloseDevice error, ret=%08x\n", ret);
        return ret;
    }
    return ret;
}

int destroy() {
    int ret = 0;

    //销毁密钥句柄
    ret = SDF_DestroyKey(sessionHandle, keyHandle);
    if (ret) {
        printf("[ts_symm] SDF_DestroyKey error, ret=%08x\n", ret);
        return ret;
    }

    ret = close_session();
    if (ret) {
        printf("[ts_symm] close_session error, ret=%08x\n", ret);
        return ret;
    }
    return 0;
}

int encrypt_with_sm4_cfb(unsigned char *iv, unsigned char *plaintext, unsigned int plaintext_len,
                         unsigned char *ciphertext,
                         unsigned int *ciphertext_len) {
    // TODO 此处代码已省略
    return 0;
}

unsigned char *transform(const unsigned char *input, size_t in_len) {
    if (in_len < 16) {
        return NULL;
    }

    unsigned char *output = malloc(in_len + 1);
    if (output == NULL) {
        return NULL;
    }
    memcpy(output, input, 8);
    memcpy(output + 8, input + in_len - 8, 8);
    memcpy(output + 8 * 2, input + 8, in_len - 8 * 2);
    output[in_len] = '\0';
    return output;
}

unsigned char *pkcs7_padding(const unsigned char *input, size_t in_len, size_t block_size, size_t *out_len) {
    // TODO 此处代码已省略
    return NULL;
}

const char hex_table[] = "0123456789ABCDEF";

void char_array_to_hex_string(const unsigned char *src, size_t len, char *dst) {
    for (size_t i = 0; i < len; i++) {
        dst[2 * i] = hex_table[(src[i] >> 4) & 0x0F];
        dst[2 * i + 1] = hex_table[src[i] & 0x0F];
    }
    dst[2 * len] = '\0';
}

// 加密地理位置经纬度
int encrypt_geo_location(unsigned char *iv, const unsigned char *location, unsigned int location_len,
                         char *ciphertext_hex,
                         unsigned int *ciphertext_hex_len) {
    int ret = 0;
    unsigned char buffer[1024] = {0};
    // 1. 转换格式
    unsigned char *transformed_location = transform(location, location_len);

    // 2. 填充
    size_t padded_plaintext_len = 0;
    unsigned char *padded_plaintext = pkcs7_padding(transformed_location, location_len, 16, &padded_plaintext_len);
    if (padded_plaintext == NULL) {
        ret = -1;
        printf("pkcs7_padding failed!");
        free(transformed_location);
        return ret;
    }

    // 3. 加密
    unsigned int ciphertext_len = 0;
    ret = encrypt_with_sm4_cfb(iv, padded_plaintext, padded_plaintext_len, buffer, &ciphertext_len);
    if (ret) {
        printf("encrypt error, ret=%08x\n", ret);
        free(transformed_location);
        free(padded_plaintext);
        free(iv);
        return ret;
    }
    char_array_to_hex_string(buffer, ciphertext_len, ciphertext_hex);
    *ciphertext_hex_len = 2 * ciphertext_len;

    // 4. 清理
    free(transformed_location);
    free(padded_plaintext);
    return ret;
}

int main(int argc, char *argv[]) {
    int ret = 0;
    const unsigned char location[] = "xxxxxxxxxxxxxxxxxxxx";
    const unsigned int location_len = sizeof(location) - 1;

    ret = init();
    if (ret) {
        printf("init error, ret=%08x\n", ret);
        return ret;
    }

    char ciphertext_hex[1024] = {0};
    unsigned int ciphertext_hex_len = 0;
    unsigned char iv[] = {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'};

    ret = encrypt_geo_location(iv, location, location_len, ciphertext_hex, &ciphertext_hex_len);
    if (ret) {
        printf("encrypt error, ret=%08x\n", ret);
        return ret;
    }
    printf("encrypt ok\n");
    printf("ciphertext_hex: %s\n", (char *) ciphertext_hex);

    ret = destroy();
    if (ret) {
        printf("destroy error, ret=%08x\n", ret);
    }
    return 0;
}

题目给了sdk密码机接口

密码机是通过国家商密认证的硬件加密设备

提供数据

m1:(113.8383,34.3784)
c1:2F94CDFB99D065B37C5B8A6951417B434C8B21334DCC203438A1C01CCFD59D01

m2:(105.9434,37.9543)
c2:2F94CCFD99D162B87C588A63534D7C437E6F11B8657CCC38F7FF24198A8EC42C

c3:2F94CEFA99DE64B879309669390A3A17F2919D80E57A03033D4D5F4FFC90D8B6C59EEFE11743637F9E0E82CEA6391A149BC52571433B6D5A778CE30EAFD879D6

已知两组明文密文,让我们求c3的内容 一看,给iv了,那我直接看手册调函数不就行了? 翻一下手册看看怎么解密 ![](.\assets\learn-crypto-3\Pasted image 20260630095703.png) 按照参数直接带进去即可

int main(int argc, char *argv[]) {
    int ret = 0;
    const unsigned char location[] = "xxxxxxxxxxxxxxxxxxxx";
    const unsigned int location_len = sizeof(location) - 1;

    ret = init();
    if (ret) {
        printf("init error, ret=%08x\n", ret);
        return ret;
    }

    // char ciphertext_hex[1024] = {0};
    // unsigned int ciphertext_hex_len = 0;
    // unsigned char iv[] = {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'};

    // ret = encrypt_geo_location(iv, location, location_len, ciphertext_hex, &ciphertext_hex_len);
    // if (ret) {
    //     printf("encrypt error, ret=%08x\n", ret);
    //     return ret;
    // }
    // printf("encrypt ok\n");
    // printf("ciphertext_hex: %s\n", (char *) ciphertext_hex);

    // ret = destroy();
    // if (ret) {
    //     printf("destroy error, ret=%08x\n", ret);
    // }

    unsigned char ciphertext[1024] = "2F94CEFA99DE64B879309669390A3A17F2919D80E57A03033D4D5F4FFC90D8B6C59EEFE11743637F9E0E82CEA6391A149BC52571433B6D5A778CE30EAFD879D6";
    unsigned char cipher[1024] = {0};
    hex_string_to_char_array(ciphertext, cipher);
    
    unsigned char iv[] = {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'};
    unsigned char plaintext[1024] = {0};
    unsigned int plaintext_len = 0;
    ret = SDF_Decrypt(sessionHandle, keyHandle,SGD_SM4_CFB, iv, cipher, sizeof(cipher), plaintext, &plaintext_len);
    if (ret) {
        printf("decrypt error, ret=%08x\n", ret);
        return ret;
    }
    printf("decrypt ok\n");
    printf("plaintext: %s\n", (char *) plaintext);
    return 0;
}

但是解出来发现前后缺失 ![](.\assets\learn-crypto-3\Pasted image 20260630100211.png)

再回头观察发现还有两组明密文没有用,然后知道加密模式是CFB模式 ![](.\assets\learn-crypto-3\Pasted image 20260630100709.png) 我们就可以知道第一块的key,又因为前面加密代码中iv的复用的,那么用于加密的数据也是一样的,我们就可以尝试用算出来的key去解第一块,方法也非常简单两个异或一下就行,但是要注意代码中把明文顺序调换了

unsigned char *transform(const unsigned char *input, size_t in_len) {
    if (in_len < 16) {
        return NULL;
    }

    unsigned char *output = malloc(in_len + 1);
    if (output == NULL) {
        return NULL;
    }
    memcpy(output, input, 8);
    memcpy(output + 8, input + in_len - 8, 8);
    memcpy(output + 8 * 2, input + 8, in_len - 8 * 2);
    output[in_len] = '\0';
    return output;
}

然后写一下代码

def xor(a, b):
    return bytes([a[i] ^ b[i] for i in range(16)])

def transform(s):
    a = s[:8]+s[len(s)-8:]+s[8:len(s)-8]
    return a

c1 = "2F94CDFB99D065B37C5B8A6951417B434C8B21334DCC203438A1C01CCFD59D01"
c3 = '2F94CEFA99DE64B879309669390A3A17F2919D80E57A03033D4D5F4FFC90D8B6C59EEFE11743637F9E0E82CEA6391A149BC52571433B6D5A778CE30EAFD879D6'

c1 = bytes.fromhex(c1)
c3 = bytes.fromhex(c3)
m1 = b"(113.8383,34.3784)"
print(transform(m1))
key = xor(c1[:16], transform(m1)[:16])
print(key)
print(xor(key, c3[:16]))

'''
b'(113.83834.3784)3,'
b'\x07\xa5\xfc\xc8\xb7\xe8V\x8bOo\xa4ZfyOj'
b'(122.6236_23_su}'
'''

后八位就是我们要的flag,所以完整flag就是 flag{shangmibei:this_is_a_flag,2026_6_23_su}

插曲

公司密码机只提供了linux的库,然后公司的密码机又需要vpn连接内网,同时公司的vpn又只支持windows没有linux,想了个办法只能通过流量转发,遂记录过程

route -p add 10.10.0.0 mask 255.255.0.0 1.1.1.1 metric 10