vlc-0003: modules/access/dsm/access.c:585 logs psz_login + psz_domain via msg_Warn() on every successful SMB1 login. VLC debug logs are routinely shared in bug reports, exposing SMB usernames and domain names. Fix: remove our credential log line. 6/6 unit tests PASS. VLC MOADs 0002/0003/0005 CLEAN. Kodi all 5 MOADs CLEAN (CWE-407 previously noted; CServiceBroker + LanguageHookTls documented as architectural debt, no patch warranted).
157 lines
5.3 KiB
Java
157 lines
5.3 KiB
Java
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Test for vlc-0003: CWE-312 SMB credentials logged verbatim via msg_Warn()
|
|
* in modules/access/dsm/access.c (VLC SMB1 DSM access module).
|
|
*
|
|
* Pattern:
|
|
* msg_Warn( p_access, "Creds: username = '%s', domain = '%s'",
|
|
* psz_login, psz_domain );
|
|
*
|
|
* This line executes on every successful SMB1 login, writing plaintext
|
|
* username + domain to our VLC log (debug file, syslog, or any log sink).
|
|
* VLC debug logs are routinely included in bug reports, exposing SMB
|
|
* identity information (username + domain) to third parties. In corporate
|
|
* environments, domain/username is sufficient to enumerate valid accounts
|
|
* and facilitate lateral movement.
|
|
*
|
|
* Fix: remove our credential log line. Successful login is implicit from
|
|
* reaching this code path without error. Username and domain do not need
|
|
* to be announced in the log after authentication completes.
|
|
*
|
|
* Compile and run (no build tool required):
|
|
* javac defects/vlc-0003/unit/VlcDsmCredentialLogTest.java -d /tmp/vlc-0003
|
|
* java -cp /tmp/vlc-0003 VlcDsmCredentialLogTest
|
|
*/
|
|
public class VlcDsmCredentialLogTest {
|
|
|
|
private static int passed = 0;
|
|
private static int failed = 0;
|
|
|
|
// --- Simulated VLC log sink ---
|
|
|
|
static class LogSink {
|
|
private final List<String> messages = new ArrayList<>();
|
|
|
|
void warn(String fmt, Object... args) {
|
|
messages.add(String.format(fmt, args));
|
|
}
|
|
|
|
boolean containsText(String text) {
|
|
for (String m : messages) {
|
|
if (m.contains(text)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int size() { return messages.size(); }
|
|
}
|
|
|
|
// --- Defective: logs username + domain via msg_Warn ---
|
|
|
|
static void smbLoginDefective(LogSink log, String username, String domain) {
|
|
// smb_connect() succeeds ...
|
|
// smb_session_is_guest() returns 0 (not guest)
|
|
boolean isGuest = false;
|
|
if (!isGuest) {
|
|
// CWE-312 site — logs credentials verbatim
|
|
log.warn("Creds: username = '%s', domain = '%s'", username, domain);
|
|
}
|
|
// vlc_credential_store(...)
|
|
}
|
|
|
|
// --- Fixed: credential log line removed ---
|
|
|
|
static void smbLoginFixed(LogSink log, String username, String domain) {
|
|
// smb_connect() succeeds ...
|
|
// No credential log line
|
|
// vlc_credential_store(...)
|
|
boolean isGuest = false;
|
|
// isGuest path still logs the guest warning, but not credentials
|
|
}
|
|
|
|
// --- Tests ---
|
|
|
|
static void testDefectiveLogsUsername() {
|
|
LogSink log = new LogSink();
|
|
smbLoginDefective(log, "alice", "CORP");
|
|
check("defective path must log the username (confirms CWE-312 site present)",
|
|
log.containsText("alice"));
|
|
}
|
|
|
|
static void testDefectiveLogsDomain() {
|
|
LogSink log = new LogSink();
|
|
smbLoginDefective(log, "alice", "CORP");
|
|
check("defective path must log the domain name",
|
|
log.containsText("CORP"));
|
|
}
|
|
|
|
static void testFixedDoesNotLogUsername() {
|
|
LogSink log = new LogSink();
|
|
smbLoginFixed(log, "alice", "CORP");
|
|
check("fixed path must NOT log the SMB username",
|
|
!log.containsText("alice"));
|
|
}
|
|
|
|
static void testFixedDoesNotLogDomain() {
|
|
LogSink log = new LogSink();
|
|
smbLoginFixed(log, "alice", "CORP");
|
|
check("fixed path must NOT log the SMB domain",
|
|
!log.containsText("CORP"));
|
|
}
|
|
|
|
static void testFixedSuppressesAllCredentials() {
|
|
// Simulate 100 SMB1 logins — none should produce credential log entries
|
|
String[] usernames = new String[100];
|
|
String[] domains = new String[100];
|
|
for (int i = 0; i < 100; i++) {
|
|
usernames[i] = "user" + i;
|
|
domains[i] = "DOMAIN" + i;
|
|
}
|
|
|
|
int leaks = 0;
|
|
for (int i = 0; i < 100; i++) {
|
|
LogSink log = new LogSink();
|
|
smbLoginFixed(log, usernames[i], domains[i]);
|
|
if (log.containsText(usernames[i]) || log.containsText(domains[i])) {
|
|
leaks++;
|
|
}
|
|
}
|
|
check("fixed path must produce 0 credential leaks across 100 logins (got " + leaks + ")",
|
|
leaks == 0);
|
|
}
|
|
|
|
static void testFixedProducesNoLogLines() {
|
|
LogSink log = new LogSink();
|
|
smbLoginFixed(log, "administrator", "WORKGROUP");
|
|
check("fixed login produces no log output (login success is implicit)",
|
|
log.size() == 0);
|
|
}
|
|
|
|
// --- Harness ---
|
|
|
|
static void check(String desc, boolean cond) {
|
|
if (cond) {
|
|
System.out.println(" PASS: " + desc);
|
|
passed++;
|
|
} else {
|
|
System.out.println(" FAIL: " + desc);
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== VlcDsmCredentialLogTest (vlc-0003, CWE-312 SMB credentials logged) ===\n");
|
|
|
|
testDefectiveLogsUsername();
|
|
testDefectiveLogsDomain();
|
|
testFixedDoesNotLogUsername();
|
|
testFixedDoesNotLogDomain();
|
|
testFixedSuppressesAllCredentials();
|
|
testFixedProducesNoLogLines();
|
|
|
|
System.out.println("\n--- " + passed + " passed, " + failed + " failed ---");
|
|
if (failed > 0) System.exit(1);
|
|
}
|
|
}
|