TLS
Expired and self-signed certificates in a PCAP
Updated ยท Worked on public sample captures, each linked to its report
Free uploads create public reports. Use only sanitized captures; choose private analysis for sensitive traffic.
A browser warning nobody reported, an internal service that suddenly fails its health checks, a command-and-control domain with a brand-new certificate: certificates are among the few things encrypted traffic still shows in the clear. To find expired and self-signed certificates in a PCAP you need handshakes from TLS 1.2 or older, because TLS 1.3 encrypts the certificate chain, and you need to judge validity at the moment of capture, not today. Three public captures show how that plays out: the Heartbleed sample, a Cobalt Strike beacon and the SHub Stealer.
Where certificates appear, and where they do not
In TLS 1.2 and earlier the server sends its chain in a Certificate message right after the Server Hello, and a capture of the handshake holds it in plain bytes. Two things hide it. A resumed session skips the message: the Cobalt Strike capture has 25 handshakes, all TLS 1.2, and 21 of them carry a chain while 4 resume an earlier session. TLS 1.3 encrypts the message outright: the SHub Stealer capture has 57 handshakes, all TLS 1.3, and not one certificate is visible, plus 90 QUIC packets that encrypt their handshake as well. For a capture to show certificates it must start before the connections it is meant to explain, so record first and reproduce after.
What the SSL/TLS view flags
Each TLS session in the SSL/TLS view shows its server name, the versions offered and chosen, the cipher, and a Certificates tab when the handshake carried a chain. Every certificate there lists its subject, issuer, DNS names and validity dates, and two amber warnings mark the ones to look at: an expired certificate, with the date it stopped being valid, and a self-signed one. The self-signed warning appears when the chain is a single certificate. A root CA at the top of a longer chain signs itself by design, so it is not flagged. Sessions to the same server repeat the same chain, so one careful read per server is enough.
Expired at the moment of capture
A certificate is expired in a report when the packet that carried it was captured after its not-after date. The
check uses the capture's own clock, so an old capture does not turn into a list of expired certificates as years
pass. The Cobalt Strike capture is from 18 December 2023, and its certificate for googlesmail.xyz was valid from 8
December 2023 to 7 March 2024: valid when captured, so not flagged, although any browser would reject it today.
The GuLoader capture of February 2026 holds Google certificates valid until April 2026, also unflagged. A capture
whose clock starts at 1970, like the arpspoof sample, flags nothing, so run capinfos -a first and
distrust the dates of a capture without a real clock.
Self-signed: issued by its own subject
The Heartbleed sample's server presents one certificate whose subject and issuer are the same: organization "Internet Widgits Pty Ltd", state "Some-State", country AU, which is what OpenSSL fills in when nobody answers its prompts. It is valid from 19:32:52 UTC on 9 April 2014 for one year, and the handshake was captured at 20:06:36 the same day, so the certificate was made about half an hour before the test. Self-signed certificates are routine on printers, hypervisors, routers and lab servers. They matter on a public service, on a host that should use your internal CA, or on an endpoint that a client connects to without ever having seen a browser warning.
Check validity against the capture time
tshark -r sslheartbleed.pcap -Y "tls.handshake.type == 11" -T fields -e frame.time_utc -e x509af.utcTime
On the Heartbleed sample this prints 2014-04-09T20:06:36.265120000Z followed by
2014-04-09 19:32:52 (UTC),2015-04-09 19:32:52 (UTC): the moment the Certificate message was captured,
then the not-before and not-after dates of the certificate in it. A longer chain prints one pair per certificate,
in chain order. Certificates that run past 2049 encode their dates as GeneralizedTime, which tshark puts in
x509af.generalizedTime instead. Compare each not-after with the frame time, not with today, and you
get the same answer as the report.
A valid certificate is not a verdict
The Cobalt Strike beacon is the reminder. Its nine sessions to mail.googlesmail.xyz on port 2096 present a chain from Google Trust Services, issued ten days before the capture, valid in every respect, and still the channel of a compromised host. Certificate checks catch neglect and lab leftovers; they do not catch intent. Read a new certificate on a look-alike domain together with the timing of the sessions, as the Cobalt Strike report does, and hand expired or self-signed findings on internal services to whoever owns their renewal.
Certificate questions
- Why does one tool call a certificate expired and another call it valid?
- They compare it with different moments. A-Packets judges a certificate at the time its packet was captured; a tool that compares with today's date calls every certificate in an old capture expired.
- Why are no certificates listed for TLS 1.3 sessions?
- TLS 1.3 encrypts the server's Certificate message, so a passive capture never sees the chain. The server name in the Client Hello and the versions on offer stay visible.
- Is every self-signed certificate a finding?
- No. Printers, hypervisors, routers and lab servers ship with them. One matters on a public service, or on a host that should present a certificate from your internal CA.