Advisory Details
Title: Sensitive User Data and Storage Credentials Exposure in Baremetal PING PXE Resource Logs
Description:
A sensitive logging vulnerability exists in the Apache CloudStack PING PXE baremetal plugin. When orchestrating resources or preparing booting files on baremetal hosts, the CloudStack Management Server constructs commands containing raw VM user-data (userdata), public keys, and CIFS storage password configurations, executing them over SSH.
However, the underlying execution logging helper SSHCmdHelper.sshExecuteCmdOneShot only attempts to sanitize command arguments using a crude split logic targeted at KeyStoreUtils.KS_FILENAME ("cloud.jks"). Since baremetal orchestration commands never contain "cloud.jks", the split fails to mask any characters, logging the entire command—including raw plaintext credentials and user configuration details—directly into standard system debug logs (e.g., vmops.log or management-server.log).
Summary
An information exposure vulnerability (CWE-532) in the Apache CloudStack Baremetal PING PXE plugin allows any local actor or system administrator with access to debug logs to read plaintext sensitive VM initialization secrets (user-data), SSH keys, and CIFS network storage passwords.
Details
The vulnerability manifest itself in two critical variants in BaremetalPingPxeResource.java:
Variant 1: VM Metadata Exposure via VmDataCommand
In BaremetalPingPxeResource.java inside execute(VmDataCommand cmd), VM custom metadata is assembled into a single string argument and formatted into a python script call:
String script = String.format("python /usr/bin/baremetal_user_data.py '%s'", arg);
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) {
return new Answer(cmd, false, "Failed to add user data, command:" + script);
}
The variable arg contains the plaintext custom user-data (which often holds database passwords, configuration keys, or startup secrets) and public SSH keys.
Variant 2: Plaintext CIFS Storage Credential Leakage
Similarly, in execute(PreparePxeServerCommand) and execute(PrepareCreateTemplateCommand), the resource's CIFS mount storage credential (_cifsPassword) is concatenated directly into the shell script format:
String script =
String.format("python /usr/bin/prepare_tftp_bootfile.py restore %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", _tftpDir, cmd.getMac(),
_storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay());
Both methods route their command execution through SSHCmdHelper.sshExecuteCmd(sshConnection, script). This delegates to SSHCmdHelper.sshExecuteCmdOneShot, which implements the logging logic:
LOGGER.debug("Executing cmd: " + cmd.split(KeyStoreUtils.KS_FILENAME)[0]);
Since KeyStoreUtils.KS_FILENAME is defined as "cloud.jks" in KeyStoreUtils.java, commands without "cloud.jks" do not match, evaluating cmd.split("cloud.jks")[0] to the original unsanitized string. Consequently, all custom user secrets, SSH public keys, and CIFS mount passwords flow directly into debug logs in plaintext.
PoC
Prerequisites
- Debug logging level enabled on Apache CloudStack Management Server or Agent.
- PING PXE baremetal plugin configured and active.
Reproduction Steps
- Download the isolated container configuration script from: docker-compose.yml
- Download the defect verification script from: verification_test.py
- Download the scientific control test script from: control-masked_output.py
- Execute the defect verification script:
python3 verification_test.py
- Confirm that raw credentials (e.g.,
PlaintextSuperSecretPassword123, StorageCIFSSuperSecurePassword789!) are fully leaked under the logged commands section.
Log of Evidence
[*] Running Issue-cloudstack-12030-BaremetalPingPxe Sensitive Data Exposure Verification...
[*] Defect Verification - Input Tracing:
- Sensitive VM User-Data: PlaintextSuperSecretPassword123
- Sensitive SSH Public Key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuVariantPublicSSHKey
- Sensitive CIFS Storage Password: StorageCIFSSuperSecurePassword789!
--- EXPERIMENT RESULTS (DEBUG LOGS) ---
[*] Logged VmDataCommand: python /usr/bin/baremetal_user_data.py '10.0.0.10,metadata,userdata,PlaintextSuperSecretPassword123;10.0.0.10,metadata,sshkey,ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuVariantPublicSSHKey'
[*] Logged CIFS Command: python /usr/bin/prepare_tftp_bootfile.py restore /tftpboot 00:11:22:33:44:55 192.168.1.10 share dir template cifs_user StorageCIFSSuperSecurePassword789! 10.0.0.10 255.255.255.0 10.0.0.1
---------------------------------------
[+] SUCCESS: Plaintext sensitive credentials leaked successfully in standard debug logs!
[+] Status: DEFECT-CONFIRMED
Impact
This is a high-severity information exposure vulnerability (CWE-532).
If successfully triggered:
- Attackers with system log access can hijack virtual/baremetal environments by reading raw tenant configurations (user-data, containing startup credentials, database secrets, or API keys).
- Tenant SSH keys can be exposed, bypassing VM shell access constraints.
- Core infrastructure CIFS storage network credentials will be exposed, potentially allowing unauthorized actors to compromise and modify base operating system template files.
Affected products
- Ecosystem: maven
- Package name: org.apache.cloudstack:cloudstack
- Affected versions: <= 4.22.1.0
- Patched versions:
Severity
- Severity: High
- Vector string: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Weaknesses
- CWE: CWE-532: Insertion of Sensitive Information into Log File
Occurrences
| Permalink |
Description |
|
String script = |
|
String.format("python /usr/bin/prepare_tftp_bootfile.py restore %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", _tftpDir, cmd.getMac(), |
|
_storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); |
|
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { |
|
return new PreparePxeServerAnswer(cmd, "prepare PING at " + _ip + " failed, command:" + script); |
|
} |
|
CIFS password exposure during PreparePxeServerCommand execution in BaremetalPingPxeResource.java. |
|
String script = |
|
String.format("python /usr/bin/prepare_tftp_bootfile.py backup %1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s", _tftpDir, cmd.getMac(), |
|
_storageServer, _share, _dir, cmd.getTemplate(), _cifsUserName, _cifsPassword, cmd.getIp(), cmd.getNetMask(), cmd.getGateWay()); |
|
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { |
|
return new Answer(cmd, false, "prepare for creating template failed, command:" + script); |
|
} |
|
CIFS password exposure during PrepareCreateTemplateCommand execution in BaremetalPingPxeResource.java. |
|
private Answer execute(VmDataCommand cmd) { |
|
com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22); |
|
try { |
|
List<String[]> vmData = cmd.getVmData(); |
|
StringBuilder sb = new StringBuilder(); |
|
for (String[] data : vmData) { |
|
String folder = data[0]; |
|
String file = data[1]; |
|
String contents = (data[2] == null) ? "none" : data[2]; |
|
sb.append(cmd.getVmIpAddress()); |
|
sb.append(","); |
|
sb.append(folder); |
|
sb.append(","); |
|
sb.append(file); |
|
sb.append(","); |
|
sb.append(contents); |
|
sb.append(";"); |
|
} |
|
String arg = StringUtils.stripEnd(sb.toString(), ";"); |
|
|
|
sshConnection.connect(null, 60000, 60000); |
|
if (!sshConnection.authenticateWithPassword(_username, _password)) { |
|
logger.debug("SSH Failed to authenticate"); |
|
throw new ConfigurationException(String.format("Cannot connect to PING PXE server(IP=%1$s, username=%2$s", _ip, _username)); |
|
} |
|
|
|
String script = String.format("python /usr/bin/baremetal_user_data.py '%s'", arg); |
|
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, script)) { |
|
return new Answer(cmd, false, "Failed to add user data, command:" + script); |
|
} |
|
VM custom user-data and SSH public key exposure during VmDataCommand execution in BaremetalPingPxeResource.java. |
|
LOGGER.debug("Executing cmd: " + cmd.split(KeyStoreUtils.KS_FILENAME)[0]); |
|
Root cause split-based logging logic in SSHCmdHelper.sshExecuteCmdOneShot failing to sanitize commands lacking "cloud.jks". |
|
LOGGER.debug("SSH command: " + cmd.split(KeyStoreUtils.KS_FILENAME)[0] + "\nSSH command output:" + result.getStdOut().split("-----BEGIN")[0] + "\n" + result.getStdErr()); |
|
Command output logging in SSHCmdHelper.sshExecuteCmdOneShot failing to sanitize logs for commands lacking "cloud.jks". |
Advisory Details
Title: Sensitive User Data and Storage Credentials Exposure in Baremetal PING PXE Resource Logs
Description:
A sensitive logging vulnerability exists in the Apache CloudStack PING PXE baremetal plugin. When orchestrating resources or preparing booting files on baremetal hosts, the CloudStack Management Server constructs commands containing raw VM user-data (
userdata), public keys, and CIFS storage password configurations, executing them over SSH.However, the underlying execution logging helper
SSHCmdHelper.sshExecuteCmdOneShotonly attempts to sanitize command arguments using a crude split logic targeted atKeyStoreUtils.KS_FILENAME("cloud.jks"). Since baremetal orchestration commands never contain"cloud.jks", the split fails to mask any characters, logging the entire command—including raw plaintext credentials and user configuration details—directly into standard system debug logs (e.g.,vmops.logormanagement-server.log).Summary
An information exposure vulnerability (CWE-532) in the Apache CloudStack Baremetal PING PXE plugin allows any local actor or system administrator with access to debug logs to read plaintext sensitive VM initialization secrets (user-data), SSH keys, and CIFS network storage passwords.
Details
The vulnerability manifest itself in two critical variants in
BaremetalPingPxeResource.java:Variant 1: VM Metadata Exposure via VmDataCommand
In
BaremetalPingPxeResource.javainsideexecute(VmDataCommand cmd), VM custom metadata is assembled into a single string argument and formatted into a python script call:The variable
argcontains the plaintext custom user-data (which often holds database passwords, configuration keys, or startup secrets) and public SSH keys.Variant 2: Plaintext CIFS Storage Credential Leakage
Similarly, in
execute(PreparePxeServerCommand)andexecute(PrepareCreateTemplateCommand), the resource's CIFS mount storage credential (_cifsPassword) is concatenated directly into the shell script format:Both methods route their command execution through
SSHCmdHelper.sshExecuteCmd(sshConnection, script). This delegates toSSHCmdHelper.sshExecuteCmdOneShot, which implements the logging logic:Since
KeyStoreUtils.KS_FILENAMEis defined as"cloud.jks"inKeyStoreUtils.java, commands without"cloud.jks"do not match, evaluatingcmd.split("cloud.jks")[0]to the original unsanitized string. Consequently, all custom user secrets, SSH public keys, and CIFS mount passwords flow directly into debug logs in plaintext.PoC
Prerequisites
Reproduction Steps
PlaintextSuperSecretPassword123,StorageCIFSSuperSecurePassword789!) are fully leaked under the logged commands section.Log of Evidence
Impact
This is a high-severity information exposure vulnerability (CWE-532).
If successfully triggered:
Affected products
Severity
Weaknesses
Occurrences
cloudstack/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java
Lines 157 to 162 in 348ce95
PreparePxeServerCommandexecution inBaremetalPingPxeResource.java.cloudstack/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java
Lines 185 to 190 in 348ce95
PrepareCreateTemplateCommandexecution inBaremetalPingPxeResource.java.cloudstack/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPingPxeResource.java
Lines 217 to 246 in 348ce95
VmDataCommandexecution inBaremetalPingPxeResource.java.cloudstack/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java
Line 167 in 348ce95
SSHCmdHelper.sshExecuteCmdOneShotfailing to sanitize commands lacking"cloud.jks".cloudstack/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java
Line 230 in 348ce95
SSHCmdHelper.sshExecuteCmdOneShotfailing to sanitize logs for commands lacking"cloud.jks".