Generated from Manticore v1.1.6 • 388 packages. View on pkg.go.dev

nbns

import "github.com/TheManticoreProject/Manticore/network/netbios/nbns"

Index

Constants

const (
    // Challenge timeouts and retries
    ChallengeTimeout = 2 * time.Second
    ChallengeRetries = 3
)

NetBIOS name-resolution client defaults and well-known transport parameters (RFC 1002 4.2.12 / RFC 1001 15.1.1).

const (
    // LimitedBroadcastAddr is the IPv4 limited-broadcast address a B-node NAME
    // QUERY REQUEST is sent to when no NBNS/WINS server is configured. Combined
    // with DefaultNBNSUDPPort it forms the default destination 255.255.255.255:137.
    LimitedBroadcastAddr = "255.255.255.255"

    // DefaultClientTimeout bounds how long the client waits for a response to a
    // single transmission before retransmitting (RFC 1001 15.1.1 models this as
    // BCAST_REQ_RETRY_TIMEOUT / UCAST_REQ_RETRY_TIMEOUT). One second is a
    // forgiving value that works for both broadcast and unicast/WINS queries.
    DefaultClientTimeout = 1 * time.Second

    // DefaultRetransmitCount is how many times an unanswered query is
    // retransmitted after the first transmission, mirroring the RFC 1001 15.1.1
    // retry counters (BCAST_REQ_RETRY_COUNT / UCAST_REQ_RETRY_COUNT).
    DefaultRetransmitCount = 2
)

Well-known NetBIOS name suffixes (the 16th byte of a NetBIOS name). The suffix selects the service registered under a name; the same base name is registered several times with different suffixes. These cover the suffixes an outbound resolver most commonly asks for.

const (
    SuffixWorkstation       byte = 0x00 // Workstation Service (the computer name)
    SuffixMessenger         byte = 0x03 // Messenger Service
    SuffixServer            byte = 0x20 // Server Service (file/print sharing)
    SuffixDomainMasterBrows byte = 0x1B // Domain Master Browser
    SuffixDomainControllers byte = 0x1C // Domain Controllers (group)
    SuffixMasterBrowser     byte = 0x1D // Master Browser
    SuffixBrowserElections  byte = 0x1E // Browser Service Elections (group)
)

Constants for name encoding

const (
    NetBIOSNameLength = 16 // NetBIOS names are exactly 16 bytes
    EncodedNameLength = 32 // Each half-byte becomes a byte in encoded form
    ASCII_A           = 0x41
)

NAME_FLAGS bits carried in each NODE_NAME entry of a NODE STATUS RESPONSE (RFC 1002 4.2.18). The field is a 16-bit big-endian word laid out, from the most-significant bit, as: G | ONT | DRG | CNF | ACT | PRM | RESERVED. The Group (G) bit is the most significant bit, matching the NB_FLAGS layout of an NB resource record.

const (
    NameFlagGroup      uint16 = 0x8000 // G:   set for a group name, clear for a unique name
    NameFlagONTMask    uint16 = 0x6000 // ONT: owner node type (see OwnerNodeType)
    NameFlagDeregister uint16 = 0x1000 // DRG: the name is in the process of being deregistered
    NameFlagConflict   uint16 = 0x0800 // CNF: the name is in conflict
    NameFlagActive     uint16 = 0x0400 // ACT: the name is active
    NameFlagPermanent  uint16 = 0x0200 // PRM: the name is a permanent node name

)

Constants for packet types and flags

const (
    // Operation codes
    OpNameQuery    uint16 = 0x0000
    OpRegistration uint16 = 0x2800
    OpRelease      uint16 = 0x3000
    OpWACK         uint16 = 0x3800
    OpRefresh      uint16 = 0x4000
    OpRedirect     uint16 = 0x4800
    OpConflict     uint16 = 0x5000
    OpNodeStatus   uint16 = 0x2100

    // Response codes
    RcodeSuccess     uint16 = 0x0000
    RcodeFormatError uint16 = 0x0001
    RcodeServerError uint16 = 0x0002
    RcodeNameError   uint16 = 0x0003
    RcodeNotImpl     uint16 = 0x0004
    RcodeRefused     uint16 = 0x0005
    RcodeActive      uint16 = 0x0006
    RcodeConflict    uint16 = 0x0007

    // Header NM_FLAGS (RFC 1002 4.2.1.1: R | OPCODE | AA TC RD RA 0 0 B | RCODE)
    FlagResponse           uint16 = 0x8000 // R:  response
    FlagAuthoritative      uint16 = 0x0400 // AA: authoritative answer
    FlagTruncated          uint16 = 0x0200 // TC: truncated
    FlagRecursion          uint16 = 0x0100 // RD: recursion desired
    FlagRecursionAvailable uint16 = 0x0080 // RA: recursion available
    FlagBroadcast          uint16 = 0x0010 // B:  broadcast/multicast

    // RcodeMask isolates the RCODE field (low nibble) of the header flags.
    RcodeMask uint16 = 0x000F

    // OpcodeMask isolates the 4-bit OPCODE field of the header flags. In the
    // RFC 1002 4.2.1.1 layout the flags word is R | OPCODE | NM_FLAGS | RCODE,
    // so the OPCODE occupies bits 14..11 (0x7800), with the R (response) bit at
    // 0x8000 sitting just above it. Masking with 0x7800 therefore yields the
    // opcode of both a request (R=0) and a response (R=1); the Op* constants are
    // already positioned within this field.
    OpcodeMask uint16 = 0x7800

    // Question Type
    QuestionTypeNB     uint16 = 0x0020
    QuestionTypeNBSTAT uint16 = 0x0021

    // Question Class
    QuestionClassIn uint16 = 0x0001 // Internet class
)
const (
    // Default TCP port for NetBIOS name service
    DefaultNBNSTCPPort = 137

    // TCP timeouts
    TCPReadTimeout  = 30 * time.Second
    TCPWriteTimeout = 30 * time.Second

    // Maximum message size
    MaxTCPMessageSize = 65535
)
const (
    // Default UDP port for NetBIOS name service
    DefaultNBNSUDPPort = 137

    // UDP timeouts and buffer sizes
    UDPReadTimeout  = 5 * time.Second
    UDPWriteTimeout = 5 * time.Second
    MaxUDPSize      = 576 // Minimum reassembly buffer size per RFC 1001
)
const (
    // DefaultCleanupInterval is how often expired names are cleaned up
    DefaultCleanupInterval = 5 * time.Minute
)

DefaultSpoofTTL is the TTL (in seconds) placed in spoofed answer records when no explicit TTL is configured. RFC 1002 does not mandate a value; a short TTL keeps a stale poisoned mapping from being cached for long, and 165 seconds is a value commonly emitted by NBT-NS responders.

const DefaultSpoofTTL uint32 = 165
const (
    // DefaultWACKWait is the time-to-wait (in seconds) advertised in a WAIT FOR
    // ACKNOWLEDGEMENT (WACK) RESPONSE while the server runs a name challenge
    // before answering a registration (RFC 1002 4.2.16). It comfortably covers
    // the challenger's retry budget (ChallengeRetries * ChallengeTimeout).
    DefaultWACKWait = 8 * time.Second
)

NB_FLAGS bits carried in the ADDR_ENTRY.Flags field of an NB resource record (RFC 1002 4.2.1.3: G | ONT | RESERVED). The Group (G) bit is the most significant bit of the 16-bit NB_FLAGS field, distinct from the header flags.

const (
    NBFlagGroup uint16 = 0x8000 // G: set for a group name, clear for a unique name
)

func DecodeSessionServiceName

func DecodeSessionServiceName(b []byte) (name string, suffix byte, n int, err error)

DecodeSessionServiceName decodes one second-level-encoded NetBIOS name as it appears in a SESSION REQUEST (RFC 1002 4.3.2), the inverse of EncodeSessionServiceName: a 0x20 length byte, EncodedNameLength encoded bytes, then the label sequence terminating the name — a bare 0x00 for the default scope, or one or more length-prefixed scope labels followed by 0x00.

It returns the name with its trailing space padding removed, the one-byte service suffix that occupied the 16th position, and the total number of bytes consumed, so a caller can decode the CALLED name and then the CALLING name that follows it.

Parameters:

  • b: the buffer positioned at the start of the encoded name

Returns:

  • name: the decoded name, trailing padding removed
  • suffix: the one-byte service suffix
  • n: the number of bytes consumed from b
  • err: non-nil if the encoding is malformed

func DefaultInterfaceIPv4

func DefaultInterfaceIPv4() (net.IP, error)

DefaultInterfaceIPv4 returns the IPv4 address of the first non-loopback, up interface, a convenience for pointing the poisoner at the operator’s own address so victims connect back. It is used when SpoofConfig.SpoofIP is not set explicitly by the caller.

func EncodeSessionServiceName

func EncodeSessionServiceName(name string, suffix byte) ([]byte, error)

EncodeSessionServiceName encodes name (with the given one-byte service suffix) into the 34-byte “second-level” form the NetBIOS session service carries in a SESSION REQUEST (RFC 1002 4.3.2 / RFC 1001 14.1): a 0x20 length byte, the 32-byte first-level encoding of the 16-byte name (up to 15 characters padded with spaces plus the suffix byte in the final position), and a single 0x00 label terminator (no scope for the default). Unlike FirstLevelEncode it permits a leading ‘*’ wildcard (e.g. the “*SMBSERVER” convention), which the session service requires and which Validate would otherwise reject.

func ParseIPFromRData

func ParseIPFromRData(rdata []byte) (net.IP, error)

ParseIPFromRData extracts an IP address from NB resource record RData. RData is expected to be in ADDR_ENTRY format (2 bytes flags + 4 bytes address).

func SuffixLabel

func SuffixLabel(suffix byte, group bool) string

SuffixLabel maps a NetBIOS name suffix (the 16th name byte) to a human-readable service description. group selects between the unique and group interpretations of the suffixes whose meaning depends on the G bit (notably 0x1C, a domain group, versus the unique services). Unknown suffixes are rendered as their hex value.

type ADDR\_ENTRY

POSITIVE NAME QUERY RESPONSE

type ADDR_ENTRY struct {
    Flags   uint16
    Address uint32
}

func (*ADDR_ENTRY) IP

func (n *ADDR_ENTRY) IP() net.IP

IP returns the address as a net.IP

func (*ADDR_ENTRY) Length

func (n *ADDR_ENTRY) Length() uint16

returns static 6 bytes number

func (*ADDR_ENTRY) Marshal

func (n *ADDR_ENTRY) Marshal() []byte

Marshals ADDR_ENTRY to buffer

func (*ADDR_ENTRY) Unmarshal

func (n *ADDR_ENTRY) Unmarshal(data []byte) error

Unmarshal decodes an ADDR_ENTRY from a byte slice

type Client

Client resolves NetBIOS names to their owner addresses by emitting NAME QUERY REQUEST packets (RFC 1002 4.2.12) and decoding the positive/negative NAME QUERY RESPONSE (RFC 1002 4.2.13). It is the outbound counterpart to the server-side name table in this package and mirrors the resolver shape of the sibling network/llmnr client (a name in, a slice of net.IP out).

A Client is a lightweight, stateless value: each Resolve call opens its own short-lived UDP socket, so a single Client is safe to reuse across sequential resolutions.

type Client struct {
    // Server is the NBNS/WINS server the query is sent to (P-node/H-node style
    // unicast). It may be a bare host ("10.0.0.1") or host:port; a missing port
    // defaults to DefaultNBNSUDPPort (137). When Server is empty the client falls
    // back to a B-node broadcast to LimitedBroadcastAddr:137.
    Server string

    // Timeout bounds the wait for a response to each individual transmission.
    // A zero value is treated as DefaultClientTimeout.
    Timeout time.Duration

    // Retransmit is the number of times an unanswered query is retransmitted
    // after the first send. A negative value is treated as zero (a single
    // transmission).
    Retransmit int

    // RecursionDesired requests, for a unicast query, that the destination
    // NBNS/WINS server perform recursive resolution (the RD bit, RFC 1002
    // 4.2.12). It is meaningful only when querying an actual name server: a
    // plain end node is not a name server and will not answer a unicast query
    // with RD set, so this defaults to false and a broadcast query always sets
    // RD regardless (the RFC B-node NAME QUERY REQUEST sets RD+B).
    RecursionDesired bool
}

func NewClient

func NewClient() *Client

NewClient returns a Client that resolves names by B-node broadcast to 255.255.255.255:137, using the default timeout and retransmit count. This is the analogue of llmnr.NewClient for NetBIOS name resolution.

func NewClientWithServer

func NewClientWithServer(server string) *Client

NewClientWithServer returns a Client that unicasts its queries to the given NBNS/WINS server (P-node style) instead of broadcasting. server may be a bare host or host:port; a missing port defaults to 137.

func (*Client) BuildNameQueryRequest

func (c *Client) BuildNameQueryRequest(name string, suffix byte, scope string) (*NBNSPacket, error)

BuildNameQueryRequest assembles a NAME QUERY REQUEST (RFC 1002 4.2.12) for the given base name, 16th-byte suffix, and optional scope. The packet carries a freshly generated transaction ID, a single NB (0x0020) question in class IN, and the header flags appropriate to the client’s transport: OPCODE query with the RD (recursion desired) bit set, plus the B (broadcast) bit when the client is broadcasting rather than unicasting to a name server.

func (*Client) NodeStatus

func (c *Client) NodeStatus(target string) (*NodeStatusResult, error)

NodeStatus performs a NetBIOS node-status query (the equivalent of “nbtstat -A”) against target: it unicasts a NODE STATUS REQUEST to target:137/udp and parses the NODE STATUS RESPONSE into the remote node’s NetBIOS name table and adapter MAC address. target may be a bare host (“10.0.0.1”) or host:port; a missing port defaults to DefaultNBNSUDPPort (137).

The request is transmitted, then retransmitted up to the client’s Retransmit count if no matching response arrives within the per-transmission timeout (RFC 1001 15.1.1 retry model). Only a response echoing the request’s transaction ID is accepted, so unrelated datagrams are ignored.

func (*Client) Resolve

func (c *Client) Resolve(name string, suffix byte) ([]net.IP, error)

Resolve resolves a NetBIOS name+suffix to its owner IPv4 address(es) using an empty scope. It is the high-level entry point mirroring llmnr.Client.Resolve: a name in, a slice of net.IP out.

A NAME_ERROR (RCODE 0x03) negative response means the name is not registered with the queried responder; this is reported as a not-found result (an empty, non-nil slice and a nil error) rather than an error, matching the LLMNR resolver’s no-answer semantics. A genuine failure to obtain any response (every transmission timing out) is returned as an error.

func (*Client) ResolveWithScope

func (c *Client) ResolveWithScope(name string, suffix byte, scope string) ([]net.IP, error)

ResolveWithScope is the scope-aware form of Resolve: it resolves name+suffix within the given NetBIOS scope ID (pass "" for the default empty scope). See Resolve for the return and not-found semantics.

type MatchMode

MatchMode selects how a SpoofHandler decides whether a queried name should be answered with a spoofed address.

type MatchMode int
const (
    // MatchAll answers every queried name (subject to the deny-list). This is the
    // aggressive default an operator uses to poison a whole segment.
    MatchAll MatchMode = iota

    // MatchList answers only names present in the case-insensitive allowlist
    // (SpoofConfig.Names). Any other name is ignored so it can still be resolved
    // by a legitimate responder on the link.
    MatchList

    // MatchRegex answers only names matching the configured regular expression
    // (SpoofConfig.Regex), e.g. `^(WPAD|PROXY)$`.
    MatchRegex
)

type NBNSHeader

NBNSHeader represents the header of a NetBIOS name service packet

type NBNSHeader struct {
    TransactionID uint16
    Flags         uint16
    Questions     uint16
    Answers       uint16
    Authority     uint16
    Additional    uint16
}

type NBNSPacket

NBNSPacket represents a complete NetBIOS name service packet

type NBNSPacket struct {
    Header     NBNSHeader
    Questions  []NBNSQuestion
    Answers    []NBNSResourceRecord
    Authority  []NBNSResourceRecord
    Additional []NBNSResourceRecord
}

func (*NBNSPacket) Marshal

func (p *NBNSPacket) Marshal() ([]byte, error)

Marshal encodes an NBNSPacket into a byte slice

func (*NBNSPacket) Unmarshal

func (p *NBNSPacket) Unmarshal(data []byte) (int, error)

Unmarshal decodes a byte slice into an NBNSPacket

type NBNSQuestion

NBNSQuestion represents a question section in a NetBIOS name service packet

type NBNSQuestion struct {
    Name  *NetBIOSName
    Type  uint16
    Class uint16
}

type NBNSResourceRecord

NBNSResourceRecord represents a resource record in a NetBIOS name service packet

type NBNSResourceRecord struct {
    Name     *NetBIOSName
    Type     uint16
    Class    uint16
    TTL      uint32
    RDLength uint16
    RData    []byte
}

type NameChallenger

NameChallenger handles name conflict detection and resolution

type NameChallenger struct {
    // contains filtered or unexported fields
}

func NewNameChallenger

func NewNameChallenger(nbns *NetBIOSNameServer, handlers *PacketHandler) *NameChallenger

NewNameChallenger creates a new name challenger instance

func (*NameChallenger) ChallengeOwnership

func (c *NameChallenger) ChallengeOwnership(name string, owner net.IP) (bool, error)

ChallengeOwnership verifies if a node still owns a name

func (*NameChallenger) DefendName

func (c *NameChallenger) DefendName(packet *NBNSPacket, response *NBNSPacket)

DefendName actively defends a name against challenges

type NameRecord

NameRecord represents a registered NetBIOS name and its attributes

type NameRecord struct {
    Name            string
    Type            NameType
    Status          NameStatus
    Owners          []net.IP  // IP addresses of nodes that own this name
    TTL             time.Time // Time-to-live for name registration
    RefreshInterval time.Duration
    ScopeID         string // NetBIOS scope identifier
}

type NameStatus

NameStatus represents the current state of a name

type NameStatus uint8
const (
    Active NameStatus = iota
    Conflict
    Releasing
)

type NameType

NameType indicates whether a name is unique or group

type NameType uint8
const (
    Unique NameType = iota // Only one owner allowed
    Group                  // Multiple owners allowed
)

type NetBIOSName

NetBIOSName represents a NetBIOS name with its scope

type NetBIOSName struct {
    Name    string
    ScopeID string
}

func FirstLevelDecode

func FirstLevelDecode(encoded string) (*NetBIOSName, error)

FirstLevelDecode decodes a first level encoded NetBIOS name

func (*NetBIOSName) FirstLevelEncode

func (n *NetBIOSName) FirstLevelEncode() (string, error)

FirstLevelEncode performs the first level encoding of a NetBIOS name as specified in RFC 1001 section 14.1

func (*NetBIOSName) Validate

func (n *NetBIOSName) Validate() error

Validate checks if a NetBIOS name is valid

type NetBIOSNameServer

NetBIOSNameServer represents a NetBIOS Name Server

type NetBIOSNameServer struct {
    // contains filtered or unexported fields
}

func NewNetBIOSNameServer

func NewNetBIOSNameServer(secured bool) *NetBIOSNameServer

NewNetBIOSNameServer creates a new NetBIOS Name Server instance. Call StartCleanup to begin background TTL expiration, and StopCleanup to stop it.

func (*NetBIOSNameServer) CleanExpiredNames

func (n *NetBIOSNameServer) CleanExpiredNames()

CleanExpiredNames removes names that have exceeded their TTL

func (*NetBIOSNameServer) MarkNameConflict

func (n *NetBIOSNameServer) MarkNameConflict(name string, scopeID string) error

MarkNameConflict marks a name as being in conflict and, when a conflict-demand sender has been installed, emits a NAME CONFLICT DEMAND (RFC 1002 4.2.15) to every current owner of the name so the offending node learns its registration is disputed. The demand is built once per owner from the record’s scope-aware name and the owner’s address; a send failure is not fatal to marking the conflict, matching the best-effort nature of a broadcast/unicast demand.

func (*NetBIOSNameServer) NameTable

func (n *NetBIOSNameServer) NameTable() []NodeName

NameTable returns a snapshot of the currently registered names as NODE_NAME entries for a NODE STATUS RESPONSE (RFC 1002 4.2.18). Each entry carries the record’s base name and NAME_FLAGS derived from the record: the G bit reflects a group name, ACT reflects an active registration and CNF a name in conflict. The owner-node-type (ONT) bits are left at 0 (B node). The name table does not track a separate service suffix, so the 16th name byte is emitted as a space (0x20) unless a caller registered a full 16-byte name whose final byte is the suffix; the marshaller (NodeName.marshal) supplies that padding.

func (*NetBIOSNameServer) QueryName

func (n *NetBIOSNameServer) QueryName(name string, scopeID string) ([]net.IP, NameType, time.Duration, error)

QueryName looks up a name and returns its owners, type, and remaining TTL

func (*NetBIOSNameServer) RefreshName

func (n *NetBIOSNameServer) RefreshName(name string, scopeID string, owner net.IP) error

RefreshName updates the TTL for a name registration

func (*NetBIOSNameServer) RegisterName

func (n *NetBIOSNameServer) RegisterName(name string, scopeID string, nameType NameType, owner net.IP, ttl time.Duration) error

RegisterName attempts to register a name with the name server

func (*NetBIOSNameServer) ReleaseName

func (n *NetBIOSNameServer) ReleaseName(name string, scopeID string, owner net.IP) error

ReleaseName removes a name registration for a specific owner

func (*NetBIOSNameServer) SetCleanupInterval

func (n *NetBIOSNameServer) SetCleanupInterval(interval time.Duration)

SetCleanupInterval configures the interval between cleanup cycles. Must be called before StartCleanup.

func (*NetBIOSNameServer) SetConflictDemandSender

func (n *NetBIOSNameServer) SetConflictDemandSender(send func(packet *NBNSPacket, owner net.IP) error)

SetConflictDemandSender installs the transport callback MarkNameConflict uses to emit a NAME CONFLICT DEMAND to a conflicting name’s owner. Passing nil (the default) restores the local-only behaviour in which MarkNameConflict merely flips the record’s status.

func (*NetBIOSNameServer) StartCleanup

func (n *NetBIOSNameServer) StartCleanup()

StartCleanup begins a background goroutine that periodically removes expired name registrations.

func (*NetBIOSNameServer) StopCleanup

func (n *NetBIOSNameServer) StopCleanup()

StopCleanup stops the background cleanup goroutine and waits for it to finish.

type NodeName

NodeName is one entry of a remote node’s NetBIOS name table as returned in a NODE STATUS RESPONSE: the (space-trimmed) 15-character base name, its 16th service suffix byte, and the raw NAME_FLAGS word. The helper predicates below decode the individual NAME_FLAGS bits.

type NodeName struct {
    Name   string // base name with trailing padding spaces trimmed
    Suffix byte   // the 16th name byte selecting the registered service
    Flags  uint16 // raw NAME_FLAGS (see the NameFlag* constants)
}

func (NodeName) IsActive

func (n NodeName) IsActive() bool

IsActive reports whether the entry’s ACT (active) bit is set.

func (NodeName) IsConflict

func (n NodeName) IsConflict() bool

IsConflict reports whether the entry’s CNF (conflict) bit is set.

func (NodeName) IsDeregistering

func (n NodeName) IsDeregistering() bool

IsDeregistering reports whether the entry’s DRG (deregister-in-progress) bit is set.

func (NodeName) IsGroup

func (n NodeName) IsGroup() bool

IsGroup reports whether the entry is a group name (the G bit is set).

func (NodeName) IsPermanent

func (n NodeName) IsPermanent() bool

IsPermanent reports whether the entry’s PRM (permanent) bit is set.

func (NodeName) OwnerNodeType

func (n NodeName) OwnerNodeType() string

OwnerNodeType decodes the two ONT bits into their RFC 1002 node-type label: “B”, “P”, “M”, or “reserved”.

func (NodeName) String

func (n NodeName) String() string

String renders the entry the way an interactive node-status listing would: name, suffix (hex + label), and the unique/group flavour.

func (NodeName) SuffixLabel

func (n NodeName) SuffixLabel() string

SuffixLabel returns a human-readable description of the entry’s service suffix, distinguishing the group and unique forms of the ambiguous suffixes.

type NodeStatusResult

NodeStatusResult is the parsed outcome of a NODE STATUS RESPONSE: the remote node’s full NetBIOS name table and the adapter MAC address taken from the STATISTICS block’s UNIT_ID field.

type NodeStatusResult struct {
    Names []NodeName       // the remote node's registered NetBIOS names
    MAC   net.HardwareAddr // adapter unit id (MAC) from the STATISTICS block, nil if absent
}

type PacketHandler

PacketHandler provides common packet handling methods for both TCP and UDP servers

type PacketHandler struct {
    // contains filtered or unexported fields
}

func NewPacketHandler

func NewPacketHandler(nbns *NetBIOSNameServer) *PacketHandler

NewPacketHandler creates a new packet handler instance

func (*PacketHandler) EnableNodeStatus

func (h *PacketHandler) EnableNodeStatus(mac net.HardwareAddr)

EnableNodeStatus turns on the NODE STATUS responder and sets the adapter MAC reported as the STATISTICS UNIT_ID. A nil or non-6-byte mac reports a zeroed UNIT_ID.

func (*PacketHandler) SetChallenger

func (h *PacketHandler) SetChallenger(c *NameChallenger)

SetChallenger installs (or, with nil, removes) the name challenger used to defend owned unique names on a conflicting registration.

func (*PacketHandler) SetRedirectManager

func (h *PacketHandler) SetRedirectManager(r *RedirectManager)

SetRedirectManager installs (or, with nil, removes) the redirect manager consulted on the name-query path.

type RedirectInfo

RedirectInfo contains information about where to redirect a client

type RedirectInfo struct {
    ServerIP   net.IP
    ServerPort uint16
}

type RedirectManager

RedirectManager handles NBNS redirection

type RedirectManager struct {
    // contains filtered or unexported fields
}

func NewRedirectManager

func NewRedirectManager() *RedirectManager

NewRedirectManager creates a new redirect manager

func (*RedirectManager) AddRedirect

func (r *RedirectManager) AddRedirect(scope string, serverIP net.IP, port uint16)

AddRedirect adds or updates a redirect mapping

func (*RedirectManager) GetRedirect

func (r *RedirectManager) GetRedirect(scope string) (RedirectInfo, bool)

GetRedirect returns redirect information for a scope

func (*RedirectManager) HandleRedirect

func (r *RedirectManager) HandleRedirect(request *NBNSPacket, response *NBNSPacket) bool

HandleRedirect modifies a response packet for redirection if needed

func (*RedirectManager) RemoveRedirect

func (r *RedirectManager) RemoveRedirect(scope string)

RemoveRedirect removes a redirect mapping

type SpoofConfig

SpoofConfig configures a SpoofHandler.

A SpoofHandler answers NBNS NAME QUERY REQUESTs for names the host does not own with an attacker-chosen address, the classic NBT-NS name-resolution poisoning primitive used to coerce a victim into connecting to the operator’s host. Which names are answered is controlled by Mode (answer-all, an exact allowlist, or a regex), and the address returned is SpoofIP (typically a local interface address so the victim connects back to the operator). It is the NetBIOS analogue of the LLMNR SpoofHandler.

type SpoofConfig struct {
    // Mode selects the name-matching strategy (MatchAll, MatchList or MatchRegex).
    Mode MatchMode

    // Names is the case-insensitive allowlist consulted when Mode is MatchList.
    // Entries are compared against the queried name's base label with the
    // service suffix and padding stripped.
    Names []string

    // Regex is the regular expression consulted when Mode is MatchRegex. It is
    // tested against the upper-cased base label of the queried name.
    Regex *regexp.Regexp

    // SpoofIP is the IPv4 address returned in the spoofed NB answer. It is
    // required: the NBNS answer record (ADDR_ENTRY) carries a 4-byte IPv4 address
    // only, so there is no IPv6 counterpart.
    SpoofIP net.IP

    // TTL is the TTL (seconds) placed in the answer record. Zero selects
    // DefaultSpoofTTL.
    TTL uint32

    // Deny is a case-insensitive deny-list of base names that are never answered,
    // even when Mode would otherwise match them. It is the place to suppress the
    // host's own name and benign lookups (e.g. add "WPAD" to opt out of proxy
    // auto-discovery poisoning) so the poisoner can coexist on a segment.
    Deny []string

    // Suffixes, when non-empty, restricts answering to queries whose NetBIOS
    // service suffix (the 16th name byte, RFC 1001 15) is in the set, e.g.
    // {0x00} to poison only workstation lookups or {0x20} for the file-server
    // service. Suffix recovery from a decoded name is best-effort (see
    // spoofNameSuffix); leave empty to answer any suffix.
    Suffixes []byte

    // Verbose logs every query that is answered.
    Verbose bool
}

type SpoofHandler

SpoofHandler answers matched NBNS NAME QUERY REQUESTs with a spoofed address. It is wired into a UDPServer with UDPServer.SetSpoofHandler, where it replaces the authoritative name-table lookup for the NAME QUERY opcode; because a poisoner answers for names it does not own, no name-table registration is required.

type SpoofHandler struct {
    // contains filtered or unexported fields
}

func NewSpoofHandler

func NewSpoofHandler(cfg SpoofConfig) (*SpoofHandler, error)

NewSpoofHandler builds a SpoofHandler from cfg, validating that the configuration can actually answer something.

It returns an error when no usable IPv4 SpoofIP is configured, when MatchList is selected with an empty allowlist, or when MatchRegex is selected without a compiled regex, so a misconfigured poisoner fails fast instead of silently answering nothing.

func (*SpoofHandler) BuildResponse

func (h *SpoofHandler) BuildResponse(request *NBNSPacket) (*NBNSPacket, bool)

BuildResponse assembles the spoofed positive NAME QUERY RESPONSE for request without transmitting it, or returns (nil, false) when the request is not a NAME QUERY or no question matched so the caller can stay silent and let legitimate resolution proceed.

The response is built per RFC 1002 4.2.13: it echoes the request’s transaction ID, sets the R (response) and AA (authoritative answer) header flags with B clear, copies the RD bit from the request (a B-node echoes the requester’s recursion-desired bit), and appends a single NB answer resource record whose ADDR_ENTRY carries the configured spoof address with the Group bit clear (a poisoned name is claimed as a unique owner, RFC 1002 4.2.1.3) and the configured TTL. The queried question is echoed into the response; RFC 1002 4.2.13 shows QDCOUNT=0, but echoing the question is widely accepted (including by this package’s own resolver) and keeps the exchange easy to correlate.

func (*SpoofHandler) HandleNameQuery

func (h *SpoofHandler) HandleNameQuery(remoteAddr net.Addr, request *NBNSPacket) (*NBNSPacket, bool)

HandleNameQuery builds the spoofed response for a NAME QUERY REQUEST and, when verbose, logs the poisoned query. It mirrors the shape of the authoritative PacketHandler.handleNameQuery so the UDPServer can invoke it in the same place in the dispatch. It returns (response, true) when a name matched and (nil, false) when nothing matched, in which case the server stays silent.

func (*SpoofHandler) Matches

func (h *SpoofHandler) Matches(name string) bool

Matches reports whether name should be answered under the handler’s match mode, deny-list and suffix filter. It is exported to make the matching logic directly testable and reusable.

type TCPServer

TCPServer represents a NetBIOS Name Server TCP component

type TCPServer struct {
    // contains filtered or unexported fields
}

func NewTCPServer

func NewTCPServer(addr string, nbns *NetBIOSNameServer) (*TCPServer, error)

NewTCPServer creates a new NBNS TCP server instance

func (*TCPServer) EnableNameDefense

func (s *TCPServer) EnableNameDefense()

EnableNameDefense installs a NameChallenger so a conflicting registration of an owned name triggers an END-NODE CHALLENGE before the registration is refused or accepted (RFC 1002 4.2.10), and a conflict-demand sender so MarkNameConflict emits a NAME CONFLICT DEMAND to the offending owner. The TCP message path returns a single framed response, so no intermediate WACK is emitted here; the challenge still runs. Off by default.

func (*TCPServer) EnableNodeStatus

func (s *TCPServer) EnableNodeStatus(mac net.HardwareAddr)

EnableNodeStatus turns on the NODE STATUS responder so an NBSTAT (0x0021) query is answered from the local name table (RFC 1002 4.2.18). mac is reported as the STATISTICS UNIT_ID; a nil or non-6-byte mac reports a zeroed UNIT_ID. Node status is off by default.

func (*TCPServer) SetRedirectManager

func (s *TCPServer) SetRedirectManager(r *RedirectManager)

SetRedirectManager installs a redirect manager so a NAME QUERY for a configured scope is answered with a REDIRECT NAME QUERY RESPONSE (RFC 1002 4.2.14). Passing nil disables redirection. Off by default.

func (*TCPServer) Start

func (s *TCPServer) Start() error

Start begins listening for TCP connections

func (*TCPServer) Stop

func (s *TCPServer) Stop()

Stop gracefully shuts down the server

type UDPServer

UDPServer represents a NetBIOS Name Server UDP component

type UDPServer struct {
    // contains filtered or unexported fields
}

func NewUDPServer

func NewUDPServer(addr string, nbns *NetBIOSNameServer) (*UDPServer, error)

NewUDPServer creates a new NBNS UDP server instance

func (*UDPServer) EnableNameDefense

func (s *UDPServer) EnableNameDefense()

EnableNameDefense wires the name-defence path into the UDP server: a NameChallenger so a conflicting registration of an owned name triggers an END-NODE CHALLENGE (preceded by a WACK telling the requestor to wait), and a conflict-demand sender so MarkNameConflict emits a NAME CONFLICT DEMAND to the offending owner. Both are off until this is called, leaving default behaviour unchanged.

func (*UDPServer) EnableNodeStatus

func (s *UDPServer) EnableNodeStatus(mac net.HardwareAddr)

EnableNodeStatus turns on the NODE STATUS responder so an NBSTAT (0x0021) query is answered from the local name table (RFC 1002 4.2.18). mac is reported as the STATISTICS UNIT_ID; a nil or non-6-byte mac reports a zeroed UNIT_ID. Node status is off by default.

func (*UDPServer) SetRedirectManager

func (s *UDPServer) SetRedirectManager(r *RedirectManager)

SetRedirectManager installs a redirect manager so a NAME QUERY for a configured scope is answered with a REDIRECT NAME QUERY RESPONSE (RFC 1002 4.2.14). Passing nil disables redirection. Off by default.

func (*UDPServer) SetSpoofHandler

func (s *UDPServer) SetSpoofHandler(h *SpoofHandler)

SetSpoofHandler installs an NBNS poisoning handler on the UDP server. Once set, NAME QUERY REQUESTs are answered by the SpoofHandler (which claims names the host does not own with an attacker-chosen address) rather than by the authoritative name table; every other opcode is handled as before. Passing nil restores the authoritative behaviour.

func (*UDPServer) Start

func (s *UDPServer) Start() error

Start begins listening for UDP packets

func (*UDPServer) Stop

func (s *UDPServer) Stop()

Stop gracefully shuts down the server