Skip to content

ParseConnBlob panics on a ConnBlob whose region or node array contains a null #51

Description

@CharmingGroot

ParseConnBlob dereferences the elements of the region and node arrays without a nil check. CBOR nulls decode to nil pointers, so a blob with null in either array crashes the process.

Reproducer

func TestNilRegion(t *testing.T) {
	b, _ := cbor.Marshal(map[string]any{
		"p": key.NewNode().Public().AppendTo(nil),
		"r": []any{nil},
	})
	ParseConnBlob(ConnBlob("tc" + base64.RawURLEncoding.EncodeToString(b)))
}
panic: runtime error: invalid memory address or nil pointer dereference
	github.com/tailscale/tailcat.(*wireRegion).derpRegion(...)
		wire.go:94
	github.com/tailscale/tailcat.ParseConnBlob(...)
		tailcat.go:849

The same happens one level down with {"r": [{"i": 1, "N": [null]}]}, which panics at wire.go:100 on a nil *wireNode.

Where

ParseConnBlob calls wr.derpRegion() on each element of w.Region (tailcat.go:848-850). derpRegion is a pointer method that reads w.RegionID immediately, and it then ranges over w.Nodes reading n.Name and friends. Neither loop checks for nil.

Why it matters

Blobs are attacker-influenced. addrBlobArg treats a CLI argument as a DNS name when it is not a tc-prefixed blob, looks up its TXT records, and takes the tailcat= value as the blob (cmd/tailcat/tailcat.go:472-490). So the owner of a DNS name — or anyone who can get a blob pasted into a command — can crash the client.

This is the same class of bug as #26, which rejected malformed public keys in NodePublic.UnmarshalBinary for the same reason. That change validated the key lengths, but the array elements were left unchecked.

Callers cannot defend against it: both call sites in cmd/tailcat probe with ParseConnBlob and test err == nil, which a panic bypasses.

ParseConnBlobRaw is not affected — it returns the wire form without the conversion, so tailcat parse prints "Region": [null] rather than crashing.

Version

main at 1fba818, go1.27.0.

I can send a PR with a fix and a test that fails before the change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions