Merge pull request #101 from okhsunrog/fix/data-system-files-not-world-readable

fix: tighten /data/system/vpnhide_*.txt to 0640 root:system
This commit is contained in:
Danila Gornushko 2026-04-26 16:59:57 +03:00 committed by GitHub
commit 7c7d725991
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 71 additions and 12 deletions

View file

@ -86,17 +86,36 @@ fi
# Resolve lsposed targets → /data/system/vpnhide_uids.txt
# Create persist dir if needed (for first-time installs)
mkdir -p /data/adb/vpnhide_lsposed 2>/dev/null
# Mode 0640 + group=system: system_server (UID 1000, in group `system`)
# reads via the group bit; untrusted apps fall to "other" and get EACCES.
# Default 0644 was a fingerprint vector — `/data/system/` itself is mode
# 0775 traversable by untrusted, so any o+r file is enumerable + readable.
if [ -f "$LSPOSED_TARGETS" ]; then
LSPOSED_UIDS="$(resolve_uids "$LSPOSED_TARGETS")"
if [ -n "$LSPOSED_UIDS" ]; then
echo "$LSPOSED_UIDS" > "$SS_UIDS_FILE"
chmod 644 "$SS_UIDS_FILE"
chmod 640 "$SS_UIDS_FILE"
chown root:system "$SS_UIDS_FILE"
chcon u:object_r:system_data_file:s0 "$SS_UIDS_FILE" 2>/dev/null
count="$(echo "$LSPOSED_UIDS" | wc -l)"
log -t vpnhide "lsposed: wrote $count UIDs to $SS_UIDS_FILE"
else
echo > "$SS_UIDS_FILE"
chmod 644 "$SS_UIDS_FILE"
chmod 640 "$SS_UIDS_FILE"
chown root:system "$SS_UIDS_FILE"
log -t vpnhide "lsposed: no UIDs resolved"
fi
fi
# Migrate pre-PR files written by older versions with mode 0644: any
# vpnhide_*.txt the lsposed app may have left in /data/system/. Touch
# only files that already exist; don't create new ones here.
for f in "$SS_UIDS_FILE" \
/data/system/vpnhide_hidden_pkgs.txt \
/data/system/vpnhide_observer_uids.txt; do
if [ -f "$f" ]; then
chmod 640 "$f"
chown root:system "$f"
chcon u:object_r:system_data_file:s0 "$f" 2>/dev/null
fi
done

View file

@ -382,21 +382,29 @@ private fun buildHidingSaveCommand(
val parts = mutableListOf<String>()
// Hidden list: package names, one per line.
// Mode 0640 + group=system: system_server reads via the group bit;
// untrusted apps get EACCES because /data/system/ is mode 0775 (the
// file's "other" bits decide reachability). Prevents apps from
// enumerating the hidden-package list to fingerprint vpnhide.
val hiddenBody = "$header\n" + hiddenPkgs.joinToString("\n") + if (hiddenPkgs.isNotEmpty()) "\n" else ""
val hiddenB64 = encode(hiddenBody)
parts +=
"echo '$hiddenB64' | base64 -d > $SS_HIDDEN_PKGS_FILE && chmod 644 $SS_HIDDEN_PKGS_FILE" +
"echo '$hiddenB64' | base64 -d > $SS_HIDDEN_PKGS_FILE" +
" && chmod 640 $SS_HIDDEN_PKGS_FILE" +
" && chown root:system $SS_HIDDEN_PKGS_FILE" +
" && chcon u:object_r:system_data_file:s0 $SS_HIDDEN_PKGS_FILE 2>/dev/null; true"
// Observer list: resolved UIDs.
// Observer list: resolved UIDs. Same 0640 root:system rationale.
if (observerPkgs.isNotEmpty()) {
parts += buildHidingUidResolver(observerPkgs, SS_OBSERVER_UIDS_FILE)
parts += "chmod 644 $SS_OBSERVER_UIDS_FILE 2>/dev/null"
parts += "chmod 640 $SS_OBSERVER_UIDS_FILE 2>/dev/null"
parts += "chown root:system $SS_OBSERVER_UIDS_FILE 2>/dev/null"
parts += "chcon u:object_r:system_data_file:s0 $SS_OBSERVER_UIDS_FILE 2>/dev/null; true"
} else {
parts +=
"echo > $SS_OBSERVER_UIDS_FILE 2>/dev/null;" +
" chmod 644 $SS_OBSERVER_UIDS_FILE 2>/dev/null;" +
" chmod 640 $SS_OBSERVER_UIDS_FILE 2>/dev/null;" +
" chown root:system $SS_OBSERVER_UIDS_FILE 2>/dev/null;" +
" chcon u:object_r:system_data_file:s0 $SS_OBSERVER_UIDS_FILE 2>/dev/null; true"
}

View file

@ -389,12 +389,20 @@ private fun buildSaveCommand(
}
// Resolve lsposed UIDs -> /data/system/vpnhide_uids.txt
// Mode 0640 + group=system: system_server reads via the group bit;
// untrusted apps get EACCES because /data/system/ is mode 0775 (parent
// is traversable, file's "other" bits decide). Prevents apps from
// enumerating the target UID list to fingerprint vpnhide.
if (lsposedPkgs.isNotEmpty()) {
parts += buildUidResolver(lsposedPkgs, SS_UIDS_FILE)
parts += "chmod 644 $SS_UIDS_FILE 2>/dev/null"
parts += "chmod 640 $SS_UIDS_FILE 2>/dev/null"
parts += "chown root:system $SS_UIDS_FILE 2>/dev/null"
parts += "chcon u:object_r:system_data_file:s0 $SS_UIDS_FILE 2>/dev/null"
} else {
parts += "echo > $SS_UIDS_FILE 2>/dev/null; true"
parts +=
"echo > $SS_UIDS_FILE 2>/dev/null" +
" && chmod 640 $SS_UIDS_FILE 2>/dev/null" +
" && chown root:system $SS_UIDS_FILE 2>/dev/null; true"
}
return parts.joinToString(" ; ")

View file

@ -172,8 +172,13 @@ internal fun ensureSelfInTargets(selfPkg: String): Boolean {
(hiddenExisting + selfPkg).sorted().joinToString("\n") + "\n"
val b64 = Base64.encodeToString(body.toByteArray(), Base64.NO_WRAP)
suExec(
// Mode 0640 + group=system: system_server reads via the group
// bit; untrusted apps fall to "other" and get EACCES.
// /data/system/ itself is mode 0775 traversable by untrusted —
// a plain 0644 here used to be enumerable + readable.
"echo '$b64' | base64 -d > $SS_HIDDEN_PKGS_FILE" +
" && chmod 644 $SS_HIDDEN_PKGS_FILE" +
" && chmod 640 $SS_HIDDEN_PKGS_FILE" +
" && chown root:system $SS_HIDDEN_PKGS_FILE" +
" && chcon u:object_r:system_data_file:s0 $SS_HIDDEN_PKGS_FILE 2>/dev/null; true",
)
VpnHideLog.i(TAG, "ensureSelfInTargets: added $selfPkg to $SS_HIDDEN_PKGS_FILE")
@ -203,7 +208,7 @@ internal fun ensureSelfInTargets(selfPkg: String): Boolean {
append(" ; fi")
append(" ; EXISTING2=\$(cat $SS_UIDS_FILE 2>/dev/null)")
append(
" ; echo \"\$EXISTING2\" | grep -q \"^\$U\$\" || { echo \"\$U\" >> $SS_UIDS_FILE; chmod 644 $SS_UIDS_FILE; chcon u:object_r:system_data_file:s0 $SS_UIDS_FILE 2>/dev/null; }",
" ; echo \"\$EXISTING2\" | grep -q \"^\$U\$\" || { echo \"\$U\" >> $SS_UIDS_FILE; chmod 640 $SS_UIDS_FILE; chown root:system $SS_UIDS_FILE; chcon u:object_r:system_data_file:s0 $SS_UIDS_FILE 2>/dev/null; }",
)
append(" ; done")
append("; fi")

View file

@ -69,15 +69,34 @@ ${expanded}"; fi
mkdir -p /data/adb/vpnhide_lsposed 2>/dev/null
if [ -f "$LSPOSED_TARGETS" ]; then
LSPOSED_UIDS="$(resolve_uids "$LSPOSED_TARGETS")"
# Mode 0640 + group=system: system_server (UID 1000, in group `system`)
# reads via the group bit; untrusted apps fall to "other" and get EACCES.
# Default 0644 was a fingerprint vector — `/data/system/` itself is mode
# 0775 traversable by untrusted, so any o+r file is enumerable + readable.
if [ -n "$LSPOSED_UIDS" ]; then
echo "$LSPOSED_UIDS" > "$SS_UIDS_FILE"
chmod 644 "$SS_UIDS_FILE"
chmod 640 "$SS_UIDS_FILE"
chown root:system "$SS_UIDS_FILE"
chcon u:object_r:system_data_file:s0 "$SS_UIDS_FILE" 2>/dev/null
count="$(echo "$LSPOSED_UIDS" | wc -l)"
log -t vpnhide "zygisk: wrote $count lsposed UIDs to $SS_UIDS_FILE"
else
echo > "$SS_UIDS_FILE"
chmod 644 "$SS_UIDS_FILE"
chmod 640 "$SS_UIDS_FILE"
chown root:system "$SS_UIDS_FILE"
log -t vpnhide "zygisk: no lsposed UIDs resolved"
fi
fi
# Migrate pre-PR files written by older versions with mode 0644: any
# vpnhide_*.txt the lsposed app may have left in /data/system/. Touch
# only files that already exist; don't create new ones here.
for f in "$SS_UIDS_FILE" \
/data/system/vpnhide_hidden_pkgs.txt \
/data/system/vpnhide_observer_uids.txt; do
if [ -f "$f" ]; then
chmod 640 "$f"
chown root:system "$f"
chcon u:object_r:system_data_file:s0 "$f" 2>/dev/null
fi
done