Skip to content

fix(python): ja4x crashed on a lone certificate with 10+ extensions - #323

Merged
vlvkobal merged 2 commits into
FoxIO-LLC:mainfrom
nkbeast:fix/python-ja4x-single-certificate-counts
Sep 22, 2026
Merged

vlvkobal merged 2 commits into
FoxIO-LLC:mainfrom
nkbeast:fix/python-ja4x-single-certificate-counts

Conversation

@nkbeast

@nkbeast nkbeast commented Sep 5, 2026

Copy link
Copy Markdown

A ServerHello carrying exactly one certificate delivers the JA4X inputs as plain strings, because tshark reports a field with a single value without a list. In to_ja4x() those counts are iterated directly:

for idx, i in enumerate(x['extension_lengths']):

so the string "12" (a certificate with 12 extensions) is walked one digit at a time — two certificates with 1 and 2 extensions. The second iteration indexes past the computed issuer and subject hashes:

  File "ja4x.py", line 87, in to_ja4x
    x[f'JA4X.{idx+1}'] = f'{x["issuer_hashes"][idx]}_...
IndexError: list index out of range

The call is not guarded in main(), so the run aborts and nothing is printed for the whole capture. Single-digit counts only worked by accident (a one-character string is a one-element iteration), and every capture in the test suite carries certificate chains, which is why this never showed up there.

Reproduced with a hand-built pcap of a TLS record containing one DER certificate with 12 extensions (via text2pcap -T 443,51000): tshark 4.4.15 reports x509af_x509af_extensions: '12', x509if_x509if_rdnSequence: '3', and ja4.py exits 1 with no output. Servers that send a lone leaf certificate with ten or more extensions are common — a typical public-CA leaf carries a dozen X.509v3 extensions.

The fix coerces extension_lengths, issuer_sequence and subject_sequence to lists at the top of to_ja4x(). Certificate chains (already lists) are unaffected, and the same issuer/subject count pairing as before is preserved.

NK added 2 commits September 22, 2026 13:52
tshark reports a field with a single value as a plain string, so a
ServerHello carrying exactly one certificate delivered the extension
and RDN counts as strings ("12"). Those counts are iterated in
to_ja4x, and a string is walked one digit at a time: 12 extensions
were read as two certificates with 1 and 2 extensions, and the second
one indexed past the computed issuer and subject hashes. The call is
not guarded in main(), so the run aborted with an IndexError and no
output at all.

Single-digit counts worked by accident, and every capture in the test
suite carries certificate chains, which is why this went unnoticed.
Any server sending a lone leaf certificate with ten or more extensions
killed the whole run. Coerce the counts to lists before iterating.
@vlvkobal
vlvkobal force-pushed the fix/python-ja4x-single-certificate-counts branch from 1ac67d4 to 39647b0 Compare September 22, 2026 13:38
@vlvkobal

Copy link
Copy Markdown
Member

I rebased this PR onto the current main after #322 was merged.

@vlvkobal
vlvkobal merged commit 16b96d9 into FoxIO-LLC:main Sep 22, 2026
1 check passed
@vlvkobal

Copy link
Copy Markdown
Member

Thank you for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants