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
- Verify the target is a real RDP server (telnet/nc banner check or Wireshark capture of the X.224 exchange)
- Dump the raw bytes around the enumerated field and compare the CONNECT_RESPONSE layout against T.125 to find the misalignment
- Ensure X.224 negotiation (requestedProtocols) matches what the server supports so the response is well-formed
- 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
- Always probe the port for an RDP server before full connect
- Keep requestedProtocols aligned with what the server advertises
- Log raw handshake bytes at debug level to diagnose tag mismatches quickly
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
- Bad integer tag
- enumerate size is wrong, get %v, expect 1
- BER length may be 1 or 2
- wrong size
- ReadApplicationTag invalid data
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/0002b8094dd7c8d3.
Report an issue: GitHub.