A flaw was found in cockpit-machines. This vulnerability allows a local attacker with the ability to inspect running processes to expose sensitive guest virtual machine (VM) credentials, such as rootPassword and userPassword. This occurs when the installmachine.py script passes these credentials as a JSON command-line argument during VM creation or installation. The exposure is limited to the period when the installation workflow is active and depends on host process-visibility policies.
AIONLYREPORT package: cockpit-machines-348-1.el10 ------ Summary: Sensitive Data Exposure in Process List via Command-Line Arguments: password values supplied during VM create or install operations can be exposed to other local users through process command-line arguments. Requirements to exploit: An attacker needs local access to the host, the ability to inspect other processes' command-line arguments while the VM workflow is running, and a create or install operation that includes rootPassword and/or userPassword. Exposure is reduced on systems that restrict /proc visibility, such as hidepid=2. Component affected: cockpit-machines source in src/libvirtApi/domain.ts (domainCreate(), domainInstall()) and src/scripts/installmachine.py Version affected: cockpit-machines-348-1.el10 Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - 4.7 (MEDIUM) AV:L - Exploitation requires local access to the host where Cockpit Machines is running. AC:L - The issue is a direct consequence of passing secrets on the command line; observation is straightforward when process arguments are visible. PR:L - A local account is typically required to inspect process listings or /proc/<pid>/cmdline. UI:N - No additional user interaction is required once the victim initiates the VM workflow. S:U - The impact remains within the same security scope. C:H - Successful exploitation can disclose VM credentials in full. I:N - This issue does not directly modify data or configuration. A:N - This issue does not directly affect service availability. Impact: Moderate. This issue can expose sensitive VM credentials, which is a meaningful confidentiality impact when reachable. However, it is not a remote flaw, it depends on local process-argument visibility and a narrow execution window, and hardened /proc settings can materially reduce exploitability. Under Red Hat's severity guidance, that places the issue below Important impact but above Low impact because the disclosed data can still lead to compromise of protected resources under realistic local-host conditions. Embargo: no Reason: The issue is local and configuration-dependent, practical mitigations already exist at deployment level, and the remediation is straightforward. This does not appear to require embargo handling typically reserved for higher-severity flaws. Acknowledgement: Aisle Research Vulnerability Details: Cockpit Machines passes password-bearing values through process arguments during VM creation and installation flows. In the cloud-image create path, plaintext passwords are passed directly to openssl on the command line. Separately, create and install flows serialize password-bearing JSON and pass it as sys.argv[1] to installmachine.py. ts if (args.userPassword) promises.push(spawn("session", ['openssl', 'passwd', '-5', args.userPassword])); if (args.rootPassword) promises.push(spawn("session", ['openssl', 'passwd', '-5', args.rootPassword])); ... await python.spawn( installVmScript, [JSON.stringify(args)], { err: "message", environ: ['LCALL=C.UTF-8'], ...(connectionName === "system" ? { superuser: "try" } : { }) }); ... return python.spawn( installVmScript, [args], { err: "message", environ: ['LCALL=C.UTF-8'], ...(vm.connectionName === "system" ? { superuser: "try" } : { }) }) installmachine.py then consumes the payload from the command line: py logging.debug(sys.argv[1]) args = json.loads(sys.argv[1], strict=False) On systems where other local users can inspect process arguments, this can disclose credentials entered for VM provisioning. The cloud-image create path clearly exposes plaintext passwords through openssl passwd -5 <password>. The JSON-based path exposes whatever password values are present in the serialized arguments at invocation time. Steps to reproduce: 1. Use a host where other local users can inspect process arguments, for example a default /proc configuration without hidepid=2. 2. Start a VM create or install operation in Cockpit Machines and provide rootPassword and/or userPassword. 3. While the operation is running, from a second unprivileged local account, inspect candidate processes: ps -ef | grep -E "openssl passwd|-c $@|installmachine.py|python3" 4. Read the command line for the relevant process: tr '\0' ' ' < /proc/<pid>/cmdline 5. Observe one of the following: openssl passwd -5 <plaintext-password> in the cloud-image create path JSON arguments containing rootPassword and/or userPassword passed to installmachine.py 6. If /proc is mounted with hidepid=2 or similar restrictions, non-root observation may be blocked. That mitigates exposure but does not change the underlying secret-in-argv behavior. Mitigation: Until code is changed, avoid supplying sensitive passwords through these create/install flows on shared hosts where local users can inspect process arguments. Restrict /proc visibility, for example with hidepid=2, and prefer provisioning methods that do not require passing passwords on the command line. Proposed Fix: Stop passing secrets in argv. Feed password material to openssl on standard input and pass the JSON payload to installmachine.py via stdin instead of sys.argv[1]. diff diff --git a/src/libvirtApi/domain.ts b/src/libvirtApi/domain.ts @@ if (args.userPassword)
promises.push(spawn("session", ['openssl', 'passwd', '-5', args.userPassword]));
if (args.rootPassword)
promises.push(spawn("session", ['openssl', 'passwd', '-5', args.rootPassword])); + if (args.userPassword) + promises.push(cockpit.spawn(['openssl', 'passwd', '-5', '-stdin'], { err: "message", input: args.userPassword + "\n" })); + if (args.rootPassword) + promises.push(cockpit.spawn(['openssl', 'passwd', '-5', '-stdin'], { err: "message", input: args.rootPassword + "\n" })); @@
await python.spawn(
installVmScript,
[JSON.stringify(args)],
{ + await python.spawn( + installVmScript, + [], + { err: "message", + input: JSON.stringify(args), environ: ['LCALL=C.UTF-8'], ...(connectionName === "system" ? { superuser: "try" } : { }) }); @@
return python.spawn(
installVmScript,
[args],
{ + return python.spawn( + installVmScript, + [], + { err: "message", + input: args, environ: ['LCALL=C.UTF-8'], ...(vm.connectionName === "system" ? { superuser: "try" } : { }) })
diff --git a/src/scripts/installmachine.py b/src/scripts/installmachine.py @@ -logging.debug(sys.argv[1]) - -args = json.loads(sys.argv[1], strict=False) +payload = sys.stdin.read() if len(sys.argv) == 1 else sys.argv[1] +args = json.loads(payload, strict=False) ------ This report was generated using AI technology. Always review AI-generated content prior to use
AIONLYREPORT package: cockpit-machines-348-1.el10 ------ Summary: RHSM Offline Token Exposure via Process Arguments in getAccessToken() (CWE-214): the package passes the RHSM offline token to a helper via argv, allowing local disclosure through process metadata while token validation is running. Requirements to exploit: An attacker needs local access to the host running Cockpit Machines, the ability to inspect another user's process metadata while the RHSM token-validation flow is active, and a deployment without /proc visibility restrictions that would otherwise block command-line inspection. Component affected: cockpit-machines; src/libvirtApi/rhel-images.ts getAccessToken(); src/scripts/rhsm/getaccesstoken.py Version affected: cockpit-machines-348-1.el10 Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N - 4.7 (MEDIUM) AV:L - Exploitation requires local access to the host where Cockpit Machines is running. AC:L - On systems without restrictive /proc settings, observing helper command lines is straightforward once the process starts. PR:L - A low-privileged local account is sufficient to inspect process metadata in affected configurations. UI:R - A user must trigger the RHSM offline-token validation flow so the helper process is spawned with the secret in argv. S:U - The impact stays within the same security scope. C:H - The exposed value is a sensitive RHSM offline token that can be used to request access tokens. I:N - This issue does not directly alter data or system state. A:N - This issue does not directly reduce service availability. Impact: Moderate. This issue can expose a high-value credential and therefore compromise confidentiality, but exploitation is local, timing-sensitive, and dependent on process-visibility configuration. Under Red Hat's severity guidance, that fits a confidentiality compromise that is real but less easily exploited than an Important issue. Embargo: no Reason: This is a local information disclosure issue with configuration-dependent exploitability and a straightforward remediation path. It does not present the kind of remotely exploitable or rapidly weaponizable condition that typically warrants embargo. Acknowledgement: Aisle Research Vulnerability Details: The RHSM offline token is serialized into JSON and passed to a Python helper as a command-line argument. The helper then reads the secret from sys.argv[1] and uses it as the refreshtoken parameter when requesting an access token: ts export function getAccessToken(offlineToken: string): cockpit.Spawn<string> { logDebug(Get access token); const arg = JSON.stringify({ offlineToken }); return python.spawn(getAccessTokenScript, [arg], { err: "message", environ: ['LCALL=C.UTF-8'] }); } py args = json.loads(sys.argv[1], strict=False) data = { "granttype": "refreshtoken", "clientid": "rhsm-api", "refreshtoken": args["offlineToken"] } Because the secret is present in argv, it can be exposed through process metadata such as ps, pgrep -af, or /proc/<pid>/cmdline while the helper is running. Reachability from the UI is direct through the Create VM RHEL download/token-validation flow. This is a local disclosure issue rather than a remote compromise; its practical exploitability depends on whether another local user can inspect that process metadata on the host. Steps to reproduce: 1. Start Cockpit Machines and open the Create VM flow. 2. Select Download an OS and choose a RHEL entry that requires an RHSM offline token. 3. Paste a valid or invalid offline token so the validation flow calls getAccessToken(). 4. From a second local shell on the same host, monitor the helper command line: bash while true; do pgrep -af getaccesstoken.py; sleep 0.1; done 5. Observe that the helper process command line contains JSON with the offlineToken value. If the host enforces restrictive /proc visibility settings such as hidepid, cross-user observation may be blocked. That limits exploitability, but it does not change the underlying secret-on-argv behavior. Mitigation: Until a fixed build is available, avoid validating RHSM offline tokens through this flow on shared systems where untrusted local users can inspect process metadata. Restrict /proc visibility so unprivileged users cannot view other users' command lines. Proposed Fix: Do not pass the offline token in argv. Spawn the helper without secret-bearing arguments and send the JSON payload over stdin instead. diff diff --git a/src/libvirtApi/rhel-images.ts b/src/libvirtApi/rhel-images.ts @@ export function getAccessToken(offlineToken: string): cockpit.Spawn<string> { logDebug(Get access token); const arg = JSON.stringify({ offlineToken });
return python.spawn(getAccessTokenScript, [arg], { err: "message", environ: ['LCALL=C.UTF-8'] }); + const proc = python.spawn(getAccessTokenScript, [], { err: "message", environ: ['LCALL=C.UTF-8'] }); + proc.input(JSON.stringify({ offlineToken })); + return proc; } diff --git a/src/scripts/rhsm/getaccesstoken.py b/src/scripts/rhsm/getaccesstoken.py @@ -args = json.loads(sys.argv[1], strict=False) +args = json.loads(sys.stdin.read(), strict=False)
------ This report was generated using AI technology. Always review AI-generated content prior to use
AIONLYREPORT package: cockpit-machines-348-1.el10 ------ Summary: Sensitive Data Exposure in Process List via JSON Argument in installmachine.py: VM installation credentials are passed in a JSON command-line argument, which can expose rootPassword and userPassword through local process inspection on affected host configurations. Requirements to exploit: Local access to the host running cockpit-machines, the ability to inspect another running process's command line, and a VM create/install operation that supplies rootPassword and/or userPassword while installmachine.py is still running. Exploitability depends on host process-visibility settings and timing. Component affected: cockpit-machines create/install flow in src/libvirtApi/domain.ts (domainCreate, domainInstall) and src/scripts/installmachine.py Version affected: cockpit-machines-348-1.el10 Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N - 4.7 (MEDIUM) AV:L - The issue is exploitable from the local host by inspecting process arguments. AC:L - Once the affected process is running, reading ps output or /proc/<pid>/cmdline is straightforward where host policy permits it. PR:L - The attacker needs local access sufficient to observe the process on the host. UI:R - Credential exposure occurs only when a user triggers the affected VM create/install flow with password fields populated. S:U - The issue does not cross a security boundary within the product. C:H - The exposed data can include guest login credentials, including rootPassword and userPassword, or other directly reusable credential material in some paths. I:N - This issue does not directly modify data. A:N - This issue does not directly affect service availability. Impact: Moderate. This is a confidentiality issue that can disclose guest credentials, but it requires local host access, depends on process-visibility policy, and is only exposed while the affected create/install workflow is active. That is consistent with a flaw that can lead to compromise under specific circumstances, but is not an easy remote or unauthenticated system-compromise condition. Embargo: no Reason: The issue appears to require local access and runtime timing, with mitigation available through a relatively contained code change and host process-visibility hardening. This is better handled through normal coordinated remediation than embargoed treatment. Acknowledgement: Aisle Research Vulnerability Details: The VM create/install path passes a full JSON payload to installmachine.py through argv, and that payload includes credential fields. The Python side then deserializes the JSON directly from the first positional argument: python args = json.loads(sys.argv[1], strict=False) The affected create and install flows build JSON that includes rootPassword and userPassword, then invoke installmachine.py with that JSON as a command-line argument. This makes the credentials observable through process metadata on hosts where other local users can inspect command lines. The exposure window can be meaningful because the install path may remain active for an extended period during guest installation. Steps to reproduce: 1. Start a VM creation or installation through the affected workflow with rootPassword and/or userPassword set. 2. While installmachine.py is still running, identify the process on the same host: ps -ef | grep installmachine.py 3. Read the command line for the matching process: tr '\0' ' ' < /proc/<pid>/cmdline 4. Observe that the JSON argument contains rootPassword and/or userPassword in cleartext, or other directly reusable credential material in some cloud-image paths. If host policy restricts cross-process inspection, the disclosure may not be observable from a separate local account, but the underlying behavior still places secrets in argv. Mitigation: Avoid passing secrets in process arguments. Until code is corrected, reduce exposure by restricting process inspection on the host where possible and by avoiding use of password-bearing install flows when another provisioning method is available. Hardening /proc visibility can reduce exploitability, but it does not remove the underlying secret-in-argv issue. Proposed Fix: Read the JSON payload from standard input, keep an argv fallback only for compatibility, and update both call sites to write the payload via Spawn.input() instead of passing it in the command line. diff diff --git a/src/scripts/installmachine.py b/src/scripts/installmachine.py index 0000000..1111111 100755 — a/src/scripts/installmachine.py +++ b/src/scripts/installmachine.py @@ -313,9 +313,16 @@ def injectmetadata(xml): logging.basicConfig(level=logging.ERROR, format='%(message)s') -logging.debug(sys.argv[1]) - -args = json.loads(sys.argv[1], strict=False) +payload = None +if len(sys.argv) > 1 and sys.argv[1]: + # backward compatibility: prefer stdin, accept argv for older callers + payload = sys.argv[1] +else: + payload = sys.stdin.read() + +if not payload: + raise ValueError("missing installmachine payload") + +args = json.loads(payload, strict=False) logging.debug(args) diff --git a/src/libvirtApi/domain.ts b/src/libvirtApi/domain.ts index 2222222..3333333 100644 — a/src/libvirtApi/domain.ts +++ b/src/libvirtApi/domain.ts @@ -591,12 +591,13 @@ export async function domainCreate(...) { await hashPasswords(args); await python.spawn( + const p = python.spawn( installVmScript,
[JSON.stringify(args)], + [], { err: "message", environ: ['LCALL=C.UTF-8'], ...(connectionName === "system" ? { superuser: "try" } : { }) }); + p.input(JSON.stringify(args)); + await p; @@ -1022,13 +1023,14 @@ export async function domainInstall({ vm } : { vm: VM }): Promise<string> {
return python.spawn( + const p = python.spawn( installVmScript,
[args], + [], { err: "message", environ: ['LCALL=C.UTF-8'], ...(vm.connectionName === "system" ? { superuser: "try" } : { })
})
.catch(ex => { + }); + p.input(args); + return p.catch(ex => { console.error(JSON.stringify(ex)); return Promise.reject(ex); })
------ This report was generated using AI technology. Always review AI-generated content prior to use