The TLS certificate is transmitted in the handshake and reveals the
public key. If the RSA modulus has been factored previously, the private
key can be recovered.
from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey from scapy import dadict from scapy.allimport * from scapy.allimport Raw, load_layer, rdpcap from scapy.layers.inet import IP, TCP from scapy.layers.tls.allimport TLS from scapy.layers.tls.cert import Cert from scapy.layers.tls.handshake import TLSCertificate
for pkt in packets: if pkt.haslayer(Raw): raw = pkt[Raw].load try: tls_parsed = TLS(raw) tls_cert_layer = tls_parsed.getlayer(TLSCertificate) if tls_cert_layer: print("[-] Found TLS Certificate.") cert = tls_cert_layer.certs for _, x509_wrapper in cert: cert_list.append(x509_wrapper) print("[-] Extracted a TLS Certificate.") else: print("[-] Found TLS packet.") except Exception as e: print(f"[-] Error: {e}") continue return cert_list
deffind_matching_private_key(target_modulus: int, keys_directory: str) -> str | None: print(f"[*] Searching for matching private key in {keys_directory} ...")
for key_file in glob(os.path.join(keys_directory, "*")): try: withopen(key_file, "rb") as f: private_key = serialization.load_pem_private_key( f.read(), password=None, backend=default_backend() )
except ValueError as e: print(f"[-] Failed to parse public key: {e}") returnNone
defcheck_pcap(pcap_path): packets = rdpcap(pcap_path) packets.show() for pkt in packets: pkt.show() if pkt.haslayer(Raw): raw = pkt[Raw].load try: print(raw.decode()) except Exception as e: print(f"[-] Error: {e}") continue
if __name__ == "__main__": cert_list = find_cert(PCAP_FILE) print(cert_list) for cert_obj in cert_list: modulus = get_modulus(cert_obj) exponent = get_exponent(cert_obj) print(modulus) print(exponent)
# if modulus: # key_path = find_matching_private_key(modulus, KEYS) # print(key_path)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
[-] Found TLS packet. [-] Found TLS Certificate. [-] Extracted a TLS Certificate. [-] Found TLS packet. [-] Found TLS packet. [-] Found TLS Certificate. [-] Extracted a TLS Certificate. [-] Found TLS packet. [-] Found TLS packet. [-] Found TLS packet. [-] Found TLS packet. [-] Found TLS packet. [[X.509 Cert. Subject:/C=AU/O=247CTF/OU=net125/CN=127.0.0.1, Issuer:/C=AU/O=247CTF/OU=net125/CN=127.0.0.1], [X.509 Cert. Subject:/C=AU/O=247CTF/OU=net125/CN=127.0.0.1, Issuer:/C=AU/O=247CTF/OU=net125/CN=127.0.0.1]] 150140677816147665104219084736753210294673482912091623639530125054379822052662632476220418069658373540642718111649733795871151252404840997598533258881471779382418788567883517594075575444723340506445280678466322096113052425236787558022472785685579744210805862764465110689084328509029822107730392445215781001579 65537 150140677816147665104219084736753210294673482912091623639530125054379822052662632476220418069658373540642718111649733795871151252404840997598533258881471779382418788567883517594075575444723340506445280678466322096113052425236787558022472785685579744210805862764465110689084328509029822107730392445215781001579 65537
try: req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req) as response: data = json.loads(response.read().decode("utf-8"))
status = data.get("status", "Unknown") factors = data.get("factors", [])
print(f"[*] Target : {number}") print(f"[*] Status : {status}")
if factors: result = " * ".join([f"{base}^{exp}"for base, exp in factors]) print(f"[*] Factors: {result}") else: print("[!] No factors returned.")
except urllib.error.URLError as e: print(f"[!] Network Error: {e.reason}") except ValueError: print("[!] JSON parsing failed. Did FactorDB change their API?")
p = 11443069641880629381891581986018548808448150675612774441982091938562801238612124445967724562059877882869924090566492089872161438646198325341704520958011761 q = 13120664517031861557695339067275706831429518210212092859212127044658713747906482358428924486662467583986570766086011893335839637764790393666582606794678939 e = 65537
n = p * q phi = (p - 1) * (q - 1) d = pow(e, -1, phi)
key = RSA.construct((n, e, d, p, q)) withopen("private_key.pem", "wb") as f: f.write(key.export_key()) print("Key forged successfully.")
Step 4: Import and Use in Wireshark
Import the generated private key into Wireshark to decrypt the TLS
traffic and recover the flag.
Key Insight
This challenge demonstrates why factorization of RSA moduli is
critical—if a modulus can be factored, the entire RSA cryptosystem is
broken for that key pair. FactorDB maintains a public database of
previously factored numbers.