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