wubba lubba dub dub.

CVE-2021-3711漏洞解析

  • 这个文件位置在1.1.1不同的版本位置可能也有变化,具体可以gpt或者官方文档看看这里示例的是)

在crypto/pkcs7/pk7_doit.c,有这个函数,是对被包装的密文进行解密的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen,
PKCS7_RECIP_INFO *ri, EVP_PKEY *pkey)
{
EVP_PKEY_CTX *pctx = NULL;
unsigned char *ek = NULL;
size_t eklen;

int ret = -1;

pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!pctx)
return -1;

if (EVP_PKEY_decrypt_init(pctx) <= 0)
goto err;

if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DECRYPT,
EVP_PKEY_CTRL_PKCS7_DECRYPT, 0, ri) <= 0) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, PKCS7_R_CTRL_ERROR);
goto err;
}

if (EVP_PKEY_decrypt(pctx, NULL, &eklen,
ri->enc_key->data, ri->enc_key->length) <= 0)
goto err;

ek = OPENSSL_malloc(eklen);

if (ek == NULL) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, ERR_R_MALLOC_FAILURE);
goto err;
}

if (EVP_PKEY_decrypt(pctx, ek, &eklen,
ri->enc_key->data, ri->enc_key->length) <= 0) {
ret = 0;
PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, ERR_R_EVP_LIB);
goto err;
}

ret = 1;

OPENSSL_clear_free(*pek, *peklen);
*pek = ek;
*peklen = eklen;

err:
EVP_PKEY_CTX_free(pctx);
if (!ret)
OPENSSL_free(ek);

return ret;
}

EVP_PKEY_decrypt()是解密函数,根据传入内容会自行判断解析结构体内的哪个解密函数,里面就包含SM2的解密函数。

我们最开始也不知道outlen有多长,这个outlen预先分配给密文的缓冲区长度。是如何分配的呢,我们得先跳转一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int pkey_sm2_decrypt(EVP_PKEY_CTX *ctx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen)
{
EC_KEY *ec = ctx->pkey->pkey.ec;
SM2_PKEY_CTX *dctx = ctx->data;
const EVP_MD *md = (dctx->md == NULL) ? EVP_sm3() : dctx->md;

if (out == NULL) {
if (!sm2_plaintext_size(ec, md, inlen, outlen))
return -1;
else
return 1;
}

return sm2_decrypt(ec, md, in, inlen, out, outlen);
}

static int pkey_sm2_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)

第一次传入out=NULL,使用sm2_plaintext_size(ec, md, inlen, outlen)函数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int sm2_plaintext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
size_t *pt_size)

{
const size_t field_size = ec_field_size(EC_KEY_get0_group(key));
const int md_size = EVP_MD_size(digest);
size_t overhead;

if (md_size < 0) {
SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_DIGEST);
return 0;
}
if (field_size == 0) {
SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_FIELD);
return 0;
}

overhead = 10 + 2 * field_size + (size_t)md_size;
if (msg_len <= overhead) {
SM2err(SM2_F_SM2_PLAINTEXT_SIZE, SM2_R_INVALID_ENCODING);
return 0;
}

*pt_size = msg_len - overhead;
return 1;
}
阅读此文
post @ 2025-07-06

Openssl使用-1

这一章介绍一些基础的命令。openssl用的地方有点多,分两张写

openssl的安装和配置

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

下载地址

使用这里是别人配置好的安装包

下好之后点击安装,之后就傻瓜式点点点。

  • 配置环境变量
阅读此文
post @ 2025-05-30

群论

群基础

群的基本定义

  1. 代数系: 设S表示一个非空集合,那么$S * S->S$ 的映射叫做S的结合法或运算$SS->S$, $(a,b)->ab$ 其中a,b∈S集合(这里的号不是乘法,只是一种计算方式)如果这个S满足封闭性(任意上述a*b∈S)。

这样的S叫做一个代数系,当然这只是为了方便我们理解群的性质

  1. 半群:如果这个S满足结合律。

    结合律: (ab)c=a(bc)

    这个S就称为半群

  2. 含幺半群:

这里我们需要理解一个很重要的概念,幺元。

设非空集合S,存在S上的一个二元运算"." ,对于元素$e∈S$,若对于∀a∈S,都有$ea=a$ ,则称e为S的左幺元,同理∀a∈S,都有ae=a,则称为S的右幺元,若e既是左幺元又是右幺元$(ea=ae=a)$,则称e为S的幺元(单位元)。

幺元(单位元):通常记为e,设a∈S,$ae=ea=a$, 有幺元的半群叫做含幺半群

阅读此文
post @ 2025-05-25

LitCTF

ez_math

简单的矩阵rsa,原理见论文

  • https://www.gcsu.edu/sites/files/page-assets/node-808/attachments/pangia.pdf#:~:text=We%20propose%20a%20variation%20on%20the%20RSA%20Cryptosystem%3A,methods%20to%20matrix%20values%20in%20addition%20to%20scalars.

​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from sage.all import *
from Crypto.Util.number import *
from uuid import uuid4

flag = b'LitCTF{'+ str(uuid4()).encode() + b'}'
flag = bytes_to_long(flag)
len_flag = flag.bit_length()
e = 65537
p = getPrime(512)
P = GF(p)
A = [[flag, getPrime(len_flag)],
[getPrime(len_flag), getPrime(len_flag)]]
A = matrix(P, A)
B = A ** e

print(f"e = {e}")
print(f"p = {p}")
print(f"B = {list(B)}".replace('(', '[').replace(')', ']'))

# e = 65537
# p = 8147594556101158967571180945694180896742294483544853070485096002084187305007965554901340220135102394516080775084644243545680089670612459698730714507241869
# B = [[2155477851953408309667286450183162647077775173298899672730310990871751073331268840697064969968224381692698267285466913831393859280698670494293432275120170, 4113196339199671283644050914377933292797783829068402678379946926727565560805246629977929420627263995348168282358929186302526949449679561299204123214741547], [3652128051559825585352835887172797117251184204957364197630337114276860638429451378581133662832585442502338145987792778148110514594776496633267082169998598, 2475627430652911131017666156879485088601207383028954405788583206976605890994185119936790889665919339591067412273564551745588770370229650653217822472440992]]

​ 解题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Util.number import *
e = 65537
p = 8147594556101158967571180945694180896742294483544853070485096002084187305007965554901340220135102394516080775084644243545680089670612459698730714507241869
P=GF(p)
B = Matrix(P,[[2155477851953408309667286450183162647077775173298899672730310990871751073331268840697064969968224381692698267285466913831393859280698670494293432275120170, 4113196339199671283644050914377933292797783829068402678379946926727565560805246629977929420627263995348168282358929186302526949449679561299204123214741547], [3652128051559825585352835887172797117251184204957364197630337114276860638429451378581133662832585442502338145987792778148110514594776496633267082169998598, 2475627430652911131017666156879485088601207383028954405788583206976605890994185119936790889665919339591067412273564551745588770370229650653217822472440992]])

gp=(p^2-p)*(p^2-1)
d = int(inverse(e,gp))
print(d)
M=B^d
print(M)
m=int(M[0][0])
print(long_to_bytes(m))

baby

阅读此文
post @ 2025-03-30

梅森旋转生成随机数的逆向预测和恢复

  • 预测下一个随机数,恢复被隐藏的随机数据

首发于先知社区 :子集和问题的两种解决方式-先知社区

算法过程

  • wiki上有算法实现的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def _int32(x):
return int(0xFFFFFFFF & x)

class MT19937:
def __init__(self, seed):
self.mt = [0] * 624
self.mt[0] = seed
self.mti = 0
for i in range(1, 624):
self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)


def extract_number(self,x=None):
if self.mti == 0 and x is None:
self.twist()
y = self.mt[self.mti]
y = y ^ y >> 11
y = y ^ y << 7 & 2636928640
y = y ^ y << 15 & 4022730752
y = y ^ y >> 18
self.mti = (self.mti + 1) % 624
return _int32(y)

def twist(self):
for i in range(0, 624):
y = _int32((self.mt[i] & 0x80000000) + (self.mt[(i + 1) % 624] & 0x7fffffff))
self.mt[i] = (y >> 1) ^ self.mt[(i + 397) % 624]

if y % 2 != 0:
self.mt[i] = self.mt[i] ^ 0x9908b0df

根据代码分析过程。因为社区已经有人讲过很详细的过程,我们这里就只说一下大致分为三个过程

  • 初始化

根据种子seed,生成初始状态mt(共有624个数据。然后mti==0.

阅读此文
post @ 2025-03-05

hgame week1

sieve

题目代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#sage
from Crypto.Util.number import bytes_to_long
from sympy import nextprime

FLAG = b'hgame{xxxxxxxxxxxxxxxxxxxxxx}'
m = bytes_to_long(FLAG)

def trick(k):
if k > 1:
mul = prod(range(1,k))
if k - mul % k - 1 == 0:
return euler_phi(k) + trick(k-1) + 1
else:
return euler_phi(k) + trick(k-1)
else:
return 1

e = 65537
p = q = nextprime(trick(e^2//6)<<128)
n = p * q
enc = pow(m,e,n)
print(f'{enc=}')
#enc=2449294097474714136530140099784592732766444481665278038069484466665506153967851063209402336025065476172617376546

$k - mul (mod k) - 1 == 0$ 这里其实是判断k是否是素数

根据威尔逊定理:如果p是一个奇素数或P=2,那么有

$(p-1)!=-1(mod p)$

证明:

设x和它的逆元x-1 如果他们相等则有 $x^2=1 mod p--->(x-1)(x+1)=0mod p$

这是x只能等于p-1或1

阅读此文
post @ 2025-02-23

背包密码和多维子集和问题(Multidimensional Subset Sum Problem。)

  • 知识:LLL算法,以及BKZ解决方法,还有MITM(中间相遇算法)算法解决。

首发于先知社区 :子集和问题的两种解决方式-先知社区

简单介绍一些这里所称的背包问题,以子集和问题(Subset Sum Problem)

  • 背包问题

$W=x_1a_1+x_2a_2+...+xna_n$

W表示背包的承重,x只能位0或1(这里是0/1背包,完全背包感兴趣可以了解这里仅详细阐述0-1背包),用来表示选中或不选中。

这种0-1背包问题也叫做子集和问题,给定一个集合,$A={a1,a2,a3...an}$

它的部分元素的的和等于W,所以叫做子集和。如果不采取任何取巧的方式暴力破解这个问题,实践复杂度位$o(2^n)$,如果n较大这是十分困难的

Merkle–Hellman公钥加密算法(这种加密算法以及不再安全)

  • 原理:虽然单纯的背包破解十分复杂,但是如果是超递增背包就能极大降低难度,我们设定初始背包为超递增背包,再利用模数m和乘数w对其进行加密

超递增背包

阅读此文
post @ 2025-01-23

coppersmith

原论文ch19.pdf

借鉴博客:https://jayxv.github.io/2020/08/13/%E5%AF%86%E7%A0%81%E5%AD%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Bcoppersmith/

  • 前言

最近经常遇到coppersmith攻击,所以决定还是有必要深入学习一下

coppersmith'Method

介绍:coppersmith方法基于格约简和LLL算法来找到一定模数下多项式的小根。其核心思想是将求解模多项式方程的问题转化为一个格中的短向量问题

例如F$F(x)=x^3+x+123(mod M)$,M=77,假设存在x0使得$F(x_0)=0$

并且这个x0小于某个特定值,就可以使用coppersmith算法

给定模M的多项式$F(x)=x^d+a^{d−1}x_{d−1}+⋯+a^1x+a^0$,[必须让$x^d$满足系数为1,可以乘$a_d^{-1}$来配凑。如果ad没有模M逆元,可以拆分为多组。

假设我们知道$F(x_0)=0(mod M)$ ,并且|$x_0$<$M^{1/d}$

假设我们现在能找到G($x_0$)=0,不需要取模,我们可以用coppersmith方法把这个F(x)变为G(x)

例如$M=17*19=323$,$F(x)=x^2+33x+215,$,假设一个小根$x_0=3$但是在整数

阅读此文
post @ 2025-01-09

DASCTF(2024楚慧杯原题杯)的一道抽象lcg做题过程

一些吐槽

为什么翻遍全网都没有这道题wp,对于真菜的我,手搓有点难泵

做题过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
from random import *
from secret import flag

LENGTH = 512
RATIO = 0.02024
M = 2**LENGTH
a, b, seed = getPrime(LENGTH), getPrime(LENGTH), getPrime(LENGTH)
SEED = seed


x = []
for i in range(64):
seed = (a*seed + b) % M
x.append("".join([bin(seed)[2:].zfill(LENGTH)[i] if uniform(0,1) < RATIO else "*" for i in range(LENGTH)]))


print("c =", bytes_to_long(flag) ^ SEED)
print("a =",a)
print("b =",b)
print("x =",x)

尝试模拟一个类似情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *
from random import *
a = 7048435472566573813031570507837890091364947084306630050544242220147807292350445564322172244244726206563452305566866223414437853917448623276909090327076693
b = 9204853069421046007176344891235245198607052139715825810823076231566533652655127030214860066312526149219510111657539481375881111759200483396551737326166933
f=b'flag{yoxi_nidi_liangmin}'
m=bytes_to_long(f)
M=2**512
seed=8656702749867102422219200570484898209691959687594188444284921871713214793286935518322058853234901407306874127174670521978953484935737586264798208150365287
RATIO=0.6
Seed=seed
print(seed)
x=[]
for i in range(3):
seed = (a * seed + b) % M
print(seed)
x.append("".join([bin(seed)[2:].zfill(512)[i] if uniform(0,1) < RATIO else "*" for i in range(512)]))
print(seed)
print(x)
c=m^Seed
print(c)

我把显示数据百分比调高了一点,因为测试如果太低就是出现错误结果。这行代码加密逻辑跟原题一样。过程也很简洁

我们使用z3约束。由于是才学的z3有的,写的时候也有些问题,比如定义

阅读此文
post @ 2024-12-29

DSA签名算法

DSA算法简介

DSA(Digital Signature Algorithm)是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard) 数字签名的标准。

DSA是一种更高级的验证方式,它是一种公开密钥算法,不能用来加密数据,一般用于数字签名和认证。DSA 不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性,包括数据的完整性以及数据发送者的身份。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。

DSA数字签名可以理解为是单向加密的升级,不仅校验数据完整性,还校验发送者身份,同时还由于使用了非对称的密钥来保证密钥的安全,所以相比消息摘要算法更安全。

DSA只是一种算法,和RSA不同之处在于它不能用作加密和解密,也不能进行密钥交换,只用于签名,它比RSA要快很多。

DSA算法签名的过程

  1. 使用消息摘要算法(例如sha-256/md5)将要发送数据加密生成信息摘要。
  2. 发送方用自己的DSA私钥对信息摘要再加密,形成数字签名。
  3. 将原报文和加密后的数字签名一并通过互联网传给接收方。
  4. 接收方用发送方的公钥对数字签名进行解密,同时对收到的数据用消息摘要算法产生同一信息摘要。
  5. 将解密后的信息摘要和收到的数据在接收方重新加密产生的摘要进行比对校验,如果两者一致,则说明在传送过程中信息没有破坏和篡改;否则,则说明信息已经失去安全性和保密性。

算法原理

阅读此文
⬆︎TOP