reassembly: the newer package has the same SYN-payload substitution as tcpassembly, by a different mechanism #14
Labels
No labels
core
cpu-dos
critical
dos
evasion
has-poc
high
integer-overflow
ip4defrag
layers
low
medium
memory-exhaustion
other
panic
pcapgo
pentest-2026-08
rce
tcpassembly
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
noi/gopacket#14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: high ·
reassembly/tcpassembly.go:691-698,724Follow-up to #3. I noted there that "the same code shape is present in
reassembly/". That turned out to be half right in a way worth writing down:reassemblyfixed the sequence arithmetic thattcpassemblygets wrong, and still has the identical attacker primitive.This matters because
reassemblyis the newer package and the one new code is steered toward.What reassembly gets right
Compare
tcpassembly/assembly.go:579, which doesconn.nextSeq = seq.Add(len(bytes) + 1)— advancing by the payload length as well.reassemblyis right andtcpassemblyis wrong.What it still gets wrong
Twenty-six lines later:
bytesis the SYN's payload andseqhas already been advanced toISN+1. So the SYN's payload is emitted as stream data at exactly the offset where the real request will arrive. The genuine segment atISN+1is then contiguous with data already delivered and is trimmed as an overlap.The net effect on the reconstructed stream is the same as #3:
analyser_stream = SYN_payload || real_request[N:].Reproduction
The third line is the whole attack: the analyser reconstructs a complete, well-formed request to
/healthzfromkube-probe, and the real sqlmap request against/flagnever appears in the stream at all — the cover is longer than the request, so nothing of the original survives the overlap trim. A Linux listener that has not negotiated TCP Fast Open discards the SYN's data entirely and serves the real request normally.Cost: bytes carried on the SYN, which the attacker was sending anyway.
Fix
Do not treat a SYN's payload as stream data:
or, if TFO support is wanted, gate it behind an explicit
AssemblerOptionsflag rather than making it the default for every SYN.tcpcheck.gois a natural second place to enforce this — it already inspects SYN flags and options for the state machine, and aTCPOptionCheckthat saw no TFO option in the handshake knows the SYN payload cannot have been delivered.Related: #3 (the
tcpassemblyvariant, plus the!ACKinsertion primitive, which should be checked here too).Verified against
b7d9dbdon Go 1.24.4. PoC:reasm_synon branchpentest/2026-08-poc.