Browse Source

first commit

master
myitinos 3 years ago
commit
802f432113
6 changed files with 572 additions and 0 deletions
  1. +111
    -0
      Arithmetic.py
  2. +63
    -0
      ContinuedFractions.py
  3. +91
    -0
      MillerRabin.py
  4. +149
    -0
      RSATG.py
  5. +94
    -0
      RSAvulnerableKeyGenerator.py
  6. +64
    -0
      RSAwienerHacker.py

+ 111
- 0
Arithmetic.py View File

@ -0,0 +1,111 @@
'''
Created on Dec 22, 2011
@author: pablocelayes
'''
def egcd(a,b):
'''
Extended Euclidean Algorithm
returns x, y, gcd(a,b) such that ax + by = gcd(a,b)
'''
u, u1 = 1, 0
v, v1 = 0, 1
while b:
q = a // b
u, u1 = u1, u - q * u1
v, v1 = v1, v - q * v1
a, b = b, a - q * b
return u, v, a
def gcd(a,b):
'''
2.8 times faster than egcd(a,b)[2]
'''
a,b=(b,a) if a<b else (a,b)
while b:
a,b=b,a%b
return a
def modInverse(e,n):
'''
d such that de = 1 (mod n)
e must be coprime to n
this is assumed to be true
'''
return egcd(e,n)[0]%n
def totient(p,q):
'''
Calculates the totient of pq
'''
return (p-1)*(q-1)
def bitlength(x):
'''
Calculates the bitlength of x
'''
assert x >= 0
n = 0
while x > 0:
n = n+1
x = x>>1
return n
def isqrt(n):
'''
Calculates the integer square root
for arbitrary large nonnegative integers
'''
if n < 0:
raise ValueError('square root not defined for negative numbers')
if n == 0:
return 0
a, b = divmod(bitlength(n), 2)
x = 2**(a+b)
while True:
y = (x + n//x)//2
if y >= x:
return x
x = y
def is_perfect_square(n):
'''
If n is a perfect square it returns sqrt(n),
otherwise returns -1
'''
h = n & 0xF; #last hexadecimal "digit"
if h > 9:
return -1 # return immediately in 6 cases out of 16.
# Take advantage of Boolean short-circuit evaluation
if ( h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8 ):
# take square root if you must
t = isqrt(n)
if t*t == n:
return t
else:
return -1
return -1
#TEST functions
def test_is_perfect_square():
print("Testing is_perfect_square")
testsuit = [4, 0, 15, 25, 18, 901, 1000, 1024]
for n in testsuit:
print("Is ", n, " a perfect square?")
if is_perfect_square(n)!= -1:
print("Yes!")
else:
print("Nope")
if __name__ == "__main__":
test_is_perfect_square()

+ 63
- 0
ContinuedFractions.py View File

@ -0,0 +1,63 @@
'''
Created on Dec 14, 2011
@author: pablocelayes
'''
def rational_to_contfrac(x,y):
'''
Converts a rational x/y fraction into
a list of partial quotients [a0, ..., an]
'''
a = x//y
pquotients = [a]
while a * y != x:
x,y = y,x-a*y
a = x//y
pquotients.append(a)
return pquotients
#TODO: efficient method that calculates convergents on-the-go, without doing partial quotients first
def convergents_from_contfrac(frac):
'''
computes the list of convergents
using the list of partial quotients
'''
convs = [];
for i in range(len(frac)):
convs.append(contfrac_to_rational(frac[0:i]))
return convs
def contfrac_to_rational (frac):
'''Converts a finite continued fraction [a0, ..., an]
to an x/y rational.
'''
if len(frac) == 0:
return (0,1)
num = frac[-1]
denom = 1
for _ in range(-2,-len(frac)-1,-1):
num, denom = frac[_]*num+denom, num
return (num,denom)
def test1():
'''
Verify that the basic continued-fraction manipulation stuff works.
'''
testnums = [(1, 1), (1, 2), (5, 15), (27, 73), (73, 27)]
for r in testnums:
(num, denom) = r
print('rational number:')
print(r)
contfrac = rational_to_contfrac (num, denom)
print('continued fraction:')
print(contfrac)
print('convergents:')
print(convergents_from_contfrac(contfrac))
print('***********************************')
if __name__ == "__main__":
test1()

+ 91
- 0
MillerRabin.py View File

@ -0,0 +1,91 @@
import random, sys
def miller_rabin_pass(a, s, d, n):
'''
n is an odd number with
n-1 = (2^s)d, and d odd
and a is the base: 1 < a < n-1
returns True iff n passes the MillerRabinTest for a
'''
a_to_power = pow(a, d, n)
i=0
#Invariant: a_to_power = a^(d*2^i) mod n
# we test whether (a^d) = 1 mod n
if a_to_power == 1:
return True
# we test whether a^(d*2^i) = n-1 mod n
# for 0<=i<=s-1
while(i < s-1):
if a_to_power == n - 1:
return True
a_to_power = (a_to_power * a_to_power) % n
i+=1
# we reach here if the test failed until i=s-2
return a_to_power == n - 1
def miller_rabin(n):
'''
Applies the MillerRabin Test to n (odd)
returns True iff n passes the MillerRabinTest for
K random bases
'''
#Compute s and d such that n-1 = (2^s)d, with d odd
d = n-1
s = 0
while d%2 == 0:
d >>= 1
s+=1
#Applies the test K times
#The probability of a false positive is less than (1/4)^K
K = 20
i=1
while(i<=K):
# 1 < a < n-1
a = random.randrange(2,n-1)
if not miller_rabin_pass(a, s, d, n):
return False
i += 1
return True
def gen_prime(nbits):
'''
Generates a prime of b bits using the
miller_rabin_test
'''
while True:
p = random.getrandbits(nbits)
#force p to have nbits and be odd
p |= 2**nbits | 1
if miller_rabin(p):
return p
break
def gen_prime_range(start, stop):
'''
Generates a prime within the given range
using the miller_rabin_test
'''
while True:
p = random.randrange(start,stop-1)
p |= 1
if miller_rabin(p):
return p
break
if __name__ == "__main__":
if sys.argv[1] == "test":
n = sys.argv[2]
print (miller_rabin(n) and "PRIME" or "COMPOSITE")
elif sys.argv[1] == "genprime":
nbits = int(sys.argv[2])
print(gen_prime(nbits))

+ 149
- 0
RSATG.py View File

@ -0,0 +1,149 @@
#!/usr/bin/env python
import socket
from thread import start_new_thread
from datetime import datetime
from os import urandom
from random import randrange as random
import RSAvulnerableKeyGenerator as keygen
FLAG = 'SlashRootCTF{W13nn3r_w1nn3r_RSA_d1nn3r}'
HOST = '0.0.0.0'
PORT = 6070
BUFF = 1024
BIT = 256
MAX = 10
def banner():
return '''\
_______________________________________________
| ____ ____ ____ ___ ____ _ _ ____ _ _ |
| |__/ [__ |__| | | | |_/ |___ |\ | |
| | \ ___] | | | |__| | \_ |___ | \| |
| ____ ____ _ _ ____ ____ ____ ___ ____ ____ |
| | __ |___ |\ | |___ |__/ |__| | | | |__/ |
| |__] |___ | \| |___ | \ | | | |__| | \ |
|_____________________________________________|
| [1] Generate RSA |
| [2] Generate Token |
| [3] Generate Flag |
|_____________________________________________|
>>> '''
def log(message, address, filename='RSATG.log'):
with open(filename, 'a') as log:
timestamp = datetime.now().strftime('%d/%m/%Y %H:%M:%S')
log.write('[%s][%s:%d] %s\n' % (timestamp, address[0], address[1], str(message)))
def serve_client(client, address, receive=''):
try:
client.send(banner())
token = []
while True:
data = ''
receive = client.recv(BUFF).strip()
if receive == '1':
log('<<< Generating RSA', address)
data = genRSA(client, address)
elif receive == '2':
log('<<< Generating Token', address)
data, token = genToken(client, address)
log('>>> Sending Token: %s' % format(token), address)
elif receive == '3':
log('<<< Generating flag', address)
if token:
client.send('Token : ')
if format(token) == client.recv(BUFF).strip():
ID = urandom(16).encode('hex')
log('<-> RSA ID: %s[%s]' % (ID, format(token)), address)
log('>>> Sending Flag: %s[%s]' % (ID, format(token)), address)
client.send('''\
RSA ID\t : %s
FLAG\t : %s
*Sertakan TOKEN dan RSA ID pada writeup agar poin dihitung!\n''' % (ID, FLAG))
break
else:
log('>-< Wrong Token: %s|%s' % (receive, format(token)), address)
data = 'Try Again!\nYour token is %s\n' % format(token)
else:
log('>-< Empty Token!', address)
data = 'Generate your token!\n'
token = []
client.send(data + '[1|2|3]>>> ')
log('Disconnected', address)
except Exception as message:
log(message, address, 'error.log')
log('>-< Disconnected because error: %s' % message, address)
finally:
client.close()
log('--- Disconnected', address)
def format(token):
return '-'.join(token)
def RSA(bit):
e, n, d = keygen.generateKeys(bit)
p = random(1000, 9999)
c = pow(p, e, n)
return e, n, d, p, c
def genRSA(client, address):
e, n ,d, p, c = RSA(BIT)
client.send('e = %s \nn = %s \nc = %s \np = ' % (e, n, c))
r = client.recv(BUFF).strip()
if r == str(p):
log('<-> Correct %s{e:%s,n:%s,d:%s,p:%s,c:%s)' % (r, e, n, d, p, c), address)
return '\n\m/ Correct \m/\n'
else:
log('>-< Wrong %s{e:%s,n:%s,d:%s,p:%s,c:%s)' % (r, e, n, d, p, c), address)
return '\nWrong :P is %s\n' % p
def genToken(client, address):
token = []
data = 'Token has been generated!\n'
for i in range(1, 6):
e, n ,d, p, c = RSA(BIT)
client.send('Token #%i\ne = %s \nn = %s \nc = %s \np = ' % (i, e, n, c))
r = client.recv(BUFF).strip()
if r == str(p):
log('<-> Correct #%d: %s{e:%s,n:%s,d:%s,p:%s,c:%s)' % (i, r, e, n, d, p, c), address)
token.append(str(p))
else:
token = []
data = '\nWrong :P is %s\n' % p
log('>-< Wrong #%d: %s{e:%s,n:%s,d:%s,p:%s,c:%s)' % (i, r, e, n, d, p, c), address)
break
return data, token
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(MAX)
log('(+) Power On Server', [HOST, PORT])
while True:
try:
client, address = server.accept()
log('<<< Client connected from IP %s with PORT %d' % (address), address)
start_new_thread(serve_client, (client, address))
except Exception as message:
log(message, [HOST, PORT], 'error.log')
except KeyboardInterrupt:
log('(-) Power Off Server', [HOST, PORT])
break
server.close()
if __name__ == '__main__':
main()

+ 94
- 0
RSAvulnerableKeyGenerator.py View File

@ -0,0 +1,94 @@
'''
Created on Dec 14, 2011
@author: pablocelayes
'''
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""\
This module generates RSA-keys which are vulnerable to
the Wiener continued fraction attack
(see RSAfracCont.pdf)
The RSA keys are obtained as follows:
1. Choose two prime numbers p and q
2. Compute n=pq
3. Compute phi(n)=(p-1)(q-1)
4. Choose e coprime to phi(n) such that gcd(e,n)=1
5. Compute d = e^(-1) mod (phi(n))
6. e is the publickey; n is also made public (determines the block size); d is the privatekey
Encryption is as follows:
1. Size of data to be encrypted must be less than n
2. ciphertext=pow(plaintext,publickey,n)
Decryption is as follows:
1. Size of data to be decrypted must be less than n
2. plaintext=pow(ciphertext,privatekey,n)
-------------------------------
RSA-keys are Wiener-vulnerable if d < (n^(1/4))/sqrt(6)
"""
import random, MillerRabin, Arithmetic
def getPrimePair(bits=512):
'''
genera un par de primos p , q con
p de nbits y
p < q < 2p
'''
assert bits%4==0
p = MillerRabin.gen_prime(bits)
q = MillerRabin.gen_prime_range(p+1, 2*p)
return p,q
def generateKeys(nbits=1024):
'''
Generates a key pair
public = (e,n)
private = d
such that
n is nbits long
(e,n) is vulnerable to the Wiener Continued Fraction Attack
'''
# nbits >= 1024 is recommended
assert nbits%4==0
p,q = getPrimePair(nbits//2)
n = p*q
phi = Arithmetic.totient(p, q)
# generate a d such that:
# (d,n) = 1
# 36d^4 < n
good_d = False
while not good_d:
d = random.getrandbits(nbits//4)
if (Arithmetic.gcd(d,phi) == 1 and 36*pow(d,4) < n):
good_d = True
e = Arithmetic.modInverse(d,phi)
return e,n,d
if __name__ == "__main__":
print("hey")
for i in range(5):
e,n,d = generateKeys()
print ("Clave Publica:")
print("e =")
print(e)
print("n =")
print(n)
print ("Clave Privada:")
print("d =")
print(d)
print("-----------------------")

+ 64
- 0
RSAwienerHacker.py View File

@ -0,0 +1,64 @@
'''
Created on Dec 14, 2011
@author: pablocelayes
'''
import ContinuedFractions, Arithmetic, RSAvulnerableKeyGenerator
def hack_RSA(e,n):
'''
Finds d knowing (e,n)
applying the Wiener continued fraction attack
'''
frac = ContinuedFractions.rational_to_contfrac(e, n)
convergents = ContinuedFractions.convergents_from_contfrac(frac)
for (k,d) in convergents:
#check if d is actually the key
if k!=0 and (e*d-1)%k == 0:
phi = (e*d-1)//k
s = n - phi + 1
# check if the equation x^2 - s*x + n = 0
# has integer roots
discr = s*s - 4*n
if(discr>=0):
t = Arithmetic.is_perfect_square(discr)
if t!=-1 and (s+t)%2==0:
#print("Hacked!")
return d
# TEST functions
def test_hack_RSA():
print("Testing Wiener Attack")
times = 5
while(times>0):
e,n,d = RSAvulnerableKeyGenerator.generateKeys(1024)
print("(e,n) is (", e, ", ", n, ")")
print("d = ", d)
hacked_d = hack_RSA(e, n)
if d == hacked_d:
print("Hack WORKED!")
else:
print("Hack FAILED")
print("d = ", d, ", hacked_d = ", hacked_d)
print("-------------------------")
times -= 1
if __name__ == "__main__":
#test_is_perfect_square()
#print("-------------------------")
test_hack_RSA()

Loading…
Cancel
Save