Description
The fix for #3368 stopped the infinite loop when a TCP option length (l) is 0, but parse_options() still crashes when the length byte itself is missing (i.e. the declared options area is longer than the actual buffer).
tcp_parse() calculates the options length from the attacker-controlled TCP data-offset ((tcp_hl * 4) - 20) without clamping it to the real buffer size. When the buffer is shorter, self:u8(...) returns nil and the next line if l > 2 then raises:
attempt to compare number with nil
This aborts any NSE script that builds a packet.Packet from sniffed/received TCP traffic without a pcall.
It is memory-safe (Lua bounds-checks) — only a script-level DoS.
Expected behavior
parse_options() should treat a missing/nil length byte as end-of-options (or simply stop) instead of throwing.
Actual behavior
Uncaught Lua error that kills the script.
Steps to reproduce
Minimal standalone reproduction (Lua 5.4):
local function u8(buf, pos)
if pos < 0 or pos >= #buf then return nil end
return string.byte(buf, pos + 1)
end
local function parse_options(buf, offset, length)
local opt_ptr = 0
while opt_ptr < length do
local t = u8(buf, offset + opt_ptr)
if t == 0 or t == 1 then
opt_ptr = opt_ptr + 1
else
local l = u8(buf, offset + opt_ptr + 1)
if l > 2 then -- <-- crashes when l is nil
opt_ptr = opt_ptr + l
else
if l == 0 then break end
break
end
end
end
end
-- Truncated cases all throw "attempt to compare number with nil"
parse_options("", 0, 40) -- claims 40 bytes, 0 present
parse_options("\x02", 0, 40) -- claims 40, only 1 present
parse_options("\x02\x04\x05", 0, 8) -- claims 8, only 3 present
### Suggested fix
In `parse_options()`:
1. Treat a `nil` value for `t` or `l` as end-of-options (`break`).
2. Or, better, clamp the options length in `tcp_parse()` to the actual remaining bytes before calling `parse_options()`.
This would complete the hardening started in #3368.
### Version
Nmap HEAD (post-7.991) / current `nselib/packet.lua`
Description
The fix for #3368 stopped the infinite loop when a TCP option length (
l) is0, butparse_options()still crashes when the length byte itself is missing (i.e. the declared options area is longer than the actual buffer).tcp_parse()calculates the options length from the attacker-controlled TCP data-offset ((tcp_hl * 4) - 20) without clamping it to the real buffer size. When the buffer is shorter,self:u8(...)returnsniland the next lineif l > 2 thenraises:attempt to compare number with nil
This aborts any NSE script that builds a
packet.Packetfrom sniffed/received TCP traffic without apcall.It is memory-safe (Lua bounds-checks) — only a script-level DoS.
Expected behavior
parse_options()should treat a missing/nil length byte as end-of-options (or simply stop) instead of throwing.Actual behavior
Uncaught Lua error that kills the script.
Steps to reproduce
Minimal standalone reproduction (Lua 5.4):