From a811fd8013716b4e0da48c4ee428500e3ef65770 Mon Sep 17 00:00:00 2001 From: Jill Kamperides <33240393+kampji@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:31:43 -0400 Subject: [PATCH 1/2] feat: add --nat-t option to probe IKE over UDP 4500 --- ikess.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/ikess.py b/ikess.py index 9d2b570..db3944a 100644 --- a/ikess.py +++ b/ikess.py @@ -34,6 +34,9 @@ CUSTOM_AUTH: List[str] = [] CUSTOM_GROUP: List[str] = [] ONLYCUSTOM: bool = False +NATT: bool = False +# Extra flags prepended to every ike-scan invocation (e.g. --nat-t) +EXTRA_IKESCAN_ARGS: List[str] = [] # --------------------------- Vulnerability text ------------------------ FLAWS = { @@ -259,6 +262,8 @@ def _implementation_sources(data: Dict[str, Any]) -> Dict[str, Optional[str]]: return {"backoff": backoff, "vid": vid_choice, "primary": primary} def run_command(cmd: List[str], timeout: int = 30) -> Tuple[str, str, int]: + if cmd and cmd[0] == "ike-scan" and EXTRA_IKESCAN_ARGS: + cmd = [cmd[0]] + EXTRA_IKESCAN_ARGS + cmd[1:] try: p = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) return (p.stdout.strip(), p.stderr.strip(), p.returncode) @@ -686,6 +691,10 @@ def generate_html_report(results: Dict, filename: str): scope_badge = 'Scope: --onlycustom' if confirmed_only \ else 'Scope: default' + if results.get("scan_info", {}).get("nat_t", False): + scope_badge += ' Transport: NAT-T (UDP 4500)' + else: + scope_badge += ' Transport: UDP 500' host_cards = [] @@ -1076,6 +1085,8 @@ def generate_reports(vpns: Dict[str, Dict[str, Any]], start_time: str, end_time: "end_time": end_time, "targets": list(vpns.keys()), "confirmed_only": ONLYCUSTOM, + "nat_t": NATT, + "udp_port": 4500 if NATT else 500, } # ensure subfolder exists @@ -1086,9 +1097,10 @@ def generate_reports(vpns: Dict[str, Dict[str, Any]], start_time: str, end_time: ts = datetime.now().strftime("%Y%m%d-%H%M%S") # build timestamped paths - xml_path = out_dir / f"{ts}_ikess_scan.xml" - json_path = out_dir / f"{ts}_ikess_scan.json" - html_path = out_dir / f"{ts}_ikess_scan.html" + suffix = "_natt4500" if NATT else "" + xml_path = out_dir / f"{ts}_ikess_scan{suffix}.xml" + json_path = out_dir / f"{ts}_ikess_scan{suffix}.json" + html_path = out_dir / f"{ts}_ikess_scan{suffix}.html" generate_xml_report(results, str(xml_path)) with open(json_path, "w", encoding="utf-8") as f: @@ -1144,7 +1156,7 @@ def scan_target(ip: str) -> Dict[str, Any]: return vpn def main() -> int: - global FULLALGS, FINGERPRINT, CUSTOM_ENC, CUSTOM_HASH, CUSTOM_AUTH, CUSTOM_GROUP, ONLYCUSTOM + global FULLALGS, FINGERPRINT, CUSTOM_ENC, CUSTOM_HASH, CUSTOM_AUTH, CUSTOM_GROUP, ONLYCUSTOM, NATT class SmartFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHelpFormatter): pass @@ -1180,6 +1192,7 @@ class SmartFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHel " sudo ./ikess.py 10.0.0.1 --enc DES,3DES --onlycustom\n" " sudo ./ikess.py 10.0.0.1 --enc AES128,3DES,1,7/256 --hash SHA1,SHA256,1 --auth PSK,RSA --group G2,G14,16\n" " sudo ./ikess.py 203.0.113.5 --enc AES256 --hash SHA256 --auth PSK --group MODP2048 --onlycustom\n" + " sudo ./ikess.py 203.0.113.5 --nat-t\n" ), ) @@ -1245,6 +1258,17 @@ class SmartFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHel ), ) + parser.add_argument( + "--nat-t", "--natt", + dest="nat_t", + action="store_true", + help=( + "Probe over NAT-T (RFC 3947): UDP 4500 source+dest with non-ESP marker (ike-scan --nat-t).\n" + "Applies to every ike-scan call. Report files get a '_natt4500' suffix. Note: many gateways\n" + "only answer on 4500 after NAT detection on 500, so no response here is not conclusive." + ), + ) + args = parser.parse_args() CUSTOM_ENC = _parse_list_arg(args.enc, ENC_ALIASES) @@ -1254,6 +1278,9 @@ class SmartFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHel ONLYCUSTOM = args.onlycustom FULLALGS = args.fullalgs FINGERPRINT = args.fingerprint + NATT = args.nat_t + if NATT: + EXTRA_IKESCAN_ARGS.append("--nat-t") if not check_ike_dependency(): return 1 @@ -1262,6 +1289,7 @@ class SmartFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawTextHel start = datetime.now() logger.info(f"Scan started at {start:%Y-%m-%d %H:%M:%S}") logger.info(f"Targets: {', '.join(args.targets)}") + logger.info(f"Transport: {'NAT-T (UDP 4500, ike-scan --nat-t)' if NATT else 'UDP 500'}") ips_to_scan: List[str] = [] for t in args.targets: From b49f91aac339c5c9c613d8eb5541e9e36ec59dd3 Mon Sep 17 00:00:00 2001 From: Jill Kamperides <33240393+kampji@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:54:16 -0400 Subject: [PATCH 2/2] docs: document --nat-t option in README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19e5ebc..f04af32 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A Python3 Script for Auditing IKE VPN Servers - Risk rates findings with summary and recommendations - Support for fingerprinting via vendor ID (VID) - Support for fingerprinting via backoff pattern (optional) +- Support for probing over NAT-T / UDP 4500 (optional) ## Usage @@ -19,7 +20,7 @@ A Python3 Script for Auditing IKE VPN Servers > This script requires the binary `ike-scan` and must be run as root ```bash -usage: ikess [-h] [--fullalgs] [--fingerprint] [--enc ENC] [--hash HASH] [--auth AUTH] [--group GROUP] [--onlycustom] targets [targets ...] +usage: ikess [-h] [--fullalgs] [--fingerprint] [--enc ENC] [--hash HASH] [--auth AUTH] [--group GROUP] [--onlycustom] [--nat-t] targets [targets ...] ikess - IKE Security Scanner (Sequential Mode) @@ -58,6 +59,9 @@ options: Examples: --group G14,G16 or --dh MODP2048,MODP4096 or --group 14,16 (default: None) --onlycustom Scan only the transforms built from your custom --enc/--hash/--auth/--group lists. Without this flag, custom items are merged into the curated or expanded set. (default: False) + --nat-t, --natt Probe over NAT-T (RFC 3947): UDP 4500 source+dest with non-ESP marker (ike-scan --nat-t). + Applies to every ike-scan call. Report files get a '_natt4500' suffix. Note: many gateways + only answer on 4500 after NAT detection on 500, so no response here is not conclusive. (default: False) Aliases you can use for --enc, --hash, --auth, --group: ENC: DES=1, 3DES=5, AES=7/128, AES128=7/128, AES192=7/192, AES256=7/256 @@ -72,6 +76,7 @@ Examples: sudo ./ikess.py 10.0.0.1 --enc DES,3DES --onlycustom sudo ./ikess.py 10.0.0.1 --enc AES128,3DES,1,7/256 --hash SHA1,SHA256,1 --auth PSK,RSA --group G2,G14,16 sudo ./ikess.py 203.0.113.5 --enc AES256 --hash SHA256 --auth PSK --group MODP2048 --onlycustom + sudo ./ikess.py 203.0.113.5 --nat-t ``` You can also run via Docker: