shadow1ng/fscan · error

ReadApplicationTag invalid data2

Error message

ReadApplicationTag invalid data2

What it means

In the short tag form (tag <= 30), ReadApplicationTag requires the first byte to equal (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) — a single-byte application tag encoding the requested number. Any other byte means the field is not the expected application tag, producing this 'invalid data2' error.

Source

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

	}
	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)
	}
}

func WriteEncodedDomainParams(data []byte, w io.Writer) {
	WriteUniversalTag(TAG_SEQUENCE, true, w)

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Hex-dump the stream at this offset and compare against the expected (CLASS_APPL|PC_CONSTRUCT)|tag byte
  2. Check upstream parsers for off-by-one byte consumption causing misalignment
  3. Verify the target service is RDP and the negotiation sequence completed correctly
  4. If the server encodes small tags in long form, extend ReadApplicationTag to accept both encodings

Example fix

// before
if bb != (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) {
    return 0, errors.New("ReadApplicationTag invalid data2")
}

// after
if bb != (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) {
    return 0, fmt.Errorf("ReadApplicationTag invalid data2: expected 0x%02X, got 0x%02X", (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag), bb)
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check the short-form application tag byte
expected := byte((ber.CLASS_APPL | ber.PC_CONSTRUCT) | (ber.TAG_MASK & tag))
if buf[pos] != expected {
    return fmt.Errorf("bad short-form application tag: got 0x%02X, want 0x%02X", buf[pos], expected)
}

Try / catch

n, err := ber.ReadApplicationTag(tag, r)
if err != nil {
    if strings.Contains(err.Error(), "invalid data2") {
        return fmt.Errorf("unexpected PDU where application tag %d expected: %w", tag, err)
    }
    return err
}

Prevention

When it happens

Trigger: ReadConnectResponse calling ReadApplicationTag with tag <= 30 when the first byte is not the matching single-byte application tag — e.g. a long-form tag byte, a different class, or complete misalignment.

Common situations: Desynced streams where content bytes are read as tag bytes; servers using long-form encoding for small tags; non-RDP services on port 3389 returning arbitrary bytes.

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/a121abd35f137f38. Report an issue: GitHub.