Protocol Helpers
May 8, 2026 · View on GitHub
The protocol package and its sub-packages provide helpers for crafting and sending protocol-specific packets. The intent is not to provide full protocol stacks but rather the building blocks that are commonly needed during exploit development — enough to trigger a vulnerability without reimplementing an entire client library.
For general exploit structure, see Getting Started. For fingerprinting and version detection patterns, see Version Checking.
SMB / NetBIOS (protocol/smb)
The smb package covers three layers that come up repeatedly in SMB-related exploit work:
- NetBIOS Name Service (UDP 137) — packet construction and delivery for WINS name operations
- NetBIOS Session Service (TCP 139) — session establishment before SMB traffic
- SMB1 — dialect negotiation for fingerprinting and version detection
The package is protocol-focused rather than OS-focused, so it is equally applicable to Linux Samba targets, NAS appliances, and Windows hosts.
NetBIOS Name Service
The Name Service runs over UDP 137 and handles name registration, queries, and releases against a WINS server (or via broadcast). All packet builders return a []byte that is passed directly to Send or SendAndRecv. Send is a fire-and-forget UDP send suited for registrations and releases that don't expect a reply; SendAndRecv sends a packet and waits up to 5 seconds for a response, which is needed for name queries.
Name registration comes in two forms. Standard registration (opcode 0x5) is a normal unicast WINS registration:
packet := smb.BuildNameRegistration("MYHOST", net.ParseIP("10.0.0.50"), 0x00)
smb.Send(conf.Rhost, 137, packet)
MULTI-HOME registration (opcode 0xF) is a separate code path in WINS, used by clients with multiple network interfaces:
packet := smb.BuildMultiHomeRegistration("MYHOST", net.ParseIP("10.0.0.1"), 0x00)
smb.Send(conf.Rhost, 137, packet)
To query a name from a WINS server and read the reply:
packet := smb.BuildNameQuery("TARGETDC", 0x1C) // 0x1C = domain controller group name
resp, ok := smb.SendAndRecv(conf.Rhost, 137, packet, 512)
if !ok {
return false
}
To release a previously registered name:
packet := smb.BuildNameRelease("MYHOST", net.ParseIP("10.0.0.50"), 0x00)
smb.Send(conf.Rhost, 137, packet)
EncodeName is also exported for cases where the raw RFC 1001/1002 encoded form is needed directly, though the packet builders above call it internally:
encoded := smb.EncodeName("FILESERVER", 0x20) // 0x20 = file service name type
NetBIOS Session Service
The Session Service runs over TCP 139 and must be completed before any SMB traffic can flow on that port. Direct-hosted SMB on port 445 still uses the 4-byte NBT length-framed header (type 0x00 + length); only the NetBIOS SESSION REQUEST handshake is skipped. The Negotiate helper applies framing automatically for both ports.
DialSession handles the full TCP connect and SESSION REQUEST/POSITIVE RESPONSE handshake, returning an open connection ready for SMB traffic:
conn, ok := smb.DialSession("192.168.1.10", "FILESERVER", "ATTACKER")
if !ok {
return false
}
defer conn.Close()
The calledName is the server's NetBIOS name; callingName is the name the client presents. Both are encoded automatically. For cases where finer control is needed — for example, when fuzzing the session handshake itself — the individual builders and response constants are also exported:
// Build a SESSION REQUEST without dialing
req := smb.BuildSessionRequest("FILESERVER", "ATTACKER")
// Wrap an SMB payload in a SESSION MESSAGE header (used for both port 139
// and port 445 — only the SESSION REQUEST handshake is 139-specific).
// Returns (nil, false) if the payload exceeds the 16-bit NBT length field.
msg, ok := smb.BuildSessionMessage(smbPayload)
if !ok {
return false
}
// Parse a raw response byte
switch resp[0] {
case smb.PositiveSessionRsp: // 0x82
// proceed
case smb.NegativeSessionRsp: // 0x83
// rejected
}
SMB1 Negotiate
Negotiate fingerprints a target by sending an SMB1 NEGOTIATE PROTOCOL request and parsing the server's dialect selection. It handles both port 445 and port 139 automatically — on port 139, the NetBIOS session layer is negotiated internally before the SMB request is sent:
resp, ok := smb.Negotiate(conf.Rhost, conf.Rport, nil) // nil uses DefaultDialects
if !ok {
return false
}
output.PrintfStatus("Server selected dialect: %s", resp.SelectedDialect)
output.PrintfStatus("OS version string: %s", resp.OsVersion)
DefaultDialects covers the full range from PC NETWORK PROGRAM 1.0 through NT LM 0.12. Pass a custom list to restrict the negotiation or probe for a specific dialect:
resp, ok := smb.Negotiate(conf.Rhost, conf.Rport, []string{"NT LM 0.12"})
A DialectIndex of 0xFFFF in the response means the server rejected all offered dialects.