Authentication
Epirootkit uses SHA-256 password hashing with constant-time comparison to authenticate access, both locally and remotely.
Password Flow
The password is never stored in plain text inside the kernel module. Here's how it flows from installation to authentication:
- At install time: the
install.shscript takes a plain-text password, hashes it with SHA-256, and passes the hash as a module parameter.password_hash=$(echo -n "$password" | sha256sum | cut -d' ' -f1)insmod epirootkit.ko password_hash="$password_hash" ... - At runtime: the hash is stored in the
password_hashkernel parameter (read-only, permission0400). - On authentication: the operator provides the password (in the browser UI). The C2 server hashes the password with SHA-256 and forwards the 64-character hex digest to the module for verification. The module compares the received 64-char hex string against the stored
password_hash.
For local escalation via /proc/epirootkit, the user must write the SHA-256 hash directly (64 hex characters). For remote authentication via C2, the server hashes the operator's plain-text password with SHA-256 and sends the 64-character hex digest to the module for comparison.
Constant-Time Comparison
The password check uses a custom ct_strcmp function instead of standard strcmp or memcmp. This is a security measure against timing attacks.
A standard string comparison returns as soon as it finds a mismatched character. An attacker could measure response times to determine how many characters of their guess are correct. The constant-time function always compares all 64 characters regardless of where a mismatch occurs:
static int ct_strcmp(const char *a, const char *b, size_t len) {
volatile int diff = 0;
for (size_t i = 0; i < len; i++) {
diff |= a[i] ^ b[i];
}
return diff;
}
Key properties:
- Uses XOR (
^) and OR (|=) to accumulate differences without short-circuiting. - The
volatilequalifier prevents the compiler from optimizing the loop away. - Returns
0only if all bytes are identical.
Validation Rules
The check_password() function enforces these checks before comparing:
- The input must not be
NULL. - The input length must be exactly 64 characters (the length of a SHA-256 hex digest).
- If both checks pass, the constant-time comparison is performed.
Any failure results in AUTH_FAILED being returned.