VYPR
Moderate severityNVD Advisory· Published Apr 12, 2022· Updated Oct 15, 2024

CVE-2022-29052

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.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:google-compute-engineMaven
< 4.3.94.3.9

Affected products

2

Patches

1
16d2ae71a1b3

[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

News mentions

1