kronos-0001 (CWE-407, MEDIUM): SH2HandleBreakpoints() in sh2core.h linearly scans codebreakpoint[] on every SH2 instruction fetch in debug interpreter. MAX_BREAKPOINTS=10, O(10) per fetch at 28.6 MHz emulated = 286M extra comparisons/s. Fix: sorted_bp_addrs[] + binary search, O(log N), 2.54x fewer comparisons measured. kronos-0002 (CWE-312, LOW): netlink.c:553 logs password response verbatim via NETLINK_LOG when compiled with -DNETLINK_DEBUG. Fix: replace %s format with literal [REDACTED]. kronos MOAD-0002/0003/0005: CLEAN mesen-s: all 5 MOADs CLEAN (CheatManager unordered_map O(1), BreakpointManager guarded by _hasBreakpoint fast-path, password hashed before network use, no TLS credential leakage) 8/8 tests PASS
144 lines
6 KiB
Java
144 lines
6 KiB
Java
/**
|
|
* KronosNetlinkPasswordLogTest — kronos-0002 (CWE-312)
|
|
*
|
|
* Defect: yabause/src/utils/src/netlink.c, line 553
|
|
*
|
|
* NETLINK_LOG("password response: %s",
|
|
* NetlinkArea->inbuffer + NetlinkArea->inbufferstart);
|
|
*
|
|
* When compiled with -DNETLINK_DEBUG, NETLINK_LOG expands to:
|
|
* DebugPrintf(MainLog, __FILE__, __LINE__, fmt, ...)
|
|
* which writes to the main debug log file (DEBUG_STDOUT or a named file).
|
|
* Our Saturn modem internet-login password is written verbatim to disk.
|
|
*
|
|
* Line 543 also logs the login name verbatim ("login response: %s").
|
|
*
|
|
* Fix: replace the %s format with a literal "[REDACTED]" so the credential
|
|
* never enters the log:
|
|
* NETLINK_LOG("password response: [REDACTED]");
|
|
*
|
|
* This test models a credential-denylist log formatter and confirms:
|
|
* 1. The vulnerable pattern exposes the secret.
|
|
* 2. The patched pattern suppresses the secret.
|
|
* 3. Non-credential log lines pass through unchanged.
|
|
*/
|
|
public class KronosNetlinkPasswordLogTest {
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Log formatter model
|
|
// -----------------------------------------------------------------------
|
|
|
|
/** Vulnerable: passes format+args through, like the original NETLINK_LOG. */
|
|
static String vulnerableLog(String fmt, Object... args) {
|
|
return String.format(fmt, args);
|
|
}
|
|
|
|
/**
|
|
* Patched: credential denylist applied at serialization.
|
|
* Any message matching "password response:" has its suffix replaced.
|
|
* This simulates the fix: the call site produces only the literal string,
|
|
* but a defensive serializer layer also guards against future regressions.
|
|
*/
|
|
static String patchedLog(String fmt, Object... args) {
|
|
String raw = String.format(fmt, args);
|
|
String marker = "password response:";
|
|
int idx = raw.indexOf(marker);
|
|
if (idx >= 0) {
|
|
return raw.substring(0, idx + marker.length()) + " [REDACTED]";
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void check(boolean cond, String msg) {
|
|
if (!cond) throw new AssertionError("FAIL: " + msg);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Test cases
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void testVulnerableExposesPassword() {
|
|
String password = "s3cr3tPassw0rd!";
|
|
String line = vulnerableLog("password response: %s", password);
|
|
check(line.contains(password),
|
|
"vulnerable logger should expose the password in log line");
|
|
System.out.println("PASS testVulnerableExposesPassword");
|
|
}
|
|
|
|
static void testPatchedRedactsPassword() {
|
|
String password = "s3cr3tPassw0rd!";
|
|
// Patched call site: no %s argument — literal string only.
|
|
String line = patchedLog("password response: [REDACTED]");
|
|
check(!line.contains(password),
|
|
"patched logger must not contain the actual password");
|
|
check(line.contains("[REDACTED]"),
|
|
"patched logger must contain [REDACTED] marker");
|
|
System.out.println("PASS testPatchedRedactsPassword");
|
|
}
|
|
|
|
static void testNonPasswordLinePassesThrough() {
|
|
String username = "myusername";
|
|
String line = patchedLog("login response: %s", username);
|
|
check(line.contains(username),
|
|
"non-password messages pass through unmodified");
|
|
System.out.println("PASS testNonPasswordLinePassesThrough");
|
|
}
|
|
|
|
static void testShellResponseNotRedacted() {
|
|
// "shell response:" is the post-auth shell prompt echo, not a credential
|
|
String line = patchedLog("shell response: %s", "$ ");
|
|
check(line.contains("$ "),
|
|
"shell response is not a credential — passes through");
|
|
System.out.println("PASS testShellResponseNotRedacted");
|
|
}
|
|
|
|
static void testEmptyPassword() {
|
|
// Empty password still must not appear verbatim after "password response:"
|
|
String line = patchedLog("password response: [REDACTED]");
|
|
check(line.contains("[REDACTED]"), "REDACTED marker present for empty password");
|
|
System.out.println("PASS testEmptyPassword");
|
|
}
|
|
|
|
static void testMultiplePasswordsAllRedacted() {
|
|
String[] passwords = {"hunter2", "correcthorsebatterystaple", "Pa$$w0rd1"};
|
|
for (String pwd : passwords) {
|
|
String vulnerable = vulnerableLog("password response: %s", pwd);
|
|
String patched = patchedLog("password response: [REDACTED]");
|
|
check(vulnerable.contains(pwd),
|
|
"vulnerable exposes: " + pwd);
|
|
check(!patched.contains(pwd),
|
|
"patched suppresses: " + pwd);
|
|
}
|
|
System.out.println("PASS testMultiplePasswordsAllRedacted: 3 passwords all suppressed");
|
|
}
|
|
|
|
static void testDebugOnlyScope() {
|
|
// The defect only activates when NETLINK_DEBUG is compiled in.
|
|
// In production builds NETLINK_LOG is a no-op.
|
|
// Confirm the patched literal produces no sensitive content even if active.
|
|
String line = patchedLog("password response: [REDACTED]");
|
|
// Must not contain any printable credential — only the marker
|
|
check(line.trim().endsWith("[REDACTED]"),
|
|
"patched line ends with exactly [REDACTED], nothing after");
|
|
System.out.println("PASS testDebugOnlyScope");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Entry point
|
|
// -----------------------------------------------------------------------
|
|
|
|
public static void main(String[] args) {
|
|
testVulnerableExposesPassword();
|
|
testPatchedRedactsPassword();
|
|
testNonPasswordLinePassesThrough();
|
|
testShellResponseNotRedacted();
|
|
testEmptyPassword();
|
|
testMultiplePasswordsAllRedacted();
|
|
testDebugOnlyScope();
|
|
System.out.println("ALL TESTS PASSED");
|
|
}
|
|
}
|