同步于博客:http://dragonliu.tk/
0x00 前期准备
01 基础知识
02 Ubuntu 安装gmpy2模块
PARI/GP是一个比较强大的数论库,“针对数论中的快速计算(大数分解,代数数论,椭圆曲线…)而设计”。
需要的依赖库 gmp mpfr mpc
gmp 库安装
sudo apt-get install libgmp-dev
mpfr 库安装
sudo apt-get install libmpfr-dev
mpc 库安装
sudo apt-get install libmpc-dev
gmpy2 安装
#python3
sudo pip3 install gmpy2
#python2
sudo pip install gmpy2
0x01 已知n、e、c,求m
01 思路
- 利用 http://factordb.com/ 分解n获得p和q;
- 计算d;
- 解密得明文m。
02 代码
#!/usr/bin/env python
# coding=utf-8
import gmpy2
#分解n得p,q
p = gmpy2.mpz(...) #填写p
q = gmpy2.mpz(...) #填写q
e = gmpy2.mpz(...) #填写e
c = gmpy2.mpz(...) #填写c
#计算d
phi_n = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n) #求解模逆元
print "private key:"
print d
#求明文
print "plaintext:"
M = pow(c, d, p*q)
print '[10进制]: ' + str(M)
flag = str( hex(M) )[2:] #[2:-1]
print '[16进制]: ' + flag
print '[ASCII码]: ' + flag.decode('hex')
0x02 低加密指数分解攻击(e = 1)
01 思路
- 加密过程: c ≡ E ( m ) ≡ m e ( m o d n ) ≡ m ( m o d n ) c \equiv E(m) \equiv m^{e}(\bmod n) \equiv m(\bmod n) c≡E(m)≡me(modn)≡m(modn),所以明文与密文模n同余;
- m = c + n ∗ k ( k = 0 , 1 , 2 , 3... ) m = c + n*k (k=0,1,2,3...) m=c+n∗k(k=0,1,2,3...),暴力破解即可。
02 代码
#!/usr/bin/env python
# coding=utf-8
import libnum
n = ... #填写n
c = ... #填写c
max_num = 6 #遍历上限
for k in range( max_num ):
m = c + n*k
print libnum.n2s(m)
0x03 Rabin加密(e=2)
01 思路
理论知识:我跳
02 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import libnum
import gmpy2
#从文件中读取密文
f = open('flag.enc','r')
c = f.read()
c = libnum.s2n(c)#字符串转数字
#分解n
p = ... #填写p
q = ... #填写p
n = p * q
#求解mp与mq
mp = pow(c, (p+1)/4, p)
mq = pow(c, (q+1)/4, q)
#求解yp与yq
yp = gmpy2.invert(p, q)
yq = gmpy2.invert(q, p)
#获得四个解
r1 = (yp*p*mq + yq*q*mp) % n
r2 = (-r1) % n
r3 = (yp*p*mq - yq*q*mp) % n
r4 = (-r3) % n
print libnum.n2s( r1 )#数字转字符串
print libnum.n2s( r2 )
print libnum.n2s( r3 )
print libnum.n2s( r4 )
当 p = q p = q p=q时,使用python命令行将16进制转十进制,然后直接求解 c c c模 n 时的平方根:
n = ...;
c = ...;
PowerMod[c, 1/2, n]
Wolfram 语言在线编辑:我跳
0x04 flag.enc + pubkey.pem
01 思路
- 解压得到两个文件【flag.enc】和【pubkey.pem】,其中【flag.enc】从文件名含有flag可以判断是加密后的密文,【pubkey.pem】是公钥文件,通过公钥文件可以得到e和n;
- 通过openssl对公钥文件【pubkey.pem】进行分解,使用命令【openssl rsa -pubin -text -modulus -in warmup -in pubkey.pem】,得到 e【Exponent】和 n【Modulus】;
- 其他根据类型判断。
02 代码
#!/usr/bin/env python
# coding=utf-8
import gmpy2
import rsa
#分解n得p,q
n = ... #填写n
p = ... #填写p
q = ... #填写q
e = ... #填写e
#计算私钥
phi_n = (p - 1) * (q - 1)
d = int( gmpy2.invert(e, phi_n) ) #求解模逆元
privatekey = rsa.PrivateKey(n , e , d , p , q) #根据已知参数,计算私钥
with open("./flag.enc" , "rb") as f: #填写文件
print rsa.decrypt(f.read(), privatekey).decode() #使用私钥对密文进行解密,并打印
0x05 共模攻击
01 思路
用相同的N,不同的e进行加密的,可以使用共模攻击。
02 代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from libnum import n2s, s2n
from gmpy2 import invert, gcdext
n = ... #填写n
e1 = ... #填写e1
e2 = ... #填写e2
s = gcdext(e1, e2)
s1 = s[1]
s2 = -s[2]
#读取密文
file1 = open('flag.enc1', 'r')
c1 = file1.read()
c1 = s2n(c1)#字符串转数字
file2 = open('flag.enc2', 'r')
c2 = file2.read()
c2 = s2n(c2)#字符串转数字
c2 = invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 , s2 , n)) % n
print n2s(m)
0x06 低加密指数分解攻击(e = 3)
01 思路
- 公钥中,e=3,N非常大。
- 加密过程: c ≡ E ( m ) ≡ m e ( m o d n ) ≡ m 3 ( m o d n ) c \equiv E(m) \equiv m^{e}(\bmod n) \equiv m^3(\bmod n) c≡E(m)≡me(modn)≡m3(modn),所以明文与密文的3次方模n同余;
- m = c + n ∗ k ( k = 0 , 1 , 2 , 3... ) m = c + n*k (k=0,1,2,3...) m=c+n∗k(k=0,1,2,3...),然后开三次方,暴力破解即可。
02 代码
#!/usr/bin/env python
# coding=utf-8
from libnum import s2n, n2s
from gmpy2 import iroot
n = ... #填写n
e = 3
#读取密文
file = open('flag.enc', 'r')
c = file.read()
c = s2n(c)#字符串转数字
file.close()
i = 0
while True:
res = iroot( (c + i*n), 3 )
if( res[1] == True):
print res
break
print "i = " + str(i)
i += 1
m = ... #暴力获得的m
print n2s(m)
0x07 私钥修复+最优非对称加密填充(God Like RSA)
01 思路
压缩包里有一个密文,一个部分缺失的私钥,一个公钥,读公钥可知 N 是 4096 位的,分解无望,肯定要从私钥着手。
- 【vscode】打开【private.corrupted】,将对应变量填入下列脚本;
- 执行脚本后得到私钥,新建文件【private.pem】并将私钥复制进去;
- 然后执行【最优非对称加密填充】脚本。
02 代码
私钥修复脚本
#!/usr/bin/python
#-*- coding:utf-8 -*-
import re
import pickle
from itertools import product
from libnum import invmod, gcd
def solve_linear(a, b, mod):
if a & 1 == 0 or b & 1 == 0:
return None
return (b * invmod(a, mod)) & (mod - 1) # hack for mod = power of 2
def to_n(s):
s = re.sub(r"[^0-9a-f]", "", s)
return int(s, 16)
def msk(s):
cleaned = "".join(map(lambda x: x[-2:], s.split(":")))
return msk_ranges(cleaned), msk_mask(cleaned), msk_val(cleaned)
def msk_ranges(s):
return [range(16) if c == " " else [int(c, 16)] for c in s]
def msk_mask(s):
return int("".join("0" if c == " " else "f" for c in s), 16)
def msk_val(s):
return int("".join("0" if c == " " else c for c in s), 16)
#根据文件pubilc.pem得到
E = ...
N = to_n("""...""")
#private.corrupted中的prime1
p_ranges, pmask_msk, pmask_val = msk(""" ... """)
#prime2
q_ranges, qmask_msk, qmask_val = msk(""" ... """)
#privateExponent
_, dmask_msk, dmask_val = msk(""" ... """)
#exponent1
_, dpmask_msk, dpmask_val = msk(""" ... """)
#exponent2
_, dqmask_msk, dqmask_val = msk(""" ... """)
def search(K, Kp, Kq, check_level, break_step):
max_step = 0
cands = [0]
for step in range(1, break_step + 1):
#print " ", step, "( max =", max_step, ")"
max_step = max(step, max_step)
mod = 1 << (4 * step)
mask = mod - 1
cands_next = []
for p, new_digit in product(cands, p_ranges[-step]):
pval = (new_digit << ((step - 1) * 4)) | p
if check_level >= 1:
qval = solve_linear(pval, N & mask, mod)
if qval is None or not check_val(qval, mask, qmask_msk, qmask_val):
continue
if check_level >= 2:
val = solve_linear(E, 1 + K * (N - pval - qval + 1), mod)
if val is None or not check_val(val, mask, dmask_msk, dmask_val):
continue
if check_level >= 3:
val = solve_linear(E, 1 + Kp * (pval - 1), mod)
if val is None or not check_val(val, mask, dpmask_msk, dpmask_val):
continue
if check_level >= 4:
val = solve_linear(E, 1 + Kq * (qval - 1), mod)
if val is None or not check_val(val, mask, dqmask_msk, dqmask_val):
continue
if pval * qval == N:
print "Kq =", Kq
print "pwned"
print "p =", pval
print "q =", qval
p = pval
q = qval
d = invmod(E, (p - 1) * (q - 1))
coef = invmod(p, q)
from Crypto.PublicKey import RSA
print RSA.construct(map(long, (N, E, d, p, q, coef))).exportKey()
quit()
cands_next.append(pval)
if not cands_next:
return False
cands = cands_next
return True
def check_val(val, mask, mask_msk, mask_val):
test_mask = mask_msk & mask
test_val = mask_val & mask
return val & test_mask == test_val
for K in range(1, E):
if K % 100 == 0:
print "checking", K
if search(K, 0, 0, check_level=2, break_step=20):
print "K =", K
break
for Kp in range(1, E):
if Kp % 1000 == 0:
print "checking", Kp
if search(K, Kp, 0, check_level=3, break_step=30):
print "Kp =", Kp
break
for Kq in range(1, E):
if Kq % 100 == 0:
print "checking", Kq
if search(K, Kp, Kq, check_level=4, break_step=9999):
print "Kq =", Kq
break
解题脚本(最优非对称加密填充)
#!/usr/bin/python
# coding=utf-8
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
with open('pubkey.pem', 'r') as f:
key = RSA.importKey(f)
N = key.n
e = key.e
print N
print e
with open('private.pem', 'r') as f:
private = RSA.importKey(f)
oaep = PKCS1_OAEP.new(private)
with open('flag.enc', 'r') as f:
print oaep.decrypt(f.read())
03 参考
0x08 wiener attack(e特别大)
01 思路
- 给出的n分解无望,而且e特别大,利用
wiener attack
脚本分解; - 然后利用一般方法求解即可。
02 代码
import gmpy2
import libnum
def continued_fractions_expansion(numerator,denominator):#(e,N)
result=[]
divident=numerator%denominator
quotient=numerator/denominator
result.append(quotient)
while divident!=0:
numerator=numerator-quotient*denominator
tmp=denominator
denominator=numerator
numerator=tmp
divident=numerator%denominator
quotient=numerator/denominator
result.append(quotient)
return result
def convergents(expansion):
convergents=[(expansion[0],1)]
for i in range(1,len(expansion)):
numerator=1
denominator=expansion[i]
for j in range(i-1,-1,-1):
numerator+=expansion[j]*denominator
if j==0:
break
tmp=denominator
denominator=numerator
numerator=tmp
convergents.append((numerator,denominator))#(k,d)
return convergents
def newtonSqrt(n):
approx = n/2
better = (approx + n/approx)/2
while better != approx:
approx = better
better = (approx + n/approx)/2
return approx
def wiener_attack(cons,e,N):
for cs in cons:
k,d=cs
if k==0:
continue
phi_N=(e*d-1)/k
#x**2-((N-phi_N)+1)*x+N=0
a=1
b=-((N-phi_N)+1)
c=N
delta = b*b - 4*a*c
if delta<=0:
continue
x1= (newtonSqrt(delta)-b)/(2*a)
x2=-(newtonSqrt(delta)+b)/(2*a)
if x1*x2==N:
return [x1,x2,k,d]
N = ...
e = ...
expansion = continued_fractions_expansion(e,N)
cons = convergents(expansion)
p, q, k, d = wiener_attack(cons, e, N)
print p
print q
c = ... #密文
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = pow(c, d, N)
print libnum.n2s(m)