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