Skip to content

Commit 4b3ef54

Browse files
committed
Add support for sage modules via subprocess. Adds boneh_durfee() and smallfractions() attacks.
1 parent c81a698 commit 4b3ef54

File tree

5 files changed

+393
-318
lines changed

5 files changed

+393
-318
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,6 @@ target/
6161

6262
# PyCharm
6363
.idea/
64+
65+
# Sage stuff
66+
*.sage.py

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ Automatic selection of best attack for the given public key
55
Attacks :
66
- Weak public key factorization
77
- Wiener's attack
8-
- Hastad's attack (Small exponent attack)
9-
- Small q (q<100,000)
8+
- Hastad's attack (Small public exponent attack)
9+
- Small q (q < 100,000)
1010
- Common factor between ciphertext and modulus attack
1111
- Fermat's factorisation for close p and q
1212
- Gimmicky Primes method
1313
- Past CTF Primes method
14-
- Self-Initializing Quadratic Sieve (SIQS) using Yafu - NEW
15-
- Common factor attacks across multiple keys - NEW
14+
- Self-Initializing Quadratic Sieve (SIQS) using Yafu
15+
- Common factor attacks across multiple keys
16+
- Small fractions method when p/q is close to a small fraction
17+
- Boneh Durfee Method when the private exponent d is too small compared to the modulus (i.e d < n^0.292)
1618

1719
## Usage:
1820
usage: RsaCtfTool.py [-h] \(--publickey PUBLICKEY | --createpub | --dumpkey\)
@@ -49,21 +51,25 @@ Mode 3 - Dump the public and/or private numbers from a PEM/DER format public or
4951
#### Examples :
5052
- weak\_public.pub, weak\_public.cipher : weak public key
5153
- wiener.pub, wiener.cipher : key vulnerable to Wiener's attack
52-
- small\exponent.pub, small\_exponent.cipher : key with e=3, vulnerable to Hastad's attack
54+
- small\_exponent.pub, small\_exponent.cipher : key with e=3, vulnerable to Hastad's attack
5355
- small\_q.pub, small\_q.cipher : public key with a small prime
5456
- close\_primes.pub, close\_primes.cipher : public key with primes suceptible to fermat factorization
5557
- elite\_primes.pub : public key with a gimmick prime
5658
- fermat.pub : public key with another vulnerability to fermat factorization
5759
- pastctfprimes.pub : public key with a prime from a past CTF
5860
- siqs.pub: 256bit public key that is factored in 30 seconds with SIQS
5961
- factordb_parsing.pub: a public key with a prime that is described as an expression on factordb.com
62+
- smallfraction.pub: a public key where p/q is close to a small fraction
63+
- boneh\_durfee.pub: a public key factorable using boneh\_durfee method
64+
- multikey-0.pub and multikey-1.pub: Public keys that share a common factor
6065

6166
#### Requirements:
6267
- GMPY
6368
- SymPy
6469
- libnum (https://github.com/hellman/libnum.git)
6570
- PyCrypto
6671
- Requests
72+
- SageMath - optional but advisable
6773

6874
### MacOS-specific Instructions
6975
If `pip install -r "requirements.txt"` fails to install requirements accessible within environment, the following command may work.

RsaCtfTool.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import requests
2222
import re
2323
import argparse
24+
import subprocess
2425
from glob import glob
2526

2627

@@ -91,12 +92,18 @@ def __init__(self, args):
9192
self.args = args
9293
self.unciphered = None
9394
self.attackobjs = None # This is how we'll know this object represents 1 key
95+
96+
# Test if sage is working and if so, load additional sage based attacks
97+
if args.sageworks:
98+
self.implemented_attacks.append(self.smallfraction)
99+
self.implemented_attacks.append(self.boneh_durfee)
100+
94101
# Load ciphertext
95102
if args.uncipher is not None:
96103
self.cipher = open(args.uncipher, 'rb').read().strip()
97104
else:
98105
self.cipher = None
99-
return
106+
return
100107

101108
def hastads(self):
102109
# Hastad attack for low public exponent, this has found success for e = 3, and e = 5 previously
@@ -114,9 +121,9 @@ def hastads(self):
114121
def factordb(self):
115122
# if factordb returns some math to derive the prime, solve for p without using an eval
116123
def solveforp(equation):
117-
if '^' in equation: k,j = equation.split('^')
118-
if '-' in j: j,sub = j.split('-')
119124
try:
125+
if '^' in equation: k,j = equation.split('^')
126+
if '-' in j: j,sub = j.split('-')
120127
eq = map(int, [k,j,sub])
121128
return pow(eq[0],eq[1])-eq[2]
122129
except Exception as e:
@@ -169,6 +176,25 @@ def wiener(self):
169176

170177
return
171178

179+
def boneh_durfee(self):
180+
# use boneh durfee method, should return a d value, else returns 0
181+
# only works if the sageworks() function returned True
182+
# many of these problems will be solved by the wiener attack module but perhaps some will fall through to here
183+
# TODO: get an example public key solvable by boneh_durfee but not wiener
184+
sageresult = int(subprocess.check_output(['sage','boneh_durfee.sage',str(self.pub_key.n),str(self.pub_key.e)]))
185+
186+
if sageresult > 0:
187+
# use PyCrypto _slowmath rsa_construct to resolve p and q from d
188+
from Crypto.PublicKey import _slowmath
189+
tmp_priv = _slowmath.rsa_construct(long(self.pub_key.n), long(self.pub_key.e), d=long(sageresult))
190+
191+
self.pub_key.p = tmp_priv.p
192+
self.pub_key.q = tmp_priv.q
193+
self.priv_key = PrivateKey(long(self.pub_key.p), long(self.pub_key.q),
194+
long(self.pub_key.e), long(self.pub_key.n))
195+
196+
return
197+
172198
def smallq(self):
173199
# Try an attack where q < 100,000, from BKPCTF2016 - sourcekris
174200
for prime in primes(100000):
@@ -180,6 +206,17 @@ def smallq(self):
180206

181207
return
182208

209+
def smallfraction(self):
210+
# Code/idea from Renaud Lifchitz's talk 15 ways to break RSA security @ OPCDE17
211+
# only works if the sageworks() function returned True
212+
sageresult = int(subprocess.check_output(['sage', 'smallfraction.sage',str(self.pub_key.n)]))
213+
if sageresult > 0:
214+
self.pub_key.p = sageresult
215+
self.pub_key.q = self.pub_key.n / self.pub_key.p
216+
self.priv_key = PrivateKey(long(self.pub_key.p), long(self.pub_key.q),
217+
long(self.pub_key.e), long(self.pub_key.n))
218+
return
219+
183220
def fermat(self, fermat_timeout=60):
184221
# Try an attack where the primes are too close together from BKPCTF2016 - sourcekris
185222
# this attack module can be optional
@@ -343,7 +380,6 @@ def attack(self):
343380
print "[-] Sorry, cracking failed"
344381

345382
implemented_attacks = [ nullattack, hastads, factordb, pastctfprimes, noveltyprimes, smallq, wiener, comfact_cn, fermat, siqs ]
346-
347383

348384
# source http://stackoverflow.com/a/22348885
349385
class timeout:
@@ -361,6 +397,19 @@ def __enter__(self):
361397
def __exit__(self, type, value, traceback):
362398
signal.alarm(0)
363399

400+
def sageworks():
401+
# Check if sage is installed and working
402+
try:
403+
sageversion = subprocess.check_output(['sage', '-v'])
404+
except OSError:
405+
return False
406+
407+
if 'SageMath version' in sageversion:
408+
409+
return True
410+
else:
411+
return False
412+
364413
if __name__ == "__main__":
365414
parser = argparse.ArgumentParser(description='RSA CTF Tool Continued')
366415
group = parser.add_mutually_exclusive_group(required=True)
@@ -398,6 +447,11 @@ def __exit__(self, type, value, traceback):
398447
print "[*] q: " + str(key.q)
399448
quit()
400449

450+
if sageworks():
451+
args.sageworks = True
452+
else:
453+
args.sageworks = False
454+
401455
attackobj = RSAAttack(args)
402456
attackobj.attack()
403457

0 commit comments

Comments
 (0)