fix: audit round 2 - DPAPI secrets, pinned hop probe, gost exe hash, admin guard, PID-scoped browser tracking, emergency disengage button, build sidecar
This commit is contained in:
@@ -24,6 +24,11 @@ GOST_RELEASE_ZIP = (
|
||||
# Verified 2026-05-21 against https://github.com/go-gost/gost/releases/download/v3.2.6/
|
||||
GOST_RELEASE_ZIP_SHA256 = "32f4edf3d94b622e67f1979f6f5de82dac62abc0977772cf96215dd199ef7e7b"
|
||||
|
||||
# Pinned SHA256 of the extracted ``gost.exe`` binary inside the v3.2.6 zip.
|
||||
# Recorded the first time the zip is unpacked and verified on every reuse, so
|
||||
# a tampered or partially-overwritten exe on disk forces a clean re-download.
|
||||
GOST_EXE_SHA256_FILE = "gost.exe.sha256"
|
||||
|
||||
|
||||
|
||||
def _add_defender_exclusion(path: Path) -> None:
|
||||
@@ -58,10 +63,55 @@ def _verify_zip_sha256(data: bytes, expected: str) -> None:
|
||||
log.info("GOST zip SHA256 OK: %s", actual)
|
||||
|
||||
|
||||
def _hash_file(path: Path) -> str:
|
||||
h = hashlib.sha256()
|
||||
with open(path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(64 * 1024), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest().lower()
|
||||
|
||||
|
||||
def _read_pinned_exe_hash(exe: Path) -> str | None:
|
||||
sidecar = exe.with_suffix(exe.suffix + ".sha256")
|
||||
try:
|
||||
return sidecar.read_text(encoding="utf-8").strip().lower() or None
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def _write_pinned_exe_hash(exe: Path, digest: str) -> None:
|
||||
sidecar = exe.with_suffix(exe.suffix + ".sha256")
|
||||
try:
|
||||
sidecar.write_text(digest, encoding="utf-8")
|
||||
except OSError as exc:
|
||||
log.warning("Could not write gost.exe hash sidecar: %s", exc)
|
||||
|
||||
|
||||
def ensure_gost(target: Path | None = None) -> Path:
|
||||
exe = target or gost_exe_path()
|
||||
if exe.is_file() and exe.stat().st_size > 10_000:
|
||||
return exe
|
||||
# Re-verify the on-disk binary against the sidecar pin so a tampered
|
||||
# exe cannot persist across launches.
|
||||
pinned = _read_pinned_exe_hash(exe)
|
||||
if pinned:
|
||||
actual = _hash_file(exe)
|
||||
if actual == pinned:
|
||||
return exe
|
||||
log.warning(
|
||||
"gost.exe SHA256 mismatch on reuse — re-downloading.\n"
|
||||
" pinned: %s\n actual: %s",
|
||||
pinned, actual,
|
||||
)
|
||||
try:
|
||||
exe.unlink()
|
||||
except OSError as exc:
|
||||
log.warning("Could not remove tampered gost.exe: %s", exc)
|
||||
else:
|
||||
# First reuse after older versions: record the current hash so the
|
||||
# next run can verify. Mark as trusted-on-first-use only when zip
|
||||
# SHA verification has already happened earlier in the session.
|
||||
_write_pinned_exe_hash(exe, _hash_file(exe))
|
||||
return exe
|
||||
exe.parent.mkdir(parents=True, exist_ok=True)
|
||||
# Add exclusion BEFORE downloading so Defender doesn't nuke it on write
|
||||
_add_defender_exclusion(exe)
|
||||
@@ -82,6 +132,8 @@ def ensure_gost(target: Path | None = None) -> Path:
|
||||
shutil.copyfileobj(src, dst)
|
||||
# Add exclusion again after write in case Defender scanned during extraction
|
||||
_add_defender_exclusion(exe)
|
||||
# Pin the freshly-extracted exe so subsequent launches can re-verify it.
|
||||
_write_pinned_exe_hash(exe, _hash_file(exe))
|
||||
log.info("GOST installed at %s", exe)
|
||||
return exe
|
||||
|
||||
|
||||
Reference in New Issue
Block a user