From 802f432113b2937b8de324ae420690e6a6c13817 Mon Sep 17 00:00:00 2001 From: myitinos Date: Fri, 7 Sep 2018 15:21:20 +0800 Subject: [PATCH] first commit --- Arithmetic.py | 111 ++++++++++++++++++++++++++ ContinuedFractions.py | 63 +++++++++++++++ MillerRabin.py | 91 +++++++++++++++++++++ RSATG.py | 149 +++++++++++++++++++++++++++++++++++ RSAvulnerableKeyGenerator.py | 94 ++++++++++++++++++++++ RSAwienerHacker.py | 64 +++++++++++++++ 6 files changed, 572 insertions(+) create mode 100644 Arithmetic.py create mode 100644 ContinuedFractions.py create mode 100644 MillerRabin.py create mode 100644 RSATG.py create mode 100644 RSAvulnerableKeyGenerator.py create mode 100644 RSAwienerHacker.py diff --git a/Arithmetic.py b/Arithmetic.py new file mode 100644 index 0000000..ff1ef45 --- /dev/null +++ b/Arithmetic.py @@ -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= 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() \ No newline at end of file diff --git a/ContinuedFractions.py b/ContinuedFractions.py new file mode 100644 index 0000000..2760724 --- /dev/null +++ b/ContinuedFractions.py @@ -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() diff --git a/MillerRabin.py b/MillerRabin.py new file mode 100644 index 0000000..9aee645 --- /dev/null +++ b/MillerRabin.py @@ -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)) + + diff --git a/RSATG.py b/RSATG.py new file mode 100644 index 0000000..5f283ae --- /dev/null +++ b/RSATG.py @@ -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() \ No newline at end of file diff --git a/RSAvulnerableKeyGenerator.py b/RSAvulnerableKeyGenerator.py new file mode 100644 index 0000000..cf02908 --- /dev/null +++ b/RSAvulnerableKeyGenerator.py @@ -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("-----------------------") diff --git a/RSAwienerHacker.py b/RSAwienerHacker.py new file mode 100644 index 0000000..59a78aa --- /dev/null +++ b/RSAwienerHacker.py @@ -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() + + + + + + +