Skip to content

WAL and last-record files created with 0666 permissions, forgeable CRC64 integrity #561

Description

@yacovm

Details

Every file the WAL package creates — the append-only consensus log (wal.go) and the last-record crash-recovery file (last_record.go) — is opened with mode 0666. The effective permissions are 0666 masked by the process umask: under the common 022 umask the files are world-readable; under 002 they are group-writable; under a 0 umask (not unusual for daemons/containers that set it explicitly) they are world-writable.

These files are the node's durable consensus safety state: signed votes, notarizations, finalizations, and block records that are replayed verbatim into the consensus engine at startup (Epoch.restoreFromWal), and the last-record file that overrides the replayed state. The only integrity mechanism on the stored records is a CRC64/ECMA checksum, which is a public, linear, non-cryptographic function — any party able to write the file can recompute valid checksums for arbitrary record bytes.

Concrete exploit: on a multi-user host (or a container/host where another compromised low-privilege service shares the filesystem) with a permissive umask, a local unprivileged user opens the WAL file, replaces or appends records (e.g., deletes the record showing the node voted for block B in round r, or injects a fabricated record), recomputes the CRC64 framing, and waits for the node to restart. Replay accepts the forged log wholesale — record framing and CRC both validate — so the attacker controls the node's remembered consensus state, e.g. inducing it to re-vote (equivocate) or to resume from attacker-chosen rounds. Even with the default 022 umask, the world-readable mode discloses the node's full vote/notarization history to any local account.

Since the files carry security-critical state, they should be created 0600 (or the package should accept a caller-specified restrictive mode) rather than delegating protection entirely to the deployment's umask.

Evidence

  1. wal/wal.go:12–14
    WalPermissions is 0666: the consensus WAL file is requested world-readable and world-writable; the effective mode is only reduced by the process umask (commonly 022, still leaving the file world-readable; umask 002/000 leaves it group/world-writable).
  2. wal/wal.go:34
    os.OpenFile creates the WAL file with the 0666 mode on first append/read.
  3. wal/last_record.go:75
    The last-record side file — the crash-recovery anchor that overrides WAL state at startup — is also created with WalPermissions (0666).
  4. wal/last_record.go:107–109
    Truncate() re-creates the last-record file with the same 0666 mode.
  5. wal/record.go:26–34
    Record integrity is protected only by CRC64/ECMA, which is non-cryptographic and computable by anyone; a local user with write access can rewrite or inject records that pass all replay validation.

Impact

Confidentiality LOW: WAL contents (votes, blocks) are largely public consensus data, so local read exposure is limited. Integrity HIGH in the writable-umask case: full forgery of replayed consensus state, since CRC64 provides no protection against deliberate modification. Availability LOW: a local writer can also corrupt the log, triggering truncation/crash paths.

Reproduction steps

  1. Requires a local account (or co-resident compromised service) on the node's host — PR LOW, AV LOCAL. Read access works under the default 022 umask; write/tamper access additionally requires the daemon to run with a permissive umask (002/000) and a restart of the node to replay the forged log — deployment conditions, so AT PRESENT. No user interaction; no defenses to defeat (CRC64 is trivially forgeable).

Recommended fix

Security-critical WAL and last-record files are created with permissive 0666 mode, relying on the deployment umask; combined with the forgeable CRC64 framing, any principal with write access can fabricate valid consensus replay state. Fix criteria: Files holding consensus state must be created with owner-only permissions (0600) or an explicit caller-provided restrictive mode, independent of umask. Verified by creating a WAL under a 0 umask and asserting the resulting file mode grants no group/other access.


Severity: MEDIUM
Status: Open
Category: Incorrect permissions
CWE: CWE-276
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-08-21


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions