snort3-0002: chp_add_candidate_to_tally() in http_url_patterns.cc calls std::find_if over CHPMatchTally vector for each Aho-Corasick HTTP key-pattern match callback, O(M*T) per packet. Fix: add unordered_map index to ChpMatchDescriptor for O(1) lookup. 48x op-count reduction at T=100/M=20. 3/3 PASS. pgbouncer-0001: scram_client_first() logs user->passwd (SCRAM verifier or plaintext password) at slog_debug level, CWE-312. Fix: remove the log line. 5/5 PASS. pgbouncer MOAD-0002/0003/0005 CLEAN (single-threaded libevent loop). snort3 MOAD-0002/0003/0004/0005 CLEAN.
132 lines
6.1 KiB
Java
132 lines
6.1 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* pgbouncer-0001: CWE-312 — SCRAM verifier logged verbatim at debug level
|
|
*
|
|
* Source: src/client.c, function scram_client_first()
|
|
* Line: slog_debug(client, "stored secret = \"%s\"", user->passwd);
|
|
*
|
|
* Defect: During SCRAM-SHA-256 authentication, PgBouncer logs the user's stored
|
|
* secret (user->passwd) at slog_debug level. Depending on the auth_type configured,
|
|
* user->passwd may contain:
|
|
* - A plaintext password (auth_type=plain)
|
|
* - A SCRAM-SHA-256 verifier: "SCRAM-SHA-256$<iterations>:<salt>$<StoredKey>:<ServerKey>"
|
|
*
|
|
* Even the SCRAM verifier is sensitive: it can be used in an offline dictionary attack
|
|
* or, for plaintext passwords, directly as the credential. Logging it to a debug log
|
|
* file violates CWE-312 (Cleartext Storage of Sensitive Information).
|
|
*
|
|
* Fix: Remove the slog_debug line. The log event type ("SCRAM client-first received")
|
|
* is still present on line 1119; no diagnostic value is lost.
|
|
*
|
|
* CVE-applicable: Yes — debug log file exposure of authentication credential.
|
|
* Severity: MEDIUM-HIGH (requires debug log access, but many ops enable debug logging
|
|
* during troubleshooting, leaving credential in log files indefinitely).
|
|
*/
|
|
public class PgBouncerScramSecretLogTest {
|
|
|
|
// Simulate the log capture system
|
|
static List<String> capturedLogs = new ArrayList<>();
|
|
|
|
static void slog_debug(String context, String fmt, Object... args) {
|
|
capturedLogs.add(String.format("[DEBUG][" + context + "] " + fmt, args));
|
|
}
|
|
|
|
// Simulated PgCredentials
|
|
static class PgCredentials {
|
|
String name;
|
|
String passwd;
|
|
boolean mock_auth;
|
|
PgCredentials(String name, String passwd) {
|
|
this.name = name;
|
|
this.passwd = passwd;
|
|
this.mock_auth = false;
|
|
}
|
|
}
|
|
|
|
// --- DEFECTIVE: logs stored secret ---
|
|
static void scram_client_first_defective(String clientContext, PgCredentials user, String clientFirstMessage) {
|
|
slog_debug(clientContext, "SCRAM client-first-message = \"%s\"", clientFirstMessage);
|
|
if (!user.mock_auth) {
|
|
// DEFECT: logs the SCRAM verifier or plaintext password
|
|
slog_debug(clientContext, "stored secret = \"%s\"", user.passwd);
|
|
}
|
|
// ... rest of SCRAM processing
|
|
}
|
|
|
|
// --- FIXED: does not log stored secret ---
|
|
static void scram_client_first_fixed(String clientContext, PgCredentials user, String clientFirstMessage) {
|
|
slog_debug(clientContext, "SCRAM client-first-message = \"%s\"", clientFirstMessage);
|
|
if (!user.mock_auth) {
|
|
// Fixed: no credential logging. Comment in code:
|
|
// /* Do not log user->passwd: it holds the SCRAM verifier or plaintext password (CWE-312). */
|
|
}
|
|
// ... rest of SCRAM processing
|
|
}
|
|
|
|
static void testDefectiveLogsSecret() {
|
|
capturedLogs.clear();
|
|
PgCredentials user = new PgCredentials("alice",
|
|
"SCRAM-SHA-256$4096:c2FsdHNhbHRzYWx0$StoredKeyHere:ServerKeyHere");
|
|
scram_client_first_defective("client:127.0.0.1:5432", user, "n,,n=alice,r=clientnonce");
|
|
|
|
boolean secretFound = capturedLogs.stream().anyMatch(line -> line.contains("stored secret"));
|
|
assert secretFound : "Defective impl should log 'stored secret'";
|
|
System.out.println("PASS defective: secret IS logged: "
|
|
+ capturedLogs.stream().filter(l -> l.contains("stored secret")).findFirst().orElse("?"));
|
|
}
|
|
|
|
static void testFixedDoesNotLogSecret() {
|
|
capturedLogs.clear();
|
|
PgCredentials user = new PgCredentials("alice",
|
|
"SCRAM-SHA-256$4096:c2FsdHNhbHRzYWx0$StoredKeyHere:ServerKeyHere");
|
|
scram_client_first_fixed("client:127.0.0.1:5432", user, "n,,n=alice,r=clientnonce");
|
|
|
|
boolean secretFound = capturedLogs.stream().anyMatch(line -> line.contains("stored secret"));
|
|
assert !secretFound : "Fixed impl must NOT log 'stored secret', got: " + capturedLogs;
|
|
System.out.println("PASS fixed: secret is NOT logged (logs: " + capturedLogs.size() + " line(s))");
|
|
}
|
|
|
|
static void testFixedStillLogsDiagnostic() {
|
|
capturedLogs.clear();
|
|
PgCredentials user = new PgCredentials("bob", "SCRAM-SHA-256$4096:abc$defgh:ijklm");
|
|
scram_client_first_fixed("client:127.0.0.1:5433", user, "n,,n=bob,r=bobnonce");
|
|
|
|
boolean clientFirstLogged = capturedLogs.stream().anyMatch(line -> line.contains("client-first-message"));
|
|
assert clientFirstLogged : "Fixed impl must still log SCRAM client-first-message event";
|
|
System.out.println("PASS fixed: diagnostic 'client-first-message' still logged");
|
|
}
|
|
|
|
static void testPlaintextPasswordNotLogged() {
|
|
capturedLogs.clear();
|
|
PgCredentials user = new PgCredentials("carol", "supersecretpassword123");
|
|
scram_client_first_fixed("client:192.168.1.1:5432", user, "n,,n=carol,r=carolnonce");
|
|
|
|
boolean plainPassFound = capturedLogs.stream().anyMatch(line ->
|
|
line.contains("supersecretpassword123"));
|
|
assert !plainPassFound : "Fixed impl must NOT log plaintext passwords";
|
|
System.out.println("PASS fixed: plaintext password NOT logged");
|
|
}
|
|
|
|
static void testMockAuthNotLogged() {
|
|
capturedLogs.clear();
|
|
PgCredentials user = new PgCredentials("mockuser", "not-a-real-secret");
|
|
user.mock_auth = true;
|
|
// Both defective and fixed skip the branch when mock_auth=true
|
|
scram_client_first_defective("client:10.0.0.1:5432", user, "n,,n=mockuser,r=mocknonce");
|
|
|
|
boolean secretFound = capturedLogs.stream().anyMatch(line -> line.contains("stored secret"));
|
|
assert !secretFound : "mock_auth=true: secret branch should be skipped";
|
|
System.out.println("PASS mock-auth: secret branch skipped for mock_auth=true");
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== pgbouncer-0001: CWE-312 SCRAM verifier logged at debug ===");
|
|
testDefectiveLogsSecret();
|
|
testFixedDoesNotLogSecret();
|
|
testFixedStillLogsDiagnostic();
|
|
testPlaintextPasswordNotLogged();
|
|
testMockAuthNotLogged();
|
|
System.out.println("ALL PASS");
|
|
}
|
|
}
|