低位攻击【d & (1 << 512 - 1)】
题目描述 :
n=92896523979616431783569762645945918751162321185159790302085768095763248357146198882641160678623069857011832929179987623492267852304178894461486295864091871341339490870689110279720283415976342208476126414933914026436666789270209690168581379143120688241413470569887426810705898518783625903350928784794371176183
e=3
m=random.getrandbits(512)
c=pow(m,e,n)=56164378185049402404287763972280630295410174183649054805947329504892979921131852321281317326306506444145699012788547718091371389698969718830761120076359634262880912417797038049510647237337251037070369278596191506725812511682495575589039521646062521091457438869068866365907962691742604895495670783101319608530
d&((1<<512)-1)=787673996295376297668171075170955852109814939442242049800811601753001897317556022653997651874897208487913321031340711138331360350633965420642045383644955
题目分析:
必备知识:
python中 & 为 符号与,即符号左右两边都为真,结果才为真
eg:
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
1 << 512 - 1 = 512 个 1 组合在一起
d & (1 << 512 - 1) = d_low(d的最后512位字节)
exp1:
def get_full_p(p_low, n,d_low):
PR.<x> = PolynomialRing(Zmod(n))
d_lowbits = d_low.nbits()
nbits = n.nbits()
p_lowbits = p_low.nbits()
f = 2^p_lowbits*x + p_low
f = f.monic()
roots = f.small_roots(X=2^(nbits//2-p_lowbits), beta=0.4)
if roots:
x0 = roots[0]
p = gcd(2^d_lowbits*x0 + p_low, n)
return ZZ(p)
def find_p_low(d_low, e, n):
X = var('X')
for k in range(1, e+1):
results = solve_mod([e*d_low*X == k*n*X + k*X + X-k*X**2 - k*n], 2^d_low.nbits())
for x in results:
p_low = ZZ(x[0])
p = get_full_p(p_low, n,d_low)
if p and p != 1:
return p
n = 92896523979616431783569762645945918751162321185159790302085768095763248357146198882641160678623069857011832929179987623492267852304178894461486295864091871341339490870689110279720283415976342208476126414933914026436666789270209690168581379143120688241413470569887426810705898518783625903350928784794371176183
c = 56164378185049402404287763972280630295410174183649054805947329504892979921131852321281317326306506444145699012788547718091371389698969718830761120076359634262880912417797038049510647237337251037070369278596191506725812511682495575589039521646062521091457438869068866365907962691742604895495670783101319608530
d_low = 787673996295376297668171075170955852109814939442242049800811601753001897317556022653997651874897208487913321031340711138331360350633965420642045383644955
e = 3
find_p_low(d_low, e, n)
之后就是正常的rsa解密了
import gmpy2
from Crypto.Util.number import *
p = 9188765830170326258642510026168563497927415242471400727621853319326260905122407669621339984215654143516262932956971178004512056542245685884515054294531083
n = 92896523979616431783569762645945918751162321185159790302085768095763248357146198882641160678623069857011832929179987623492267852304178894461486295864091871341339490870689110279720283415976342208476126414933914026436666789270209690168581379143120688241413470569887426810705898518783625903350928784794371176183
c = 56164378185049402404287763972280630295410174183649054805947329504892979921131852321281317326306506444145699012788547718091371389698969718830761120076359634262880912417797038049510647237337251037070369278596191506725812511682495575589039521646062521091457438869068866365907962691742604895495670783101319608530
q = n // p
phi = (p-1)*(q-1)
e = 3
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
print(long_to_bytes(m))
# FLAG{2^8rsa5ab086745f6ec745619a8b65fe4ec560}
于2024/05/10更新
高位攻击【d >> 222 << 222】
题目描述:
from secret import m1
def task1():
e = 149
p = getPrime(512)
q = getPrime(512)
n = p * q
d = inverse(e,(p-1)*(q-1))
return (pow(m1, e, n), d >> 222 << 222, n)
c1, leak1, n1 = task1()
print(c1, leak1, n1)
# (89623543982221289730635223555830551523170205418976759060541832483843039074358160566735322009158596405712449020903311144480669706226166537602750967447480664875090686428406188847601970724994074417752345460791736191511081890913041143570455636020205647345764692062144226011846336769477026234106683791286490222089, 138474880017294332349992670187778287774153347391371789199005713563195654993662610111557185709277805165708109047494971468100563949333712521647405765501704478862377527360106399421209690341743821320754482338590671565510629203215009008479290995785318405211007685664887994061938667418220613430135743123498167435264, 146331610798417415036517077006943013321623040860385791423062775325646472298267580898028515394910588437521335092742913111680737790430660749825981979147191282007208147041227246620008377726207734522466015971515317594545750944838673018946440525615131606652748549901880641896940968837669894325535750125282351577689)
假设
l
e
a
k
d
=
d
>
>
222
<
<
222
leak_d = d >> 222 << 222
leakd=d>>222<<222
方法和低位攻击差不多,不过不需要模
2
L
2
2^{\frac{L}{2}}
22L,用RealField(1000)建立一个精度为1000位的实数环,解出来的值p满足:
e
∗
l
e
a
k
d
∗
p
≈
k
p
n
+
k
p
+
p
−
k
p
2
−
k
n
e*leak_d * p\approx kpn + kp + p - kp^2 - kn
e∗leakd∗p≈kpn+kp+p−kp2−kn
解出来的p与实际p的误差,和
l
e
a
k
d
leak_d
leakd与实际d之间的误差相当,故解出来的p的高位是准确的,后面可能会因进位产生误差,不过影响不大,之后p高位攻击求解的时候把最大限制位数上调几位就行
exp:
from tqdm import *
from Crypto.Util.number import *
def get_full_p(p_high, n,d_high,bits):
PR.<x> = PolynomialRing(Zmod(n))
f = x + p_high
f = f.monic()
roots = f.small_roots(X=2^(bits + 4), beta=0.4)
if roots:
x0 = roots[0]
p = gcd(x0 + p_high, n)
return ZZ(p)
def find_p_high(d_high, e, n,bits):
PR.<X> = PolynomialRing(RealField(1000))
for k in tqdm(range(1, e+1)):
f=e * d_high * X - (k*n*X + k*X + X-k*X**2 - k*n)
results = f.roots()
if results:
for x in results:
p_high = int(x[0])
p = get_full_p(p_high, n,d_high,bits)
if p and p != 1:
return p
c1 = 89623543982221289730635223555830551523170205418976759060541832483843039074358160566735322009158596405712449020903311144480669706226166537602750967447480664875090686428406188847601970724994074417752345460791736191511081890913041143570455636020205647345764692062144226011846336769477026234106683791286490222089
leak1 = 138474880017294332349992670187778287774153347391371789199005713563195654993662610111557185709277805165708109047494971468100563949333712521647405765501704478862377527360106399421209690341743821320754482338590671565510629203215009008479290995785318405211007685664887994061938667418220613430135743123498167435264
n1 = 146331610798417415036517077006943013321623040860385791423062775325646472298267580898028515394910588437521335092742913111680737790430660749825981979147191282007208147041227246620008377726207734522466015971515317594545750944838673018946440525615131606652748549901880641896940968837669894325535750125282351577689
e1 = 149
p1 = find_p_high(leak1, e1, n1,222)
q1 = n1 // p1
d1 = inverse(e1,(p1 - 1) * (p1 - 1))
m1 = pow(c1,int(d1),n1)
# m1 = 93042260506308905687316210296370903867944448843426931916177804361314554938769155297064710450598988156852495157007543827533397400499205420109469753501281147177076078618487311899665501210039745369195187361529223336792457967014013480910834750387942851959412408192660971257781477567993988241665134269163250719147