shadow1ng/fscan · error

ReadApplicationTag bad tag

Error message

ReadApplicationTag bad tag

What it means

In the long tag form (tag > 30), ReadApplicationTag validates the identifier byte and then reads the actual tag number byte; if that byte does not equal the requested tag, the parsed application tag is the wrong one and this error is returned.

Source

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

func WriteBoolean(b bool, w io.Writer) {
	bb := uint8(0)
	if b {
		bb = uint8(0xff)
	}
	WriteUniversalTag(TAG_BOOLEAN, false, w)
	WriteLength(1, w)
	core.WriteUInt8(bb, w)
}

func ReadApplicationTag(tag uint8, r io.Reader) (int, error) {
	bb, _ := core.ReadUInt8(r)
	if tag > 30 {
		if bb != (CLASS_APPL|PC_CONSTRUCT)|TAG_MASK {
			return 0, errors.New("ReadApplicationTag invalid data")
		}
		bb, _ := core.ReadUInt8(r)
		if bb != tag {
			return 0, errors.New("ReadApplicationTag bad tag")
		}
	} else {
		if bb != (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) {
			return 0, errors.New("ReadApplicationTag invalid data2")
		}
	}
	return ReadLength(r)
}

func WriteApplicationTag(tag uint8, size int, w io.Writer) {
	if tag > 30 {
		core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|TAG_MASK, w)
		core.WriteUInt8(tag, w)
		WriteLength(size, w)
	} else {
		core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag), w)
		WriteLength(size, w)
	}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Log/inspect the received tag byte to see which PDU the server actually sent
  2. Verify X.224 negotiation succeeded (check the CC result byte) before parsing CONNECT_RESPONSE
  3. Fix any upstream desync so the tag byte is read at the correct offset
  4. Compare the server's PDU numbering against the T.125 spec and adjust the expected tag constant if it is a known variant

Example fix

// before
if bb != tag {
    return 0, errors.New("ReadApplicationTag bad tag")
}

// after
if bb != tag {
    return 0, fmt.Errorf("ReadApplicationTag bad tag: expected %d, got %d", tag, bb)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// peek the tag number byte and compare before full parse
if len(buf) > pos+1 && buf[pos+1] != expectedTag {
    return fmt.Errorf("server sent application tag %d, expected %d", buf[pos+1], expectedTag)
}

Try / catch

n, err := ber.ReadApplicationTag(101, r)
if err != nil {
    if strings.Contains(err.Error(), "bad tag") {
        // server sent a different PDU — inspect which one and handle
    }
    return err
}

Prevention

When it happens

Trigger: ReadConnectResponse requesting a specific high application tag (e.g. 101) but the stream carries a different application tag number at that position — a different PDU arrived where CONNECT_RESPONSE was expected.

Common situations: Server rejecting negotiation and sending a different application-tagged PDU; desynced parsing misreading content bytes as the tag byte; nonstandard server PDU numbering.

Related errors


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