Skip to content

nselib/packet.lua: uncaught error on truncated TCP options #3447

Description

@ibrahimaljaafreh

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`

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions