mirror of
https://github.com/storytold/craft-anything.git
synced 2026-11-05 20:47:45 +01:00
Reject invalid TCP header lengths (from #712)
This commit is contained in:
@@ -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());
|
||||
}
|
||||
Reference in New Issue
Block a user