Add TLS pcap decryption via GoGoRoboCap with optional SSLproxy MITM support#2956
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly upgrades the platform's capability to analyze encrypted network traffic by integrating advanced TLS decryption and interception mechanisms. It introduces a new module for automatic PCAP decryption using GoGoRoboCap and an optional auxiliary module for dynamic SSLproxy MITM. These enhancements ensure that both network and Suricata analysis modules can process decrypted traffic, offering deeper insights into malware behavior over TLS, while also improving the user experience with better PCAP management and download options. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive TLS interception capabilities using SSLproxy and GoGoRoboCap. It adds a new SSLProxy auxiliary module to redirect VM TCP traffic for MITM, and a DecryptPcap processing module to decrypt TLS traffic using either synthetic PCAPs from SSLproxy or collected master keys. The jar package is updated to support Java's Windows certificate store for MITM. Network and Suricata processing modules are enhanced to utilize these decrypted PCAPs, and the web interface now provides download options for decrypted and mixed PCAP files. A high-severity issue was identified where the nfqueue_mark parameter in sslproxy_nfqueue_enable is not used, making the sslproxy.conf setting ineffective for firewall marking.
| run("rmdir", cgroup_dir) | ||
|
|
||
|
|
||
| def sslproxy_nfqueue_enable(nfqueue_mark): |
There was a problem hiding this comment.
The nfqueue_mark parameter is logged but is not used within this function. Instead, a hardcoded firewall mark of 3 is used when creating the ip rule. The related functions sslproxy_enable and sslproxy_disable also use a hardcoded mark 0x3. This makes the nfqueue_mark setting in sslproxy.conf ineffective. The firewall mark should be configurable and passed down to these functions.
33cb8f0 to
f7bde53
Compare
Adds per-analysis TLS interception and PCAP decryption pipeline:
SSLproxy auxiliary module (modules/auxiliary/SSLProxy.py):
- Per-analysis SSLproxy instance using autossl mode (detects TLS
ClientHello on any port + STARTTLS upgrades, passthrough for non-TLS)
- NAT REDIRECT via rooter for VM traffic interception
- Cgroup-based VPN routing for SSLproxy upstream connections
- Single PCAP output (-X) with master key logging (-M)
- Activated per-task via sslproxy=1 option
GoGoRoboCap decryption processor (modules/processing/decryptpcap.py):
- Auto-detects best decryption method:
- SSLproxy synthetic PCAPs: strips prepended TLS ClientHello via
--sslproxy-clean so Suricata can do protocol identification, then
merges cleaned output with original PCAP via mergecap
- TLS keylog: collects keys from tlsdump, sslkeylogfile, and
sslproxy master_keys.log for GoGoRoboCap decryption
- Configurable via pcapsrc option (auto/pcap_with_keylog/sslproxy_synth_pcap)
- Produces dump_decrypted.pcap and dump_mixed.pcap
Network and Suricata integration:
- Both modules auto-use dump_mixed.pcap when available
- Suricata HTTP/2 header parsing (pseudo-headers, control frame filtering)
- HTTP/2 passlist filtering via :authority header
UI changes:
- pcapzip download bundles all available PCAPs (original, decrypted,
mixed, sslproxy raw, sslproxy clean)
- Download buttons for decrypted and mixed PCAPs on network page
- Fix _suricata_http.html method field name
Java package (jar.py):
- Prefer javaw.exe, fall back to java.exe
- JAVA_TOOL_OPTIONS trust store override when sslproxy active
(-Djavax.net.ssl.trustStoreType=Windows-ROOT)
sslkeylogfile.py:
- Document that Schannel KeyLogging registry key must be set in VM
snapshot (requires reboot, cannot be enabled at runtime)
f7bde53 to
d402476
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds per-analysis TLS decryption support to CAPEv2 by generating decrypted/mixed PCAPs (via GoGoRoboCap and optionally SSLproxy MITM) and wiring those outputs into Suricata/network processing and the UI download flow.
Changes:
- Introduce
decryptpcapprocessing module to generatedump_decrypted.pcapanddump_mixed.pcap, and prefer the mixed PCAP for Suricata + network processing when present. - Add optional SSLproxy auxiliary module with rooter rules to MITM TLS and produce synthetic PCAPs (plus config scaffolding).
- Update UI/zip downloads to expose decrypted/mixed PCAPs and expand PCAP zip bundling; extend Suricata HTTP parsing for HTTP/2.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| web/templates/analysis/network/index.html | Adds UI download buttons for decrypted/mixed PCAPs. |
| web/templates/analysis/network/_suricata_http.html | Switches displayed method field to http_method. |
| web/analysis/views.py | Exposes mixed_pcap_exists; expands pcapzip to include multiple PCAP artifacts; adds mixed_pcap download category. |
| utils/rooter.py | Adds SSLproxy enable/disable handlers; adds iptables-legacy helper and legacy cleanup logic. |
| modules/processing/suricata.py | Prefers mixed PCAP; adds HTTP/2 pseudo-header extraction; improves passlist parsing safety. |
| modules/processing/network.py | Prefers mixed PCAP; adjusts hashing/sorting behavior when mixed is used; skips httpreplay on mixed. |
| modules/processing/decryptpcap.py | New GoGoRoboCap-based decryption module producing decrypted + mixed PCAPs. |
| modules/auxiliary/SSLProxy.py | New per-analysis SSLproxy controller that configures routing and launches SSLproxy. |
| lib/cuckoo/core/startup.py | Minor whitespace-only change. |
| conf/default/sslproxy.conf.default | New default config for SSLproxy binary/CA/interface/fwmarks. |
| conf/default/processing.conf.default | Enables and configures decryptpcap processing by default. |
| analyzer/windows/tests/test_analysis_packages.py | Updates expected Jar package summary string. |
| analyzer/windows/modules/packages/jar.py | Prefers javaw.exe; optionally sets Java trust-store opts when sslproxy task option is set. |
| analyzer/windows/modules/auxiliary/sslkeylogfile.py | Documents Schannel key logging registry prerequisite. |
| {% endif %} | ||
| </td> | ||
| <td>{{http.method}} | ||
| <td>{{http.http_method}} |
There was a problem hiding this comment.
moloch_http_method_url is still populated in web/analysis/views.py only when the Suricata entry has a method field, but Suricata processing/template now uses http_method. Result: the "[MLCH]" link next to the Method column will never render. Align the field naming (either emit/alias method in Suricata results or update the view logic to look at http_method).
| <td>{{http.http_method}} | |
| <td> | |
| {% if http.http_method %} | |
| {{ http.http_method }} | |
| {% elif http.method %} | |
| {{ http.method }} | |
| {% endif %} |
| # Clean up any leftover SSLproxy iptables-legacy mangle rules | ||
| # (cleanup_rooter's iptables-save/restore only handles nft rules, not legacy) | ||
| run(IPTABLES_LEGACY, "-t", "mangle", "-F", "FORWARD") | ||
| run(IPTABLES_LEGACY, "-t", "mangle", "-F", "PREROUTING") | ||
| run(IPTABLES_LEGACY, "-t", "mangle", "-F", "POSTROUTING") |
There was a problem hiding this comment.
cleanup_rooter() flushes the entire iptables-legacy mangle chains (FORWARD/PREROUTING/POSTROUTING). This will delete unrelated host firewall rules and can break networking outside CAPE. Also, it unconditionally calls /usr/sbin/iptables-legacy, which will log errors on systems without iptables-legacy installed. Prefer removing only CAPE/SSLproxy rules (e.g., by filtering iptables-legacy-save output for the "CAPE-rooter" comment, or using a dedicated chain) and gate the legacy cleanup on the binary being present.
| req_headers = {h["name"]: h["value"] for h in http_data.get("request_headers", []) if "name" in h} | ||
| # Skip HTTP/2 control frames (SETTINGS, WINDOW_UPDATE, etc.) that have no :method | ||
| if ":method" not in req_headers: | ||
| continue | ||
| resp_headers = {h["name"]: h["value"] for h in http_data.get("response_headers", []) if "name" in h} |
There was a problem hiding this comment.
The HTTP/2 header dict comprehensions can raise KeyError if a Suricata header object is missing a value key (you only guard on "name" in h). Use h.get("name") / h.get("value") (and skip entries without both) to make this parsing robust.
| req_headers = {h["name"]: h["value"] for h in http_data.get("request_headers", []) if "name" in h} | |
| # Skip HTTP/2 control frames (SETTINGS, WINDOW_UPDATE, etc.) that have no :method | |
| if ":method" not in req_headers: | |
| continue | |
| resp_headers = {h["name"]: h["value"] for h in http_data.get("response_headers", []) if "name" in h} | |
| req_headers = {} | |
| for h in http_data.get("request_headers", []): | |
| name = h.get("name") | |
| value = h.get("value") | |
| if name is None or value is None: | |
| continue | |
| req_headers[name] = value | |
| # Skip HTTP/2 control frames (SETTINGS, WINDOW_UPDATE, etc.) that have no :method | |
| if ":method" not in req_headers: | |
| continue | |
| resp_headers = {} | |
| for h in http_data.get("response_headers", []): | |
| name = h.get("name") | |
| value = h.get("value") | |
| if name is None or value is None: | |
| continue | |
| resp_headers[name] = value |
|
|
||
| def __init__(self): | ||
| Auxiliary.__init__(self) | ||
| Thread.__init__(self) |
There was a problem hiding this comment.
SSLProxy calls Thread.__init__(self) even though it does not inherit from Thread (only SSLProxyThread does the actual threading). This is misleading and can set thread internals on an object that is not used as a thread. Drop the Thread.__init__ call (or have SSLProxy inherit Thread if that was intended) to match the intent and avoid confusion.
| Thread.__init__(self) |
- Fix Moloch URL for http_method field (check both http_method and method) - Only delete CAPE-tagged iptables-legacy mangle rules instead of flushing entire chains; skip if iptables-legacy not installed - Guard HTTP/2 header parsing against missing value key - Remove erroneous Thread.__init__() call from SSLProxy
Summary
GoGoRoboCap Decryption (modules/processing/decryptpcap.py)
Auto-detects the best decryption method:
Config: pcapsrc in processing.conf — auto (default), pcap_with_keylog, or sslproxy_synth_pcap.
SSLproxy Auxiliary Module (optional)
Per-analysis SSLproxy instance using autossl mode — detects TLS ClientHello on any port and STARTTLS upgrades, passes non-TLS through cleanly. Activated via sslproxy=1 task option.
Setup:
Install SSLproxy
Generate a CA cert and import it into guest VM Windows certificate store:
Configure conf/sslproxy.conf (see conf/default/sslproxy.conf.default)
Submit tasks with sslproxy=1 option
Other Changes
Dependencies
Test plan