-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Description:
CLOUDSTACK VERSION
4.22.0.0
Summary
The Script.java class provides a command execution framework with password masking capabilities, but the masking logic is critically flawed. It only obscures parameters following specific flags (-y/-z) or certain URI patterns, leaving credentials passed through direct parameter addition (e.g., script.add(username, newPassword)) completely exposed. This results in plaintext password logging at both DEBUG and WARN levels throughout the command execution lifecycle.
Severity
Critical - Systematic password exposure across multiple log levels (DEBUG and WARN) affects all components using the Script framework. WARN-level logging is typically enabled in production, making this vulnerability active in live deployments.
Vulnerability Details
Root Cause Analysis
1. Inadequate Password Masking Logic
The cloud.utils.script.Script#buildCommandLine() method only masks parameters in very specific scenarios:
protected String buildCommandLine(String[] command) {
StringBuilder builder = new StringBuilder();
boolean obscureParam = false;
for (int i = 0; i < command.length; i++) {
String cmd = command[i];
// Only handles specific URI patterns
if (sanitizeViCmdParameter(cmd, builder) ||
sanitizeRbdFileFormatCmdParameter(cmd, builder)) {
continue;
}
if (obscureParam) {
builder.append("******").append(" "); // ✅ Masks ONLY if flag detected
obscureParam = false;
} else {
builder.append(cmd).append(" "); // ❌ All other params logged plaintext
}
// ❌ ONLY triggers masking for -y/-z flags
if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true;
_passwordCommand = true;
}
}
return builder.toString();
}Critical Flaw: The masking logic assumes passwords are always preceded by -y or -z flags. Any other parameter passing pattern bypasses masking entirely.
2. Direct Parameter Addition Bypasses Masking
The CitrixUpdateHostPasswordCommandWrapper uses direct parameter addition:
// CitrixUpdateHostPasswordCommandWrapper.java (lines 34-45)
final Script script = new Script(scriptPath + scriptName, _timeout, logger);
script.add(username, newPassword); // ❌ No flag, no masking
final String result = script.execute();The add() method simply appends parameters to the command array:
public void add(String... params) {
for (String param : params) {
_command.add(param); // ❌ Direct addition, no masking metadata
}
}Result: When buildCommandLine() processes these parameters, they don't follow -y/-z flags, so they're logged in plaintext.
3. Multiple Logging Points Expose Credentials in com.cloud.utils.script.Script#execute
** Point 1: DEBUG Level Logging **
public String execute(OutputInterpreter interpreter) {
String[] command = _command.toArray(new String[_command.size()]);
String commandLine = buildCommandLine(command);
if (_logger.isDebugEnabled() && !avoidLoggingCommand) {
// ❌ Logs full command with plaintext password
_logger.debug(String.format("Executing command [%s].",
commandLine.split(KeyStoreUtils.KS_FILENAME)[0]));
}
// ...
}Exposure: Every command execution logs the full command line at DEBUG level unless avoidLoggingCommand is explicitly set.
** Point 2: WARN Level Logging on Timeout **
TimedOutLogger log = new TimedOutLogger(_process);
Task timedoutTask = new Task(log, ir);
_logger.trace(String.format("Running timed out task of process [%s] for command [%s].",
processPid, commandLine));
if (!_passwordCommand) {
// ❌ Logs full command with password at WARN level
_logger.warn(String.format("Process [%s] for command [%s] timed out. Output is [%s].",
processPid, commandLine, timedoutTask.getResult()));
} else {
_logger.warn(String.format("Process [%s] for command [%s] timed out.",
processPid, commandLine));
}Critical Issue: The _passwordCommand flag is only set to true when -y/-z flags are detected. Since CitrixUpdateHostPasswordCommandWrapper doesn't use these flags:
_passwordCommandremainsfalse- Timeout warnings log the full command with plaintext password at WARN level
- WARN logs are typically enabled in production
** Point 3: WARN Level Logging on Execution Failure **
_logger.debug(String.format("Exit value of process [%s] for command [%s] is [%s].",
processPid, commandLine, _process.exitValue()));
BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);
String error;
if (interpreter != null) {
error = interpreter.processError(reader);
} else {
error = String.valueOf(_process.exitValue());
}
// ❌ Logs full command with password at WARN level on failure
_logger.warn(String.format("Process [%s] for command [%s] encountered the error: [%s].",
processPid, commandLine, error));Exposure: Any script execution failure logs the full command at WARN level, including plaintext passwords.
** Point 4: WARN Level Logging on Exception **
} catch (SecurityException ex) {
// ❌ Logs command with password at WARN level
_logger.warn(String.format("Exception [%s] occurred. This may be due to an attempt of " +
"executing command [%s] as non root.", ex.getMessage(), commandLine), ex);
return stackTraceAsString(ex);
} catch (Exception ex) {
// ❌ Logs command with password at WARN level
_logger.warn(String.format("Exception [%s] occurred when attempting to run command [%s].",
ex.getMessage(), commandLine), ex);
return stackTraceAsString(ex);
}Exposure: Any exception during script execution logs the full command at WARN level.
4. Missing avoidLoggingCommand Flag
The Script class provides an avoidLoggingCommand flag to suppress logging:
// Script.java
private boolean avoidLoggingCommand = false;
public void setAvoidLoggingCommand(boolean avoidLoggingCommand) {
this.avoidLoggingCommand = avoidLoggingCommand;
}Problem: CitrixUpdateHostPasswordCommandWrapper never calls setAvoidLoggingCommand(true):
// CitrixUpdateHostPasswordCommandWrapper.java
final Script script = new Script(scriptPath + scriptName, _timeout, logger);
script.add(username, newPassword);
// ❌ Missing: script.setAvoidLoggingCommand(true);
final String result = script.execute();Result: Even the DEBUG-level protection is not applied.
Complete Vulnerability Chain
1. CitrixUpdateHostPasswordCommandWrapper creates Script instance
↓
2. Calls script.add(username, newPassword) without -y/-z flags
↓
3. Does NOT call script.setAvoidLoggingCommand(true)
↓
4. Script.execute() calls buildCommandLine()
↓
5. buildCommandLine() doesn't detect -y/-z flags
↓
6. Password NOT masked in command string
↓
7. _passwordCommand remains false
↓
8. Password logged at DEBUG level
↓
9. On timeout/failure/exception: Password logged at WARN level
Exposed Sensitive Data
| Data Type | Exposure Point | Log Level | Production Impact |
|---|---|---|---|
| New host password | DEBUG logging | DEBUG | If DEBUG enabled |
| New host password | Timeout logging | WARN | Always in production |
| New host password | Failure logging | WARN | Always in production |
| New host password | Exception logging | WARN | Always in production |