出题思路:区块链、公钥RSA、流密码、分组密码
机密性(加密算法)、完整性(消息摘要)、可用性、认证性(认证签名)、不可否认性、
编码基础|HEX:a-f 0-9,考虑ascii解码
编码基础|Base64:A-W a-w 0-9 + / 共64个字符使用4字符表达3字节,不足用0替换,也即是=
import base64
str = "解密字符串" //将数字用!@#等替换
basestr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
newstr = ''
table = ')!@#$%^&*('
for i in str:
if i not in table:
newstr=newstr + i
else
newstr=newstr+(table.index(i))
import base64
print(base.b64decode(newstr))
编码基础|古典密码学:单表代换加密–爆破和词频分析
编码基础|仿射密码:一小串字母数字,有2个以上重复字母
加密函数:E(x) = (ax + b) (mod m),其中 a与b互质,m是编码系统中字母的个数(通常都是26)。
解密函数:D(x) = a^-1(x-b)(mod m),a的逆元可以用pow(a,-1,m)求出,
异或加密|OTP一次性密码本:key长度大于message,一次性key
现代密码|哈希函数:散列算法,单向性、固定长度、雪崩效应—MD5 16字节
flag = 'd0g3{' + hashlib.md5(SECRET).hexdigest() + '}'
broken_flag = 'd0g3{71b2b5616**2a4639**7d979**de964c}'
assert flag[:14] == broken_flag[:14]
assert flag[16:22] == broken_flag[16:22]
assert flag[24:29] == broken_flag[24:29]
ciphier = hashlib.sha256(flag).hexdigest()
print(ciphier)
'''ciphier = '0596d989a2938e16bcc5d6f89ce709ad9f64d36316ab80408cb6b89b3d7f064a'''
//多线程爆破hash串
#!/usr/bin/python
from pwn import pwnlib
from pwnlib.util.iters import mbruteforce
import hashlib
# flag = 'd0g3{71b2b5616**2a4639**7d979**de964c}'
msgbroken = 'd0g3{71b2b5616**2a4639**7d979**de964c}'
table = '0123456789abcdef'
assert len(table) ==16
m1 = 'd0g3{71b2b5616'
m2='2a4639'
m3='7d979'
m4='de964c}'
def f(res):
ciphier = '0596d989a2938e16bcc5d6f89ce709ad9f64d36316ab80408cb6b89b3d7f064a'
msgbroken = m1+res[0:2]+m2+res[2:4]+m3+res[4:6]+m4
tmp = hashlib.sha256(msgbroken.encode("utf-8")).hexdigest()
if tmp ==ciphier:
return True
if __name__ == "__main__":
res = mbruteforce(f,table,6,method='fixed')
print(m1+res[0:2]+m2+res[2:4]+m3+res[4:6]+m4)
现代密码|steam crypto:明文与随机数进行位异或得到密文,下面是随机数产生的一些函数
Pseudorandom Number Generator PNG,伪随机生成器
#Linear Congruential Generator
S0 = seed Si+1 = ASi + B mod m
class LCG() :
def __init__(self, seed) :
self .state = seed
self.m = 2**32
self.A = random.getrandbits(32)| 1
self.B = random.getrandbits(32)| 1
def getbits(self):
self .clock()
return self .state
def clock(self):
self.state = (self.A * self.state + self.B) % self.m
#decrypto
S1 = S0A+B %m
S2 = S1A+B %m
A = (S2-S1)//gmpy2.invert(S1-S0,m)%m
B = S0*A+B%m
example:MT19937
LFSR:liner feedback shift register
P(x) = x^m + Pm-1 * x^(m-1) + ...+p1x+p0
class LFSR:
def_init__ (self, key, taps):
d = max(taps)
assert len(key) == d, "Error: key of wrong size."
self. S = key
self.t=[d-tfortintaps]
def sum(self, L) :
S=0
forxinL:
s^=x
return S
def_ clock(self):
b = self._ s[0]
self. S = self. _s[1:] + [self._ sum(self._ s[p] for p in self._ t)]
return b
def getbit(self):
return self._ clock()
Correlation Attack
现代密码|block crypto:
Feistel结构:加解密相同,仅轮密钥顺序相反输入,round key按轮次与key进行进行function最后得到c m
Substitution-permutation network:每轮keyn与m做异或后产生多个固定大小s,s输入类似hashtable产出下个m,轮n次后输出c
example:AES->输入输出长度固定,加密任意长度明文需要一些额外加工(不够就padding)->加工方式 ECB CBC CFB OFB CTR
ECB:相同明文出现相同密文(攻击:通过剪切达到改变数据目的)
def ecb_encrypto(text):
return text+flag
#通过类似ascii爆破sql的方法,一个s里面的16个byte,只输入填充flag前15个,即放一位flag进来,此时得到加密后c1,这时候填充16个进去,通过遍历第16个的字符判定相等即可得出flag[0],如此反复最后得到整段flag
CBC:每块密文都依赖前面的明文->有初始IV,与m异或后得到m0和key进行function_encrypto,得到的作为IV参与下一轮次的m异或(攻击:字节翻转攻击->通过修改iv和c来控制m)
#encrypto
flag="d0g3{1235346547}"
key = "0123456789abcdef"
iv = flag.strip('d0g3{').strip('}')
hint = os.urandom(4) * 8
msg = b'Welcome to this competition, I hope you can have fun today!!!!!!'
def encrypto(message):
aes = AES.new(key,AES.MODE_CBC,iv)
return aes.encrypt(message)
#解密aes-CBC
#get key
tmp = 56631233292325412205528754798133970783633216936302049893130220461139160682777
hint = int(str(hex(tmp))[2:10] * 8, 16)
key = long_to_bytes(tmp ^ hint)
#get iv
msg = b'Welcome to this competition, I hope you can have fun today!!!!!!'
msgs = [msg[ii:(ii+16)] for ii in range(0,len(msg),16)]
msgs.reverse()
IV = binascii.unhexlify('3c976c92aff4095a23e885b195077b66')
def decry(key,IV,ms):
aes = AES.new(key,AES.MODE_ECB)
return strxor(aes.decrypt(IV),ms)
for ms in msgs:
IV=decry(key,IV,ms)
print(b'd0g{'+IV+b'}')
Padding:PKCS#7–>定义了一种padding方法,要填充n个bytes就填充n个0xn(攻击:padding oracle attack->因为padding错误oracle会报错)
def pad(data):
p=16-len(data)%16
return data + bytes([p]) * p
def unpad(data):
if not all([x == data[-1] for x in data[-data[-1]:]):
raise Va lueError
return data[:-data[-1]]
CTR:产生个随机数字counter1,按顺序到countern,counter1与key参与func_encry运算得到的结果与m1异或,得到第一段密文m1,这样可以输入m马上得到c
GCM:
现代密码|公钥密码:Factoring Integers大数分解 | Discrete Logrithm 离散对数| Elliptic Curves椭圆曲线
RSA:最大公约数GCD:
def gcd(a,b):
while b:
a,b = b, a%b
return a
def gcd(a,b):
if b==0: return a
else
a=gcd(b,a%b)
return a
def egcd(a,b):
if b==0: return (a,1,0)
else:
g,x,y = egcd(b,a%b)
return (g,y,x-(a//b)*y)
a^p = a mod p 推出 a^(q-1) = 1 mod p
由中国剩余定理得到 a^(q-1) = 1 mod p;a^(q-1) = 1 mod q;a^(q-1) = 1 mod n;
c^d = m ^ed = m^kphi+1 =m mod n
RSA|过程分解:密钥生成,公钥加密,私钥解密
密钥生成:选大质数p、q, N=p*q,再选一个符合欧拉函数n的e,这样e必有倒数e-1,可得私钥d=e-1 mod(p-1)(q-1),公钥=(N,e),e大加密慢,e小不安全
公钥加密:明文m,计算密文C=m^e(mod N)
私钥解密:密文C,计算明文m=C^d(mod N)
RSA|基于N分解的RSA几种情况
RSA|共模攻击:相同模数n加密,两个密文c 原理:a * e1+b * e2 = 1,有n,e1,c1,e2,c2,求e1 e2的逆元和公因子
RSA|维纳攻击:e指数特别大,d作为倒数特别小,有n,e,c, phi = (p-1)*(q-1) = pq - (p+q)+1 = n-(pq)+1 => p+q = n-phi+1,连分子计算
RSA|低加密指数攻击(小明文攻击):e一般是65525,当e=3时,可直接破解
如果e=3,且m^en,那么设k,有:
c= m^e +kn
爆破k,如果c−kn能开三次根式,得到m.
RSA|低加密广播攻击:如果选取的加密指数较低,并且使用了相同的加密指数给一个接受者的群发送相同的信息,那么可以进行广播攻击得到明文。n、c不同,明文m,e相同,其e比较小。爆破e后求根,使用中国剩余定理求解
RSA|共享素数:两个n里使用有相同的素数p或q。在CTF中,同样一个e(一般为65537)和m, 有两个或多个n和c时,那么n之间可能是共享素数
RSA|dp泄露:dp=d%(p-1),有n,e,dp,c
RSA|dp dq:dp=d%(p-1) dq=d%(q-1),有p,q,c,dp,dq
RSA|n是p的r次方:n=p^r,有q,n,e,c
RSA|N分解三个素数:n分解出三个素数
RSA|e和phi_n不互素:不互素就没倒数d,需要e除两者最大公约数再互素
RSA|选择密文攻击:
现代密码|离散对数:Diffle-Hellman密钥协商协议,约定大质数p、模p下的生成元g(p,g必互素)
现代密码|离散对数:ElGamal Encryption Scheme
现代密码|离散对数:DLP 群
现代密码|椭圆曲线:三个问题:密钥交换、数字签名、离散对数
攻击 | 需求 | 使用 |
---|---|---|
共模攻击 | 1n(同), 2e, 2c | 同n,求e1 e2的逆元和公因子 |
广播攻击 | 多n, 1e, 多c | 共享素数,即中国剩余定理,gcd(n1,n2)出p,分别求q1q2 |
维纳攻击 | n, e(大), c | e很大,d很小,e/n近似k/d,连分子计算 |
小e攻击 | n, e(小), c | 小明文攻击,通过m^e%n反向得出当e小时,可通过c开根爆破出m |
模(n)不互素 | 2n, 1e, 2c | gcd(n1,n2)出p |
NC不互素 | n, e, c | gcd(n,c)出p |
仅dp泄露 | n, e, c, dp | dp放d位置,计算后删掉dp,重新计算出d填入 |
dp dq泄露 | p, q, c, d, dp, dq | dp dq放入d位置计算,计算n后选e del_f不互素 |
e del_f | p, q, e, c | |
rabin算法 | p, q, n, c, e | |
n是p的r次方 | n, e, c | p位置重复r次填 |
nc不互素:指n与c有最大公约数,这个公约数是与p相关数,通过p可求出q,最后通过题目给出算式得出m
#M = 2021 *m*1001*p
#c = pow(M,e,n) //题目给条件,给c n e
#推理:c = M^e%n = M^e+kn = (2021 *m*1001*p)^e + k*p*q
#两边同时mod p , c%p = (2021 *m*1001*p)^e%p + k*p*q%p
# c % p = 0,所以c=kp,n=qp,c与n存在公因数
import gmpy2
import libnum
n= c= e= 65537
p = gmpy2.gcd(n,c) //求最大公因数
q = n//p
phi_n = (p-1)*(q-1)
d = gmpy2.invert(e,phi_n)
M = pow(c,d,n)
m = M//(2021*1001*p)#需要除掉无关数得到真正的密文m
print(libnum.n2s(int(m)))
#2 题目给c=pow(m*p+n,e,n),同上步骤
#(m*p+n)^e%n=c,两边mod p,c=kp,n c存在公约数,求解出m1,此时m1=m*p+n,因为n=kp,与前面合并即m1=m*p
#m = m1//p
pq的逆元:不给q,p,但是给出p,q的逆元,可以通过推理公式,最后计算长度得知k值,使用脚本联立方程跑出p q
#p = libnum.generate_prime(512)
#q = libnum.generate_prime(512)
#p1 = gmpy2.invert(p,q)
#q1 = gmpy2.invert(q,p)
#c = pow(m,e,n) 题目给p1,q1,phi_n,e,c
#推理:
#p1*p%q=1 p1*p-1=k1q
#q1*q%p=1 q1*q-1=k2p
#上下两方程相乘 (p1*p-1)*(q1*q-1)=kpq
# p1*q1*n+1-p1p-q1q=kn
# p1p+q1q-1=kn ,此时由于给出pq长度,pq=n,所以n长度不超过1024,推出k=1
#联立phi=(q-1)*(p-1),p1p+q1q-1=n,可得pq
#解题
import gmpy2
import libnum
from z3 import *
e= 65537 phi= c= p1= q1=
s = Solver()
p=Int("p")
q=Int("q")
s.add(phi==(q-1)*(p-1)) #联立方程组
s.add(p1*p+q1*q-1==p*q)
if s.check()==sat:
print(s.model())
p =
q =
n = q*p
d = gmpy2.invert(e, phi)
m = pow(c,int(d),int(n))
print(libnum.n2s(int(m)))
费马小定理运用:a^p=a mod p
#hint = pow(2020*p+2021, q, n) 题目给出n c hint
#推理:h=(2020*p+2021)^q%n=(2020*p+2021)^q+kn=k1p^q+2021^q+k2n
#两边mod p h%p=2021^q%p
# 2021^q%p-h=kp(p与n有倍数关系,即n与2021^p-h存在公约数,可以用gcd,但q不知道,需要替换)
#由费马小定理,a^p=a mod p 得,(2021^q)^p%p-h=kp 可推2021^q%p-h=kp,所以可证2021^n%p=2021^q%p
#最后得2021^n%p-h=kp,n与2021^n-h存在公因数,用gcd求解出p
import libnum
import gmpy2
n=
c=
hint=
e = 65537
p = gmpy2.gcd(pow(2021,n,n)-hint,n)
#原本是gcd(2021^n-hint,n),但是太大无法计算,等化为2021^n%n,即pow(2021,n,n)
q = n//p
print(q,p)
phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
m = pow(c,d,n)
print(libnum.n2s(int(m)))
pq在两条不同公式,通过消去得到n替代
# h1=pow(2022*p+2021*q,1919,n)
# h2=pow(2021*p+2022*q,9191,n) 题目给出条件,p q在通过h1 h2给出
# c=pow(m, e ,n)
h1= h2= n= c= e=65537
#推理
# h1 = 2022*p+2021*q ^ 1919 %n
# h2 = 2021*p+2022*q ^ 9191 %n
#两边同乘,直到右边相等,相减消去
# h1^9191*2021^(9191*1919)%q = 2022^(9191*1919)*p^(1919*9191)*2021^(9191*1919)
# h2^1919*2022^(9191*1919)%q = 2021^(9191*1919)*p^(9191*1919)*2022^(9191*1919)
# h2^1919*2022^(9191*1919)-h1^9191*2021^(9191*1919)
#存在公约数
# h2^1919 *p^ 9191*1919-h1^9191 =kn
#数字太大,需要通过pow减小
h3 = pow(h2,1919,n)*pow(2022,9191*1919,n)-pow(h1,9191,n)*pow(2021,9191*1919,n)
p = gmpy2.gcd(h3,n)
q = n//p
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
print(libnum.n2s(int(m)))
二项式:(n+1)^p = (kpn+1)
# g=n+1
# c= (pow(g,p,n*n) *pow (m,n,n*n))%(n*n)
# print("c="+str(c))
# print("n="+str(n))
# print("hint="+str(pow(m,n,n*n))) 题目给出条件
#推理:c = [(g^p)%n*n * (m^n)%n*n]%(n*n) = (g^p * m^n)%(n*n) =(n+1)^p%(n*n)*hint
# c + knn = (kpn+1)*hint = kpn*hint+hint
# c-hint = kpn*hint + knn
# (c-hint) // n = kp*hint + kn
#两边同时mod p, (c-hint) // n =kp ,[(c-hint)//n]与n存在公约数
p = gmpy2.gcd((c-hint)//n,n)
print(p)
q = n//p
phi = (p-1)*(q-1)
d = gmpy2.invert(n,phi)
m = pow(c,d,n)
print(libnum.n2s(int(m)))
pq接近但无c:通过c的其他表达替换解出c,pq接近证明可用yafu分解
#给e n c1 c2
#解析:n扔yafu解出p q, c1=c+kp c2=c+kq
# c1=c2+kp+kq (c1-c2)%q=kp 两边同除p,即pq逆元p1,(c1-c2)*p1%q = k
#即c = c1-(c1-c2)*p1%q
p1 = gmpy2.invert(p,q)
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c1-(c1-c2)*p1%q, d, n)
print(libnum.n2s(int(m)))
共享素数(广播攻击):同素数,直接gcd(n1,n2),p是共同的素数,后面就按照题目逻辑继续
e = n1 = c1 = n2 = c2 =
q = gmpy2.gcd(n1,n2)
p = n1//q
d = gmpy2.invert(e,(q-1)*(p-1))
m = libnum.n2s(int(pow(c1,d,n1)))
print(m)
小明文攻击(e很小):题目只给c n,通过m^e%n反向得出当e小时,可通过c开根爆破出m
题目只给了出
e=2 c= n=
import libnum
import gmpy2
for i in range(2**6):
if i ==0:
continue
if gmpy2.iroot(c,i)[1]==True: #开根
print(gmpy2.iroot(c,i)[0])
# print(libnum.n2s(int(29305044677847256883031643626546437461373017758852477))) #明文转字符
共模攻击:n相同,求e1 e2逆元再用公因子求根
#e1e2 = 3087 flag1 = pow(m1,e1,n) flag2 = pow(m1,e2,n)
#推理:给了e1*e2,和c1 c2,爆破拆散e1e2组合,然后分别放入等式
#c1 = m1^e1%n ,c2 = m1^e2%n ,两边同时开方e1 e2的逆元k1 k2,让右边等于m1
#c1^k1%n= m1,c2^k2%n=m1,等式相乘后对结果用公因子求根,m = gmpy2.iroot(m0,k)[0]
import libnum
import gmpy2
flag1= flag2= n= e1e2=3087
for e1 in range(1, e1e2 + 1):
if e1e2 % e1 == 0:
e2 = e1e2 // e1
k ,k1,k2= gmpy2.gcdext(e1,e2) #k1 k2此时是逆元,即求d
m0 = pow(flag1,k1,n)*pow(flag2,k2,n)%n
m = gmpy2.iroot(m0,k)[0]
print(libnum.n2s(int(m)))
相邻素数:有next_prime必能分解,分解出pq观察代码,c1 c2有两条公式组成,上z3,出原文
p = sympy.nextprime(secret_num)
q = sympy.nextprime(p)
n=pq
c1 = F1 + F2
c2 = pow(F1, 3) + pow(F2, 3)
assert(c2 < N)
m1 = pow(c1, e, N)
m2 = pow(c2, e, N)
#提供n,e,m1,m2
#解题
s = Solver()
f1=Int("f1")
f2=Int("f2")
s.add((f1+f2)==int(c1))
s.add((f1**3)+(f2**3)==int(c2))
if s.check()==sat:
print(s.model())
n分解出3个素数:有多余的,需要n后面除掉
#初始化n和phi。遍历3素数,查看e与哪个互素,是就乘进去n和phi,然后正常流程
import libnum
import gmpy2
from z3 import *
n = 3454083680130687060405946528826790951695785465926614724373
e = 3
c = 1347530713288996422676156069761604101177635382955634367208
p=11761833764528579549
q=17100682436035561357
r3=17172929050033177661
n, phi = 1, 1 #初始化n phi,也可以除掉不互素的
for i in [p,q,r3]:
if not gmpy2.gcd(e, i - 1) - 1:
phi *= i - 1
n *= i
#或 phi = (p-1)*(r3-1) n=n//q
d = gmpy2.invert(e, phi)
flag = pow(c, d, n)
print(libnum.n2s(int(flag)))
爆破公式和e
# A=(((y%x)**5)%(x%y))**2019+y**316+(y+1)/x
# p=next_prime(z*x*y)
# q=next_prime(z) 题目条件,给出n A c
#解析:没有pq,试试扔工具拆分,不行就用A等式爆破x y,爆破后n=z^2*x*y,n开方后即得到z,在附近找素数
#题目给的n可以拆分,所以直接出pq,然后爆破e
# print(p//q)
# for x in range(1,1000):
# for y in range(1,1000):
# try:
# if ((((y%x)**5)%(x%y))**2019+y**316+(y+1)//x==A):
# print(x,y)
# except:
# continue
x = 2 y = 83
for e in range(1,2^50):
if gmpy2.is_prime(e)==True:
try:
d = gmpy2.invert(e, (p-1)*(q-1))
flag = pow(c,d,n)
if b'CTF{' in libnum.n2s(int(flag)):
print(libnum.n2s(int(flag)))
except:
continue
p高位泄露:有p>>200,只剩高位
p3 = getPrime(512)
q3 = getPrime(512)
N3 = p3*q3
给e n3 p3>>200 c
#解析,上sage,打开sage notebook
from sage.all import *
c3 = n3 = p3_200 =
pbits = 512
kbits = pbits - p3_200.nbits()
p3 = p3_200 << 200
print(p3.nbits())
PR. = PolynomialRing(Zmod(n3)) # 构建多项式环
f = x + p3 # 定义函数
roots = f.small_roots(X = 2^kbits,beta = 0.4) #直接解出低位
if roots:
print(roots[0])
p = p3 + int(roots[0])
print("n3: "+str(n3))
print("p: "+str(p))
print("q: "+str(n3//p))
密钥加密及打开
import libnum
import gmpy2
from z3 import *
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import base64
from Crypto.Cipher import PKCS1_OAEP
p=
q=
with open("public.key","rb") as f:
key = RSA.import_key(f.read())
n=key.n
e=key.e
print(n,e)
with open("flag.enc","rb") as f:
c=f.read()
c=base64.b64decode(c)
c=libnum.s2n(c)
print(c)
d = gmpy2.invert(e, (p-1)*(q-1))
# flag = pow(c1, d, n2)
rsa_components = (n, e, int(d), p, q)
arsa = RSA.construct(rsa_components)
rsakey = RSA.importKey(arsa.exportKey())
rsakey = PKCS1_OAEP.new(rsakey)
decrypted = rsakey.decrypt(libnum.n2s(c))
print(decrypted)