tls: read the InnerPlaintext content type past the padding - #5171
Closed
itzzdev09 wants to merge 1 commit into
Closed
tls: read the InnerPlaintext content type past the padding#5171itzzdev09 wants to merge 1 commit into
itzzdev09 wants to merge 1 commit into
Conversation
RFC 8446 sect. 5.2 lays a TLS 1.3 InnerPlaintext out as
content || content_type || zeros(padding)
TLSInnerPlaintext.pre_dissect() tried to skip that padding, but the guard
could never fire:
if s[-1] != b"\x00":
s is bytes, so s[-1] is an int and the comparison against b"\x00" is
always true. The padding branch was dead code, and msg_len was always
len(s) - 1 -- the type was read from the final byte, which for a padded
record is a pad byte, not the content type.
The dead branch was also inverted: it started at n = 1 and advanced while
the byte was non-zero, so had it run it would have stopped immediately on
the first zero and stripped one byte regardless of the padding length.
Scan back over the zero padding instead; the last non-zero byte is the
content type. An unpadded record stops the loop immediately and keeps
msg_len = len(s) - 1, exactly as before. An all-zero payload holds no
valid content type (0 is not a ContentType), so it keeps the previous
behaviour rather than raising, which leaves default-constructed packets
dissectable.
Before, on b"HELLO" + b"\x16" + b"\x00" * 3:
type=0x00 pad=b''
After:
type=0x16 pad=b'\x00\x00\x00'
Test suites, run against this checkout and against an unmodified master
worktree for comparison. cert/sslv2/tlsclientserver fail identically in
both arms -- pre-existing here with a newer `cryptography` than this
checkout supports -- and are listed for completeness:
tls13 53 -> 56 passed, 0 failed (3 new; 1 fails on master)
tls 91 passed, 0 failed unchanged
cert 33 passed, 37 failed unchanged
sslv2 1 passed, 27 failed unchanged
tlsclientserver 9 passed, 26 failed unchanged
flake8 on the touched module is identical to master.
AI-Assisted: yes (Claude Code, Claude Opus 5)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5171 +/- ##
==========================================
+ Coverage 80.81% 81.12% +0.30%
==========================================
Files 390 391 +1
Lines 97736 97798 +62
==========================================
+ Hits 78986 79336 +350
+ Misses 18750 18462 -288
🚀 New features to boost your workflow:
|
Member
|
@itzzdev09 Please do not PR farm with AI. I do not understand what you're trying to fix, the summary is unreadable. Please reopen once you've written it manually. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
RFC 8446 §5.2 lays a TLS 1.3
InnerPlaintextout as:TLSInnerPlaintext.pre_dissect()tries to skip that padding, but the guard can never fire:sisbytes, sos[-1]is an int and the comparison againstb"\x00"is always true. The padding branch is dead code andmsg_lenis alwayslen(s) - 1, so the content type is read from the final byte — which for a padded record is a pad byte.The dead branch is also inverted: it starts at
n = 1and advances while the byte is non-zero, so even if the comparison were fixed it would stop immediately on the first zero and strip exactly one byte regardless of how much padding there is.Same input, dissected before and after:
typepad0x00b''0x16b'\x00\x00\x00'(input
b"HELLO" + b"\x16" + b"\x00" * 3"). On master the record reports content type 0, which is not a validContentTypeat all, and the padding is swallowed into the message list rather than landing inpad.The fix
Scan back over the zero padding; the last non-zero byte is the content type.
msg_len = len(s) - 1, exactly as before.Tests
Three cases added to
test/scapy/layers/tls/tls13.uts: the padded matrix (pad lengths 1/3/16 × content types 0x15/0x16/0x17), an unpadded record, and the all-zero payload. The padded test fails onmaster; the other two pass in both arms and are there to pin that I didn't change the unpadded or degenerate paths.Run on
190b4ba3with the branch applied, and the same suites on an unmodifiedmasterworktree:cert,sslv2andtlsclientserverfail identically in both arms — pre-existing in this checkout with a newercryptography(50.0.1) than it supports. Flagging them rather than omitting them; they are not caused by this change.flake8on the touched module is identical to master.Disclosure per CONTRIBUTING: prepared with AI assistance (Claude Code, Claude Opus 5). Found by a build/dissect round-trip sweep over scapy's packet classes; every number above comes from running the suites in both arms rather than from inspection.
🤖 Generated with Claude Code