CVE-2022-29052
Description
Jenkins Google Compute Engine Plugin 4.3.8 and earlier stores private keys unencrypted in cloud agent config.xml files, exposing them to users with Extended Read permission or file system access.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins Google Compute Engine Plugin 4.3.8 and earlier stores private keys unencrypted in cloud agent config.xml files, exposing them to users with Extended Read permission or file system access.
Vulnerability
Jenkins Google Compute Engine Plugin version 4.3.8 and earlier stores private keys used for SSH authentication in cloud agent config.xml files on the Jenkins controller in plaintext [1][2]. These keys are written unencrypted, making them accessible to any user who can read those files.
Exploitation
An attacker needs either Extended Read permission on the Jenkins controller (which allows reading most configuration files) or direct access to the controller's file system. With that access, they can retrieve the private key from the config.xml file of any cloud agent that uses the plugin [1].
Impact
Successful exploitation allows the attacker to obtain the unencrypted SSH private key. This key can then be used to authenticate to the corresponding Google Compute Engine instances, potentially leading to unauthorized access to those instances and the data they contain [1][2].
Mitigation
The fix was released in Google Compute Engine Plugin version 4.3.9, which encrypts the private key using Jenkins' Secret mechanism [3]. Users should upgrade to version 4.3.9 or later. No workaround is available for earlier versions [1].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins:google-compute-engineMaven | < 4.3.9 | 4.3.9 |
Affected products
2- Range: unspecified
Patches
116d2ae71a1b3[SECURITY-2045]
4 files changed · +38 −8
src/main/java/com/google/jenkins/plugins/computeengine/ComputeEngineLinuxLauncher.java+2 −1 modified@@ -20,6 +20,7 @@ import com.google.jenkins.plugins.computeengine.ssh.GoogleKeyPair; import com.trilead.ssh2.Connection; import hudson.model.TaskListener; +import hudson.util.Secret; import java.io.IOException; import java.util.Optional; import java.util.logging.Logger; @@ -81,7 +82,7 @@ private Optional<Connection> bootstrap( bootstrapConn = connectToSsh(computer, listener); isAuthenticated = bootstrapConn.authenticateWithPublicKey( - node.getSshUser(), kp.getPrivateKey().toCharArray(), ""); + node.getSshUser(), Secret.toString(kp.getPrivateKey()).toCharArray(), ""); } catch (IOException e) { logException(computer, listener, "Exception trying to authenticate", e); if (bootstrapConn != null) {
src/main/java/com/google/jenkins/plugins/computeengine/ssh/GoogleKeyPair.java+6 −5 modified@@ -16,35 +16,36 @@ package com.google.jenkins.plugins.computeengine.ssh; +import hudson.util.Secret; import java.io.Serializable; import java.util.Map; public class GoogleKeyPair implements Serializable { - private final String privateKey; + private final Secret privateKey; private final String publicKey; private final String user; - private GoogleKeyPair(String publicKey, String privateKey, String user) { + private GoogleKeyPair(String publicKey, Secret privateKey, String user) { this.publicKey = user + ":" + publicKey + " " + user; this.privateKey = privateKey; this.user = user; } public static GoogleKeyPair generate(String user) { Map<String, String> keys = SshKeysHelper.generate(); - return new GoogleKeyPair(keys.get("public"), keys.get("private"), user); + return new GoogleKeyPair(keys.get("public"), Secret.fromString(keys.get("private")), user); } public String getPublicKey() { return publicKey; } - public String getPrivateKey() { + public Secret getPrivateKey() { return privateKey; } @Override public String toString() { - return "Public key:\n" + publicKey + "\n\nPrivate key:\n" + privateKey; + return "Public key:\n" + publicKey + "\n\nPrivate key:\n" + privateKey.getEncryptedValue(); } }
src/test/java/com/google/jenkins/plugins/computeengine/GoogleKeyPairTest.java+26 −0 modified@@ -16,17 +16,43 @@ package com.google.jenkins.plugins.computeengine; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import com.google.jenkins.plugins.computeengine.ssh.GoogleKeyPair; +import hudson.util.XStream2; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.WithoutJenkins; + +import java.io.File; +import java.nio.charset.StandardCharsets; public class GoogleKeyPairTest { + @Rule public JenkinsRule r = new JenkinsRule(); + + @WithoutJenkins @Test public void KeyPairGeneration() { GoogleKeyPair gkp = GoogleKeyPair.generate("user"); assertNotNull(gkp.toString()); assert (gkp.getPublicKey().contains("user")); } + + @Issue("SECURITY-2045") + @Test + public void privateKeyNotStoredAsPlainTextOnDisk() throws Exception { + GoogleKeyPair sshKeyPair = GoogleKeyPair.generate("test-user"); + File configFile = new File(r.jenkins.getRootDir(), sshKeyPair.getClass().getName() + ".xml"); + FileUtils.write(configFile, new XStream2().toXML(sshKeyPair), StandardCharsets.UTF_8); + + String configAsString = FileUtils.readFileToString(configFile, StandardCharsets.UTF_8); + assertTrue(configAsString.contains(sshKeyPair.getPrivateKey().getEncryptedValue())); + assertFalse(configAsString.contains(sshKeyPair.getPrivateKey().getPlainText())); + } }
src/test/java/com/google/jenkins/plugins/computeengine/integration/ITUtil.java+4 −2 modified@@ -64,6 +64,7 @@ import hudson.plugins.powershell.PowerShell; import hudson.tasks.Builder; import hudson.tasks.Shell; +import hudson.util.Secret; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -131,7 +132,7 @@ class ITUtil { private static final String LAUNCH_TIMEOUT_SECONDS_STR = ""; static final int SNAPSHOT_TIMEOUT = windows ? 600 : 300; private static final GoogleKeyPair SSH_KEY = GoogleKeyPair.generate(RUN_AS_USER); - static final String SSH_PRIVATE_KEY = SSH_KEY.getPrivateKey(); + static final String SSH_PRIVATE_KEY = Secret.toString(SSH_KEY.getPrivateKey()); private static final String WINDOWS_STARTUP_SCRIPT = "Stop-Service sshd\n" + "$ConfiguredPublicKey = " @@ -203,7 +204,8 @@ private static String initWindowsSshCredentials(CredentialsStore store) throws I CredentialsScope.GLOBAL, null, RUN_AS_USER, - new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(SSH_KEY.getPrivateKey()), + new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource( + Secret.toString(SSH_KEY.getPrivateKey())), null, "integration test private key for windows"); store.addCredentials(Domain.global(), windowsPrivateKeyCredentials);
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-vhxq-9mpv-gj87ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-29052ghsaADVISORY
- github.com/jenkinsci/google-compute-engine-plugin/commit/16d2ae71a1b34c81db1d74f83c41577536e5256fghsaWEB
- www.jenkins.io/security/advisory/2022-04-12/ghsax_refsource_CONFIRMWEB
News mentions
1- Jenkins Security Advisory 2022-04-12Jenkins Security Advisories · Apr 12, 2022