shadow1ng/fscan · error

invalid ber tag

Error message

invalid ber tag

What it means

ReadEnumerated parses a BER-encoded ENUMERATED value in the X.224/T.125 CONNECT_RESPONSE. It first checks the universal tag byte equals TAG_ENUMERATED; if the byte read from the stream does not match, the stream is not a valid BER enumerated field and this error is returned.

Source

Thrown at libs/grdp/protocol/t125/ber/ber.go:46

	TAG_INTEGER               = 0x02
	TAG_BIT_STRING            = 0x03
	TAG_OCTET_STRING          = 0x04
	TAG_OBJECT_IDENFIER       = 0x06
	TAG_ENUMERATED            = 0x0A
	TAG_SEQUENCE              = 0x10
	TAG_SEQUENCE_OF           = 0x10
)

func berPC(pc bool) uint8 {
	if pc {
		return PC_CONSTRUCT
	}
	return PC_PRIMITIVE
}

func ReadEnumerated(r io.Reader) (uint8, error) {
	if !ReadUniversalTag(TAG_ENUMERATED, false, r) {
		return 0, errors.New("invalid ber tag")
	}
	length, err := ReadLength(r)
	if err != nil {
		return 0, err
	}
	if length != 1 {
		return 0, errors.New(fmt.Sprintf("enumerate size is wrong, get %v, expect 1", length))
	}
	return core.ReadUInt8(r)
}

func ReadUniversalTag(tag uint8, pc bool, r io.Reader) bool {
	bb, _ := core.ReadUInt8(r)
	return bb == (CLASS_UNIV|berPC(pc))|(TAG_MASK&tag)
}

func WriteUniversalTag(tag uint8, pc bool, w io.Writer) {
	core.WriteUInt8((CLASS_UNIV|berPC(pc))|(TAG_MASK&tag), w)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the target is a real RDP server (telnet/nc banner check or Wireshark capture of the X.224 exchange)
  2. Dump the raw bytes around the enumerated field and compare the CONNECT_RESPONSE layout against T.125 to find the misalignment
  3. Ensure X.224 negotiation (requestedProtocols) matches what the server supports so the response is well-formed
  4. Check for TLS/proxy interference between client and server corrupting the stream

Example fix

// before
if !ReadUniversalTag(TAG_ENUMERATED, false, r) {
    return 0, errors.New("invalid ber tag")
}

// after
if !ReadUniversalTag(TAG_ENUMERATED, false, r) {
    bb, _ := r.(io.ByteReader)
    _ = bb
    return 0, fmt.Errorf("invalid ber tag: expected ENUMERATED (0x0A), got 0x%02X", lastByte)
}
Defensive patterns

Strategy: validation

Validate before calling

// validate the endpoint before running the full handshake
func isRdpEndpoint(host string, port int) bool {
    c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 3*time.Second)
    if err != nil { return false }
    defer c.Close()
    // send X.224 CR and check the response starts with a valid RDP negotiation header
    return probeX224(c) == nil
}

Try / catch

if _, err := ber.ReadEnumerated(connBuf); err != nil {
    if strings.Contains(err.Error(), "invalid ber tag") {
        return fmt.Errorf("server response is not a valid T.125 CONNECT_RESPONSE: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: ReadConnectResponse -> ReadEnumerated when the byte at the enumerated field position is not the TAG_ENUMERATED universal tag — i.e. the CONNECT_RESPONSE payload is misaligned, truncated, or not BER-encoded as expected.

Common situations: Pointing the client at a non-RDP service on port 3389; server response desynchronized by earlier protocol mismatch (wrong security/negotiation flags); proxies mangling bytes so field boundaries shift.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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