Threat actors love to reuse tools, and sometimes, they get lazy. Case in point: AsyncRAT and its notorious fork, DcRAT. These remote access trojans often ship with default, self-signed certificates. If the operators don’t bother to swap them out before deploying their infrastructure, they leave a massive, highly visible behavioral red flag in the network traffic.
Traditionally, hunting for these specific certificate strings across disparate log files requires clunky pipelines or heavy SIEM queries. But if you have Zeek logs and a terminal, DuckDB makes this process incredibly fast and elegant.
Here is how I recently used DuckDB’s Zeek extension to hunt for this exact indicator in a PCAP containing AsyncRAT traffic.
The Hunt: Joining the Logs
To find the malicious traffic, we need to map the base TCP connections directly to their negotiated certificate strings. This means we have to join three separate Zeek logs:
- conn.log (for the IPs and ports)
- ssl.log (for the SSL/TLS session data)
- x509.log (for the actual certificate details)
Using DuckDB’s read_zeek() function, we can query these raw log files directly as if they were SQL tables. Here is the query to connect the dots:
SELECT
c.ts,
c.uid,
c.id_orig_h AS src_ip,
c.id_resp_h AS dst_ip,
c.id_resp_p AS dst_port,
s.server_name,
x.certificate_subject AS cert_subject,
x.certificate_issuer AS cert_issuer
FROM read_zeek('conn.log') AS c
JOIN read_zeek('ssl.log') AS s USING (uid)
JOIN read_zeek('x509.log') AS x
ON list_contains(s.cert_chain_fps, x.fingerprint)
WHERE x.certificate_subject ILIKE '%dcrat%'
OR x.certificate_issuer ILIKE '%dcrat%';
Pro-Tip: Notice the list_contains() function. Zeek’s cert_chain_fps is a vector type (a list of strings). DuckDB parses this natively, allowing us to easily check if the fingerprint from x509.log exists anywhere in that SSL certificate chain.
The Execution & Results
Dropping into the DuckDB CLI, the query executes in milliseconds. No data ingestion, no indexing delays—just straight SQL on raw files.
% ls
a72d5f1a-1703-4f86-af3a-6896282f5277.pcap ocsp.log
conn.log packet_filter.log
dns.log ssl.log
files.log weird.log
http.log x509.log
% duckdb
DuckDB v1.5.2 (Variegata)
Enter ".help" for usage hints.
memory D load zeek;
memory D SELECT
c.ts,
c.uid,
c.id_orig_h AS src_ip,
c.id_resp_h AS dst_ip,
c.id_resp_p AS dst_port,
s.server_name,
x.certificate_subject AS cert_subject,
x.certificate_issuer AS cert_issuer
FROM read_zeek('conn.log') AS c
JOIN read_zeek('ssl.log') AS s USING (uid)
JOIN read_zeek('x509.log') AS x
ON list_contains(s.cert_chain_fps, x.fingerprint)
WHERE x.certificate_subject ILIKE '%dcrat%'
OR x.certificate_issuer ILIKE '%dcrat%';
┌───────────────────────┬────────────────────┬────────────────┬───────────────┬──────────┬─────────────┬──────────────┬───────────────────────┐
│ ts │ uid │ src_ip │ dst_ip │ dst_port │ server_name │ cert_subject │ cert_issuer │
│ timestamp with time z │ varchar │ inet │ inet │ uint16 │ varchar │ varchar │ varchar │
│ one │ │ │ │ │ │ │ │
├───────────────────────┼────────────────────┼────────────────┼───────────────┼──────────┼─────────────┼──────────────┼───────────────────────┤
│ 2026-04-22 10:45:04.0 │ CwW7GO2oFwvT98QrXf │ 192.168.100.17 │ 178.16.52.105 │ 207 │ NULL │ CN=DcRat │ C=CN,L=SH,O=DcRat By │
│ 28574-04 │ │ │ │ │ │ │ qwqdanchun,OU=qwqdanc │
│ │ │ │ │ │ │ │ hun,CN=DcRat Server │
└───────────────────────┴────────────────────┴────────────────┴───────────────┴──────────┴─────────────┴──────────────┴───────────────────────┘
The Breakdown
Boom. The output immediately surfaces our malicious connection:
- Victim IP: 192.168.100.17
- C2 Destination: 178.16.52.105
- Anomalous Port: 207 (A quick secondary indicator that this isn’t standard web traffic).
- The Smoking Gun: A certificate explicitly issued by C=CN,L=SH,O=DcRat By qwqdanchun,OU=qwqdanchun,CN=DcRat Server.
Conclusion
When performing incident response or threat hunting, speed and agility are everything. DuckDB’s ability to cleanly parse Zeek’s complex list and vector types makes navigating complex log relationships incredibly clean. Next time you have a directory full of Zeek logs, skip the heavy ingestion pipelines and try querying them directly.
How is everyone else analyzing their Zeek logs these days? Let me know!
Leave a Reply