shadow1ng/fscan · error

NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNE

Error message

NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL

What it means

When the server sends a Channel Join Confirm with confirm != 0 (refused) for a static channel — either the MCS Global Channel or the user's own channel — the library emits this sentinel error. T.125 requires the server to always accept joins for these mandatory static channels; a refusal means a broken or nonstandard server.

Source

Thrown at libs/grdp/protocol/t125/mcs.go:512

	}

	if !readMCSPDUHeader(option, CHANNEL_JOIN_CONFIRM) {
		c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM"))
		return
	}

	confirm, _ := per.ReadEnumerates(r)
	userId, _ := per.ReadInteger16(r)
	userId += MCS_USERCHANNEL_BASE

	if c.userId != userId {
		c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID"))
		return
	}

	channelId, _ := per.ReadInteger16(r)
	if (confirm != 0) && (channelId == uint16(MCS_GLOBAL_CHANNEL_ID) || channelId == c.userId) {
		c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL"))
		return
	}
	glog.Debug("Confirm channelId:", channelId)
	if confirm == 0 && c.serverNetworkData != nil {
		for i := 0; i < int(c.serverNetworkData.ChannelCount); i++ {
			if channelId == c.serverNetworkData.ChannelIdArray[i] {
				var t MCSChannelInfo
				t.ID = channelId
				t.Name = string(c.clientNetworkData.ChannelDefArray[i].Name[:])
				c.channels = append(c.channels, t)
			}
		}
	}
	c.channelsConnected++
	c.connectChannels()
}

func (c *MCSClient) Pack(data []byte, channelId uint16) []byte {

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the target is a standard RDP server; try connecting with mstsc or freerdp to confirm server behavior.
  2. Check whether an RDP gateway/proxy sits in the path and relax its channel restrictions.
  3. Log the refused channelId and confirm code to report/diagnose the server implementation.
  4. If you control the client, treat refusal of a non-critical static channel as fatal-but-reported instead of aborting the whole session.
Defensive patterns

Strategy: fallback

Try / catch

c.On("error", func(err error) {
    if strings.Contains(err.Error(), "SERVER_MUST_CONFIRM_STATIC_CHANNEL") {
        log.Println("server refused static channel join; target is nonconformant or proxied")
    }
})

Prevention

When it happens

Trigger: recvChannelJoinConfirm reads channelId and the condition (confirm != 0) && (channelId == MCS_GLOBAL_CHANNEL_ID || channelId == c.userId) is true, i.e. the server explicitly refused the join for a static channel.

Common situations: Connecting through an RDP proxy/gateway or hardened terminal server that restricts channel joins; a non-Windows RDP implementation with incomplete T.125 support; MITM devices mangling the channel id.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/a92095c3f1c89c4f. Report an issue: GitHub.