Skip to content

Randomize the CLI socket to remove a /proc/net/unix fingerprint - #892

Closed
JingMatrix wants to merge 1 commit into
masterfrom
randomize-cli-socket
Closed

Randomize the CLI socket to remove a /proc/net/unix fingerprint#892
JingMatrix wants to merge 1 commit into
masterfrom
randomize-cli-socket

Conversation

@JingMatrix

Copy link
Copy Markdown
Owner

The daemon bound its CLI socket to a fixed filesystem path, /data/adb/lspd/.cli_sock. A filesystem socket's bind address is copied verbatim into /proc/net/unix, which is world-readable across Android's single global network namespace — so any unprivileged app reads the full path, regardless of the 0700 directory guarding the node on disk. That constant string was a stable, version-independent signature: an integrity scanner only had to grep for it, even with every other surface clean (#891).

Permissions were never the leak, so the fix changes the address itself. The socket now binds in the abstract namespace under a name minted from 128 random bits at each daemon start, appearing as @<hex> — no path, nothing framework-specific, different every boot.

Since the daemon and CLI are separate processes, they can no longer share the name through a compile-time constant. The daemon writes the current name to a root-only file (/data/adb/lspd/.sock) and the CLI reads it at connect time; that file is not a socket, so it never appears in /proc/net/unix.

Both auth layers are unchanged — the compiled-in CLI_TOKEN still gates every connection and the CLI still requires root — so the abstract namespace's lack of a filesystem-permission gate costs nothing exploitable. The bind carries no file, so the startup delete and shutdown unlink are gone.

Verified on-device: the socket binds as @<hex>, the old constant is absent from /proc/net/unix, cli status round-trips, and there is no SELinux connectto denial.

Closes #891.

The daemon bound its CLI socket to a fixed filesystem path,
/data/adb/lspd/.cli_sock. A filesystem socket's bind address is copied
verbatim into /proc/net/unix, which is world-readable across Android's
single global network namespace, so any unprivileged app reads the full
path regardless of the 0700 directory guarding the node on disk. That
constant string was a stable, version-independent signature: a scanner
only had to grep for it, even with every other surface clean (#891).

Permissions were never the leak, so the fix changes the address itself.
The socket now binds in the abstract namespace under a name minted from
128 random bits at each daemon start, appearing as @<hex> - no path,
nothing framework-specific, different every boot.

Since the daemon and CLI are separate processes, they can no longer
share the name through a compile-time constant. The daemon writes the
current name to a root-only file (/data/adb/lspd/.sock) and the CLI reads
it at connect time; that file is not a socket, so it never appears in
/proc/net/unix.

Both auth layers are unchanged - the compiled-in CLI_TOKEN still gates
every connection and the CLI still requires root - so the abstract
namespace's lack of a filesystem-permission gate costs nothing
exploitable. The bind carries no file, so the startup delete and shutdown
unlink are gone.
@JingMatrix

Copy link
Copy Markdown
Owner Author

TODO before megre: add docs of using the cli in the daemon subproject README.

@JingMatrix

Copy link
Copy Markdown
Owner Author

Closing this. The premise is wrong.

The change rests on "any unprivileged app reads the full path" from /proc/net/unix with "no privilege required." That is false. /proc/net/unix is labeled proc_net, and the untrusted_app domain has no read on it — a test app gets:

open failed: EACCES (Permission denied)
avc: denied { read } name="unix" scontext=u:r:untrusted_app:s0 \
  tcontext=u:object_r:proc_net:s0 tclass=file permissive=0

I also ran the scanner from #891 on a device where .cli_sock is present in the global table: it runs as untrusted_app with no root, its socket check reads /proc/self/net/unix, returns Clean, and never sees the path. #891 is closed as unsubstantiated — its attached report contains no socket leak at all.

So there is no app-visible fingerprint to remove here, and the justification in the body and the two code comments is incorrect. If we still want the CLI socket randomized for consistency with the Dex2Oat abstract socket, that's a separate, honestly-scoped change against a root/shell-context reader — not this.

@JingMatrix JingMatrix closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce predictable fingerprint exposure by making the CLI socket path non-deterministic

1 participant