@ -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() |
@ -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() |
@ -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)) | |||||
@ -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() |
@ -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("-----------------------") |
@ -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() | |||||