]> git.ipfire.org Git - thirdparty/suricata-verify.git/commitdiff
test/ike: add test for duplicate proposals
authorJason Ish <jason.ish@oisf.net>
Thu, 25 Sep 2025 17:31:27 +0000 (11:31 -0600)
committerVictor Julien <victor@inliniac.net>
Mon, 6 Oct 2025 17:56:11 +0000 (19:56 +0200)
Ticket: #7902

tests/ikev1-duplicate-proposals/README.md [new file with mode: 0644]
tests/ikev1-duplicate-proposals/gen-pkt.py [new file with mode: 0755]
tests/ikev1-duplicate-proposals/generated.pcap [new file with mode: 0644]
tests/ikev1-duplicate-proposals/test.yaml [new file with mode: 0644]

diff --git a/tests/ikev1-duplicate-proposals/README.md b/tests/ikev1-duplicate-proposals/README.md
new file mode 100644 (file)
index 0000000..b3bc56a
--- /dev/null
@@ -0,0 +1,9 @@
+Test the logging of IKEv1 records in the presence of duplicate proposals.
+
+Ticket: https://redmine.openinfosecfoundation.org/issues/7902
+
+## PCAP
+
+Based on the the PCAP found in tests/ikev1-rules/ikev1-isakmp-main-mode.pcap (md5sum: e7e6d064e402997e81ea26b481963731):
+- First packet was extracted
+- LLM used to generate scapy script to create identical packet, then add duplicate proposal
diff --git a/tests/ikev1-duplicate-proposals/gen-pkt.py b/tests/ikev1-duplicate-proposals/gen-pkt.py
new file mode 100755 (executable)
index 0000000..30b9a85
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Generated by Claude Code based on an input packet.
+
+from scapy.all import *
+
+def generate_isakmp_packet():
+    # Build packet using raw bytes for exact control
+
+    # Ethernet header (14 bytes)
+    eth_bytes = bytes.fromhex("00175aed7af0001da18b36d00800")
+
+    # IP header (20 bytes)
+    ip_bytes = bytes.fromhex("45c000cc02890000ff111e84c0a80c01c0a80c02")
+
+    # UDP header (8 bytes)
+    udp_bytes = bytes.fromhex("01f401f400b8291c")
+
+    # ISAKMP header (28 bytes)
+    isakmp_header = bytes.fromhex(
+        "e47a591fd057587f"  # init_cookie
+        "0000000000000000"  # resp_cookie
+        "01"                # next_payload (SA)
+        "10"                # version
+        "02"                # exch_type (Identity Protection)
+        "00"                # flags
+        "00000000"          # message_id
+        "000000b0"          # length (176 bytes total)
+    )
+
+    # SA payload header and DOI/Situation (12 bytes)
+    sa_header = bytes.fromhex(
+        "0d"        # next_payload (VendorID)
+        "00"        # reserved
+        "0044"      # length (68 bytes)
+        "00000001"  # DOI (IPSEC)
+        "00000001"  # situation (identity)
+    )
+
+    # Proposal (20 bytes)
+    proposal = bytes.fromhex(
+        "00"        # next_payload (None)
+        "00"        # reserved
+        "0038"      # length (56 bytes)
+        "01"        # proposal number
+        "01"        # protocol (ISAKMP)
+        "00"        # SPI size
+        "01"        # number of transforms
+    )
+
+    # Transform header (8 bytes)
+    transform = bytes.fromhex(
+        "00"        # next_payload (None)
+        "00"        # reserved
+        "0030"      # length (48 bytes)
+        "01"        # transform number
+        "01"        # transform ID (KEY_IKE)
+        "0000"      # reserved
+    )
+
+    # Transform attributes (40 bytes - includes duplicate life duration)
+    attributes = bytes.fromhex(
+        "80010007"  # Encryption (AES)
+        "800e0080"  # Key Length (128)
+        "80020002"  # Hash (SHA)
+        "000c0004"  # Life Duration (attribute) - moved after Hash
+        "00015180"  # 86400 seconds
+        "80040002"  # Group (1024 MODP)
+        "80030001"  # Auth (PSK)
+        "800b0001"  # Life Type (seconds)
+        "000c0004"  # Life Duration (duplicate attribute)
+        "0000ffff"  # 65535 seconds
+    )
+
+    # Vendor IDs
+    vid1 = bytes.fromhex("0d0000144a131c81070358455c5728f20e95452f")  # NAT-T
+    vid2 = bytes.fromhex("0d000014439b59f8ba676c4c7737ae22eab8f582")
+    vid3 = bytes.fromhex("0d0000147d9419a65310ca6f2c179d9215529d56")
+    vid4 = bytes.fromhex("0000001490cb80913ebb696e086381b5ec427b1f")  # last one has next_payload=0
+
+    # Combine all parts
+    packet_bytes = (
+        eth_bytes +
+        ip_bytes +
+        udp_bytes +
+        isakmp_header +
+        sa_header +
+        proposal +
+        transform +
+        attributes +
+        vid1 +
+        vid2 +
+        vid3 +
+        vid4
+    )
+
+    # Create Scapy packet from raw bytes
+    packet = Ether(packet_bytes)
+
+    # Set timestamp
+    packet.time = 1439117415.368374
+
+    return packet
+
+# Generate the packet
+packet = generate_isakmp_packet()
+
+# Write to pcap file
+wrpcap("generated.pcap", [packet])
+
+print("ISAKMP packet generated and saved to generated.pcap")
diff --git a/tests/ikev1-duplicate-proposals/generated.pcap b/tests/ikev1-duplicate-proposals/generated.pcap
new file mode 100644 (file)
index 0000000..12cc5cf
Binary files /dev/null and b/tests/ikev1-duplicate-proposals/generated.pcap differ
diff --git a/tests/ikev1-duplicate-proposals/test.yaml b/tests/ikev1-duplicate-proposals/test.yaml
new file mode 100644 (file)
index 0000000..e67b24f
--- /dev/null
@@ -0,0 +1,10 @@
+requires:
+  min-version: 9.0.0
+
+checks:
+  - filter:
+      count: 1
+      match:
+        ike._v: 2
+        ike.ikev1.client.proposals[3]: {"key": "sa_life_duration", "value": "Unknown", "raw": 86400}
+        ike.ikev1.client.proposals[7]: {"key": "sa_life_duration", "value": "Unknown", "raw": 65535}