Reject invalid TCP header lengths (from #712)

This commit is contained in:
Afloat
2026-11-04 01:18:22 +07:00
committed by bflatastic
parent 22c74345a2
commit 16c6e364ed
2 changed files with 63 additions and 4 deletions
+11 -4
View File
@@ -652,10 +652,17 @@ fn transport(proto: u8, p: &[u8], d: &mut Decoded) {
};
d.protocols.push("tcp");
let (seq, ack, off, flags, win) = (r.u32_be(4), r.u32_be(8), r.u8(12), r.u8(13), r.u16_be(14));
if win.is_none() {
d.note = Some("TCP header cut short".into());
}
let payload = off.map_or(&[][..], |off| p.get(usize::from(off >> 4) * 4..).unwrap_or(&[]));
let payload = match off.map(|off| usize::from(off >> 4) * 4) {
Some(length) if length < 20 => {
d.note = Some("TCP data offset is smaller than its header".into());
&[][..]
}
Some(length) if length <= p.len() => p.get(length..).unwrap_or(&[]),
_ => {
d.note = Some("TCP header cut short".into());
&[][..]
}
};
d.transport = Some(Transport {
protocol: "tcp",
src_port: Some(sp),
@@ -0,0 +1,52 @@
use craft_anything_formats::pcap::{Decoded, decode};
use craft_anything_testkit::pcap::{ipv4_pkt, tcp};
fn decoded(segment: &[u8]) -> Decoded {
decode(101, &ipv4_pkt(6, [192, 0, 2, 1], [192, 0, 2, 2], segment))
}
#[test]
fn a_tcp_header_shorter_than_twenty_bytes_is_not_payload() {
for words in 0..5u8 {
let mut segment = tcp(51000, 80, 0x18, b"GET / HTTP/1.1\r\n\r\n");
segment[12] = words << 4;
let result = decoded(&segment);
let transport = result.transport.unwrap();
assert_eq!(transport.payload_len, 0, "offset {words}");
assert_eq!((transport.src_port, transport.dst_port), (Some(51000), Some(80)));
assert!(result.http.is_none() && result.tls.is_none());
assert!(result.note.is_some());
}
}
#[test]
fn a_cut_tcp_header_keeps_ports_but_reports_the_cut() {
let whole = tcp(51000, 80, 0x18, b"request");
for end in 4..20 {
let result = decoded(&whole[..end]);
let transport = result.transport.unwrap();
assert_eq!((transport.src_port, transport.dst_port), (Some(51000), Some(80)));
assert_eq!(transport.payload_len, 0);
assert!(result.note.is_some(), "cut at {end}");
}
for words in 6..=15u8 {
let mut segment = tcp(51000, 80, 0x18, &[]);
segment[12] = words << 4;
let result = decoded(&segment);
assert_eq!(result.transport.unwrap().payload_len, 0);
assert!(result.note.is_some(), "missing options for offset {words}");
}
}
#[test]
fn a_valid_tcp_header_with_options_finds_its_payload() {
let mut segment = tcp(51000, 80, 0x18, &[]);
segment[12] = 7 << 4;
segment.extend([1; 8]);
let payload = b"GET /resource HTTP/1.1\r\nHost: example.test\r\n\r\n";
segment.extend(payload);
let result = decoded(&segment);
assert_eq!(result.transport.unwrap().payload_len, payload.len());
assert_eq!(result.http.unwrap().target.as_deref(), Some("/resource"));
assert!(result.note.is_none());
}