diff --git a/week06/easy/__pycache__/pwn_utils.cpython-312.pyc b/week06/easy/__pycache__/pwn_utils.cpython-312.pyc new file mode 100644 index 0000000..3bf0b11 Binary files /dev/null and b/week06/easy/__pycache__/pwn_utils.cpython-312.pyc differ diff --git a/week06/easy/client.py b/week06/easy/client.py index 10ce462..2de94cf 100644 --- a/week06/easy/client.py +++ b/week06/easy/client.py @@ -1,25 +1,44 @@ import socket - +from Crypto.Util.number import getPrime, inverse +import re # Fill in the right target here -HOST = 'this.is.not.a.valid.domain' # TODO -PORT = 0 # TODO +HOST = 'netsec.net.in.tum.de' # TODO +PORT = 20106 # TODO def int_to_bytes(m): return m.to_bytes((m.bit_length() + 7) // 8, 'big').decode() +def modular_inverse(p, phi_q): + x = inverse(p, phi_q) + return x % phi_q + +def decrypt_message(encrypted, p, q): + phi_q = q - 1 + d = modular_inverse(p, phi_q) + message = pow(encrypted, d, q) + return message + + def get_flag(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) sf = s.makefile('rw') # we use a file abstraction for the sockets - - message1 = sf.readline().rstrip('\n') - # TODO - + m = sf.readline().rstrip('\n') + bit_len = int(re.search(r"[0-9]+", m).group()) + print(f"bit_len is {bit_len}") + p = getPrime(bit_len*2) + q = getPrime(bit_len*2) + sf.write(f'{p};{q}\n') + sf.flush() + c_m = int(sf.readline().rstrip('\n')) + print("C_M: ", c_m) + d_m = decrypt_message(c_m, p, q) sf.close() s.close() + print(int_to_bytes(d_m)) if __name__ == '__main__': diff --git a/week06/easy/pwn_utils.py b/week06/easy/pwn_utils.py new file mode 100644 index 0000000..a0d1c2f --- /dev/null +++ b/week06/easy/pwn_utils.py @@ -0,0 +1,27 @@ +import asyncio + + +class utils: + @staticmethod + async def read_line_safe(reader): + """ + Simple implementation to read a line from an async reader + Mimics the original read_line_safe functionality + """ + try: + line = await reader.readline() + return line.decode().strip() + except Exception: + return None + + +def log_error(e, client_writer=None): + """ + Basic error logging function + """ + print(f"Error occurred: {e}") + if client_writer: + try: + client_writer.write(f"Error: {str(e)}\n".encode()) + except Exception: + print("Could not send error to client") diff --git a/week06/easy/server.py b/week06/easy/server.py index b0401c3..44fb808 100644 --- a/week06/easy/server.py +++ b/week06/easy/server.py @@ -46,18 +46,19 @@ async def handle_client(client_reader: StreamReader, client_writer: StreamWriter return match resp.split(';'): case [e, n]: - if not e.isdigit() or not n.isdigit(): + if not e.isdigit() or not n.isdigit(): # has to be not client_writer.write('Invalid input\n'.encode()) await client_writer.drain() return e, n = int(e), int(n) - if not (no_bits * 2 - 1 <= n.bit_length() <= no_bits * 2): + if not (no_bits * 2 - 1 <= n.bit_length() <= no_bits * 2): # has to be not client_writer.write('Wrong bit count\n'.encode()) await client_writer.drain() return - flag = subprocess.check_output('flag').decode().strip() + flag = "flaggy" m = int.from_bytes(flag.encode(), 'big') c = pow(m, e, n) + print(c) client_writer.write(f'{c}\n'.encode()) case _: client_writer.write('Invalid input\n'.encode()) diff --git a/week06/easy/test.py b/week06/easy/test.py new file mode 100644 index 0000000..8a22db5 --- /dev/null +++ b/week06/easy/test.py @@ -0,0 +1,36 @@ +import random +from Crypto.Util.number import getPrime, inverse +from Crypto.Util.number import inverse + + +def moc_encryption_message(p, q): + message = int.from_bytes("flaggy".encode(), 'big') + print("original message: ", message) + c = pow(message,p,q) + return c + + +def modular_inverse(p, phi_q): + x = inverse(p, phi_q) + return x % phi_q + +def decrypt_message(encrypted, p, q): + phi_q = q - 1 + d = modular_inverse(p, phi_q) + message = pow(encrypted, d, q) + return message + + +if __name__ == "__main__": + bit_length = 992 + o = getPrime(bit_length*2) + q = getPrime(bit_length*2) + e, n = int(o), int(q) + if not (bit_length * 2 - 1 <= n.bit_length() <= bit_length * 2): # has to be not + print("wrong bit count") + c = moc_encryption_message(o, q) + m = decrypt_message(c, o, q) + print("decrypted message: ", m) + byte_length = (m.bit_length() + 7) // 8 + decoded_string = m.to_bytes(byte_length, 'big').decode() + print("Decrypted_message_to_string: ", decoded_string) \ No newline at end of file diff --git a/week06/hard/__pycache__/insecurelib.cpython-312.pyc b/week06/hard/__pycache__/insecurelib.cpython-312.pyc new file mode 100644 index 0000000..bfd7eb8 Binary files /dev/null and b/week06/hard/__pycache__/insecurelib.cpython-312.pyc differ diff --git a/week06/hard/__pycache__/pwn_utils.cpython-312.pyc b/week06/hard/__pycache__/pwn_utils.cpython-312.pyc new file mode 100644 index 0000000..a145bf2 Binary files /dev/null and b/week06/hard/__pycache__/pwn_utils.cpython-312.pyc differ diff --git a/week06/hard/alice.py b/week06/hard/alice.py index 28e74df..60e6649 100644 --- a/week06/hard/alice.py +++ b/week06/hard/alice.py @@ -6,7 +6,30 @@ import logging from asyncio import StreamReader, StreamWriter from insecurelib import * -from pwn_utils.utils import read_line_safe + +async def read_line_safe(reader): + """ + Simple implementation to read a line from an async reader + Mimics the original read_line_safe functionality + """ + try: + line = await reader.readline() + return line.decode().strip() + except Exception: + return None + + +def log_error(e, client_writer=None): + """ + Basic error logging function + """ + print(f"Error occurred: {e}") + if client_writer: + try: + client_writer.write(f"Error: {str(e)}\n".encode()) + except Exception: + print("Could not send error to client") + log = logging.getLogger(__name__) clients = {} # task -> (reader, writer) @@ -73,20 +96,20 @@ class AuthenticatedChannel: return None # calculate shared key + print("calculating key now") key = str(pow(Y, a, mod=p)) key = KDRV256(key.encode()) - # decrypt and verify signature decrypted_sig = decrypt(key, s) if not verify(bob_public, message=f'{Y},{X}'.encode(), signature=decrypted_sig): self.writer.write('Signature verification failed\n'.encode()) await self.writer.drain() return None - # sign X and Y and send signature sig = sign(privKey, f'{X},{Y}'.encode()) sig = encrypt(key, sig) self.writer.write(sig + b'\n') + print("finished the do_STS_key_exchange") await self.writer.drain() self.shared_key = key @@ -111,7 +134,7 @@ async def do_session_key_DH_exchange(channel: AuthenticatedChannel) -> bytes | N return p, g, X = map(int, pgX.split(',')) - + print("p, g, x is here: ", (p,g,X)) # two checks to prevent DOSes and improve performance if not check_int_range(p): await channel.send_encrypted(f'{p} must be in [{0}..{MAX_PRIME}]'.encode()) @@ -119,7 +142,7 @@ async def do_session_key_DH_exchange(channel: AuthenticatedChannel) -> bytes | N if not check_int_range(g): await channel.send_encrypted(f'{g} must be in [{0}..{MAX_PRIME}]'.encode()) return - + print("two checks to prevent doses and improve performance finished") # check if parameters are valid if not is_prime(p): await channel.send_encrypted(f'{p} is not a prime number!'.encode()) @@ -132,13 +155,13 @@ async def do_session_key_DH_exchange(channel: AuthenticatedChannel) -> bytes | N if X >= p: await channel.send_encrypted(f"X ({X} can't be larger or equal to p {p}!".encode()) return - + print("check if parameters are valid finished") # create own public/private key parts: b = random.randint(1, p - 1) Y = pow(g, b, mod=p) - + print("sending encryption") await channel.send_encrypted(f'{Y}'.encode()) - + print("sending encyrption finished") # calculate shared key key = str(pow(X, b, mod=p)) key = KDRV256(key.encode()) @@ -165,7 +188,7 @@ async def handle_client(client_reader: StreamReader, client_writer: StreamWriter msg = 'Hey Bob, plz send me my f14g :-)' encrypted_msg = encrypt(session_key, msg.encode()) - + print("sending encypretd message about igving me flag") await authenticated_channel.send_encrypted(encrypted_msg) data = await authenticated_channel.recv_encrypted() diff --git a/week06/hard/alice_private.pem b/week06/hard/alice_private.pem new file mode 100644 index 0000000..0d6204e --- /dev/null +++ b/week06/hard/alice_private.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIEeMyeJI/JBSKPBQVfaOY/T6Ew9fH5JwVrievvcX85V7 +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/week06/hard/alice_public.pem b/week06/hard/alice_public.pem new file mode 100644 index 0000000..6455eb8 --- /dev/null +++ b/week06/hard/alice_public.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAcn6Y7i8GkW0KvMTIaPWj+axVBY29ki2bdAmxza2X6EU= +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/week06/hard/bob.py b/week06/hard/bob.py index a5f5f1d..7a208af 100644 --- a/week06/hard/bob.py +++ b/week06/hard/bob.py @@ -8,7 +8,31 @@ import random from asyncio import StreamReader, StreamWriter from insecurelib import * -from pwn_utils.utils import read_line_safe + + + +async def read_line_safe(reader): + """ + Simple implementation to read a line from an async reader + Mimics the original read_line_safe functionality + """ + try: + line = await reader.readline() + return line.decode().strip() + except Exception: + return None + + +def log_error(e, client_writer=None): + """ + Basic error logging function + """ + print(f"Error occurred: {e}") + if client_writer: + try: + client_writer.write(f"Error: {str(e)}\n".encode()) + except Exception: + print("Could not send error to client") log = logging.getLogger(__name__) clients = {} # task -> (reader, writer) @@ -51,16 +75,16 @@ class AuthenticatedChannel: async def do_STS_key_exchange(self): # receive p,q and public keypart to other server (over the client) and wait for response pgX = await read_line_safe(self.reader) - if pgX is None: return - if pgX.count(',') != 2: self.writer.write('Invalid amount of arguments (expected 3; p,g,X)\n'.encode()) await self.writer.drain() return p, g, X = map(int, pgX.split(',')) + print(p,g,X) + # primality and size checks not necessary since fixed values from RFC 3526 are used for STS key exchange @@ -105,8 +129,7 @@ async def do_session_key_DH_exchange(channel: AuthenticatedChannel) -> bytes | N # send p,q and public keypart pgX = f'{p},{g},{X}' - await channel.send_encrypted(pgX.encode()) - + await channel.send_encrypted(pgX.encode()) # Start debuggin from here bob Y = await channel.recv_encrypted() log.info(f'received "{Y}" as Y (public key)') @@ -121,6 +144,7 @@ async def do_session_key_DH_exchange(channel: AuthenticatedChannel) -> bytes | N # calculate shared key key = str(pow(Y, a, mod=p)) key = KDRV256(key.encode()) + print("do seession dh is finished") return key @@ -140,9 +164,9 @@ async def handle_client(client_reader: StreamReader, client_writer: StreamWriter # do session key DH exchange session_key = await do_session_key_DH_exchange(authenticated_channel) - + print("session key obtained") message = await authenticated_channel.recv_encrypted() - + print("message received for last: ", message) if message is None: return @@ -155,7 +179,7 @@ async def handle_client(client_reader: StreamReader, client_writer: StreamWriter ) return - flag = subprocess.check_output('flag') + flag = "flaggy".encode() encrypted_flag = encrypt(session_key, flag) await authenticated_channel.send_encrypted(encrypted_flag) diff --git a/week06/hard/bob_private.pem b/week06/hard/bob_private.pem new file mode 100644 index 0000000..270bb38 --- /dev/null +++ b/week06/hard/bob_private.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBT9LU2Gd571saJkzcQzxwdfmejkLeKjsJy1WhVOjxY2 +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/week06/hard/bob_public.pem b/week06/hard/bob_public.pem new file mode 100644 index 0000000..9381f15 --- /dev/null +++ b/week06/hard/bob_public.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAeI+vbbnxM+olxPgHiYZUuvOFM6WpWBt7CvNuLSioCxQ= +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/week06/hard/client.py b/week06/hard/client.py index bf6c422..80737af 100644 --- a/week06/hard/client.py +++ b/week06/hard/client.py @@ -7,9 +7,9 @@ import socket from insecurelib import KDRV256, HMAC, encrypt, decrypt # Fill in the right target here -HOST = 'this.is.not.a.valid.domain' # TODO -PORT1 = 20008 -PORT2 = 20108 +HOST = 'netsec.net.in.tum.de' +PORT1 = 20206 +PORT2 = 20306 # note the numbers you encounter may be small for demonstration purposes. @@ -25,7 +25,7 @@ def debug_secure_channel(s1, s2, data: str): else: print(f"from {s1} to {s2}: '{data}...'") - iv, ciphertext, mac = data.split(',') + iv, ciphertext, mac = data.split(';') assert len(iv) == 16 * 2 # a hexlified byte is two bytes long, the IV should be 16 bytes assert ( len(ciphertext) % (16 * 2) == 0 @@ -52,9 +52,18 @@ def main(): data = s1f.readline().rstrip('\n') print(f"from s1 to s2: '{data}'") p, g, X = map(int, data.split(',')) + p = int(p) + g = int(g) + X = int(X) + print("p: {} g: {} X: {}".format(p, g, X)) # TODO: get the flag + s2f.write(data) + s2f.flush() + # Receive answer (second step of DH) + data = s2f.readline().rstrip('\n') + print("from s2 to s1: `{}'".format(data)) s1f.close() s2f.close() s1.close() diff --git a/week06/hard/generate_sig_keys.py b/week06/hard/generate_sig_keys.py index 6408687..4d75950 100644 --- a/week06/hard/generate_sig_keys.py +++ b/week06/hard/generate_sig_keys.py @@ -18,6 +18,8 @@ def write_keys(name: str, private_key: str, public_key: str): if __name__ == '__main__': privA, pubA = generate_keys() privB, pubB = generate_keys() + privC, pubC = generate_keys() write_keys('alice', privA, pubA) write_keys('bob', privB, pubB) + write_keys('mitm', privC, pubC) print('Keys generated and written to files') diff --git a/week06/hard/insecurelib.py b/week06/hard/insecurelib.py index d7c3709..dfa9b75 100644 --- a/week06/hard/insecurelib.py +++ b/week06/hard/insecurelib.py @@ -214,12 +214,13 @@ def decrypt(key: bytes, message: str) -> bytes: key_int = key[16:] assert not message.endswith('\n'), 'message should not end with a newline!' - + print("message is: ", message) try: iv, ciphertext, mac = message.split(';') iv = unhexlify(iv) ciphertext = unhexlify(ciphertext) mac = unhexlify(mac) + print(len(mac)) assert len(mac) == 16 except Exception as e: diff --git a/week06/hard/pwn_utils.py b/week06/hard/pwn_utils.py new file mode 100644 index 0000000..a0d1c2f --- /dev/null +++ b/week06/hard/pwn_utils.py @@ -0,0 +1,27 @@ +import asyncio + + +class utils: + @staticmethod + async def read_line_safe(reader): + """ + Simple implementation to read a line from an async reader + Mimics the original read_line_safe functionality + """ + try: + line = await reader.readline() + return line.decode().strip() + except Exception: + return None + + +def log_error(e, client_writer=None): + """ + Basic error logging function + """ + print(f"Error occurred: {e}") + if client_writer: + try: + client_writer.write(f"Error: {str(e)}\n".encode()) + except Exception: + print("Could not send error to client") diff --git a/week06/hard/test.py b/week06/hard/test.py new file mode 100644 index 0000000..b0511d1 --- /dev/null +++ b/week06/hard/test.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import socket +from Crypto.Cipher import AES +from binascii import hexlify, unhexlify +import random + +from Crypto.PublicKey import ECC +HOST = 'localhost' +PORT1 = 20206 +PORT2 = 20306 + + + +def debug_secure_channel(s1, s2, data): + data = data.rstrip('\n') # remove trailing newline + if len(data) >= 1024: + print("from {} to {}: `{}...'".format(s1, s2, data[:1024])) + else: + print("from {} to {}: `{}'".format(s1, s2, data)) + + iv, ciphertext, mac = data.split(";") + assert (len(iv) == 16 * 2) # a hexlified byte is two bytes long, the IV should be 16 bytes + assert (len(ciphertext) % (16 * 2) == 0) # a hexlified byte is two bytes long, AES block size is 128 bit (16 byte) + assert (len(mac) == 16 * 2) # a quite short MAC. Hint: you still don't want to brute force it! + print("relayed {} encrypted blocks of payload".format(len(unhexlify(ciphertext)) // AES.block_size)) + + +def main(): + s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s1.connect((HOST, PORT1)) + s1f = s1.makefile("rw") # file abstraction for the sockets + + s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s2.connect((HOST, PORT2)) + s2f = s2.makefile("rw") + + # First step of Diffie Hellman (compare slides) + data = s1f.readline() + print("from s1 to s2: '{}'".format(data)) # you should see the newline at the end printed + + p, g, X = data.split(",") + p = int(p) + g = int(g) + X = int(X) + print("p: {} g: {} X: {}".format(p, g, X)) + a = random.randint(1, p-1) + eve_X = pow(g, a, mod=p) + eve_data = f"{p},{g},{eve_X}\n" + # # forward to Bob + s2f.write(eve_data) + s2f.flush() + + # # # Receive answer (second step of DH) + data = s2f.readline() + print("from s2 to s1: '{}'".format(data)) + + # # # Forward received + # s1f.write(data) # the data should still have a b'\n' at the end + # s1f.flush() + + # # # Alice sends something to Bob + # data = s1f.readline() + # print("S1f {}".format(data)) + # debug_secure_channel("Alice", "Bob", data) + + # # # Forward received data to Bob + # s2f.write(data) + # s2f.flush() + # data = s2f.readline() + # debug_secure_channel("Bob", "Alice", data) + # print(f"get data from bob: {data}") + # s1f.write(data) + # s1f.flush() + # data = s1f.readline() + # debug_secure_channel("Bob", "Alice", data) + # print(f"get data from alice: {data}") + # # "alice sending hey bob, plz send me my f14g" + # s2f.write(data) + # s2f.flush() + # data = s1f.readline() + # print(f"get data from alice: {data}") + # s2f.write(data) + # s2f.flush() + # data = s2f.readline() + # print(f"get data from bob :{data}") + # s1f.write(data) + # s1f.flush() + # data = s1f.readline() + # print(f"get data from alice again: {data}") + s1f.close() + s2f.close() + s1.close() + s2.close() + + +if __name__ == "__main__": + main()