shadow1ng/fscan · error
NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER
NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER
Error message
NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER
What it means
recvLicenceInfo in sec/sec.go reads the security header of an incoming license PDU on the channel. If the securityFlag does not include LICENSE_PKT (0x0080), the data is not a valid license packet, so the client emits the node-rdp-style constant error NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER and stops processing.
Source
Thrown at libs/grdp/protocol/sec/sec.go:730
c.sendFlagged(EXCHANGE_PKT, message.serialize())
return true
}
func (c *Client) sendInfoPkt() {
var secFlag uint16 = INFO_PKT
if c.enableEncryption {
secFlag |= ENCRYPT
}
glog.Debug("RdpVersion:", c.ClientCoreData().RdpVersion, ":", gcc.RDP_VERSION_5_PLUS)
c.sendFlagged(secFlag, c.info.Serialize(c.ClientCoreData().RdpVersion == gcc.RDP_VERSION_5_PLUS))
}
func (c *Client) recvLicenceInfo(channel string, s []byte) {
glog.Debug("sec recvLicenceInfo", hex.EncodeToString(s))
r := bytes.NewReader(s)
h := readSecurityHeader(r)
if (h.securityFlag & LICENSE_PKT) == 0 {
c.Emit("error", errors.New("NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER"))
return
}
p := lic.ReadLicensePacket(r)
switch p.BMsgtype {
case lic.NEW_LICENSE:
glog.Info("sec NEW_LICENSE")
c.Emit("success")
goto connect
case lic.ERROR_ALERT:
message := p.LicensingMessage.(*lic.ErrorMessage)
glog.Info("sec ERROR_ALERT and ErrorCode:", message.DwErrorCode)
if message.DwErrorCode == lic.STATUS_VALID_CLIENT && message.DwStateTransaction == lic.ST_NO_TRANSITION {
goto connect
}
goto retry
case lic.LICENSE_REQUEST:
glog.Info("sec LICENSE_REQUEST")View on GitHub (pinned to 95cc12e753)
Solutions
- Log the raw header bytes (h.securityFlag, h.securitySequence) to confirm misalignment vs. a genuinely non-license packet.
- Verify earlier PDU framing: ensure the length consumed by the previous packet matches, so readSecurityHeader starts at the true header offset.
- If the server doesn't implement licensing properly, configure it to skip the license phase (e.g. fix xrdp/terminal server licensing settings) or patch the client to ignore non-license data on this channel.
- Update grdp/node-rdp-derived code so recvLicenceInfo tolerates and skips unexpected PDUs instead of erroring.
Example fix
// before
if (h.securityFlag & LICENSE_PKT) == 0 {
c.Emit("error", errors.New("NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER"))
return
}
// after
if (h.securityFlag & LICENSE_PKT) == 0 {
glog.Warnf("skipping non-license packet on licence channel, flags=0x%x", h.securityFlag)
return
} Defensive patterns
Strategy: try-catch
Type guard
func isLicensePacket(h securityHeader) bool {
return (h.securityFlag & LICENSE_PKT) != 0
} Try / catch
client.On("error", func(err error) {
if strings.Contains(err.Error(), "BAD_LICENSE_HEADER") {
glog.Warn("non-license data on licence channel; ignoring")
return
}
glog.Error("rdp error: ", err)
}) Prevention
- Confirm the server implements RDP licensing (or is configured to skip it, e.g. xrdp settings)
- Check PDU framing upstream — a misaligned stream corrupts the security header flags
- Log h.securityFlag values to distinguish real non-license packets from desync
When it happens
Trigger: Server sends a licensing-phase message whose security header flags lack the LICENSE_PKT bit — e.g. a differently framed PDU delivered on the licensing path, stream misalignment making readSecurityHeader read the wrong bytes, or a server that doesn't do licensing but still sends data where a license packet is expected.
Common situations: Connecting to servers with nonstandard or disabled licensing (e.g. some Linux RDP servers, xrdp misconfigurations); connecting when license negotiation state is out of order after a reconnect; a preceding parse bug shifting bytes so flags read incorrectly.
Related errors
- Not a valid license packet
- Unsupport FastPathPDU type 0x%x
- Unknown FastPathPDU type 0x%x
- unsupported Capability type 0x%04x
- Unknown data pdu type2 0x%02x
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/3699ed2eb698ff82.
Report an issue: GitHub.