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了,那我直接看手册调函数不就行了? 翻一下手册看看怎么解密  按照参数直接带进去即可
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;
}
但是解出来发现前后缺失 
再回头观察发现还有两组明密文没有用,然后知道加密模式是CFB模式  我们就可以知道第一块的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