CVE-2022-30951
Description
Jenkins WMI Windows Agents Plugin 1.8 and earlier lacks access control in the Windows Remote Command library, allowing users to start processes even without login permission.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins WMI Windows Agents Plugin 1.8 and earlier lacks access control in the Windows Remote Command library, allowing users to start processes even without login permission.
Vulnerability
Jenkins WMI Windows Agents Plugin (formerly windows-slaves) versions 1.8 and earlier include the Windows Remote Command library, which does not implement access control. This allows users who can configure agents to start arbitrary processes on Windows agent nodes, even if they are not permitted to log in interactively. The affected plugin version is 1.8 and all earlier versions. [1][4]
Exploitation
An attacker with at least Overall/Read and Agent/Configure permissions (or equivalent) can exploit this by configuring a Windows agent to use the WMI launcher. The plugin will then use the Windows Remote Command library to start processes on the agent without checking whether the user has the right to log in. No additional authentication is required beyond Jenkins credentials. [1][2]
Impact
Successful exploitation allows the attacker to start arbitrary processes on the Windows agent machine. This can lead to unauthorized code execution, data exfiltration, or lateral movement within the network, depending on the agent's privileges and network configuration. The attacker gains the ability to execute commands as the agent's Windows service account. [1][4]
Mitigation
The vulnerability is fixed in Jenkins WMI Windows Agents Plugin version 1.8.1, released on 2022-05-17. Users should upgrade to 1.8.1 or later. No workaround is available. The plugin is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of this writing. [2][3]
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:windows-slavesMaven | < 1.8.1 | 1.8.1 |
Affected products
2- Jenkins project/Jenkins WMI Windows Agents Pluginv5Range: unspecified
Patches
14638cf0e56ca[SECURITY-2604]
6 files changed · +6 −286
docs/troubleshooting.adoc+0 −30 modified@@ -169,36 +169,6 @@ NAT is not security damn it! == Windows registry related issues -=== Remote Communication Service - -The Remote Communication Service "RemComSvc" must be running in order to launch commands remotely. -If not started, Jenkins will try to (remotely) start this service, assuming it is well configured. -If not, you may get an error like : - -``` -.... -Checking if Java exists -ERROR: Failed to prepare Java -java.lang.reflect.UndeclaredThrowableException -.... -``` - -In some cases (especially Windows 2008 R2) this can be caused by the lack of the Visual C++ runtime libraries needed by the service. -If this is the case you will see an error in the windows event log similar to: - -``` -.... -Activation context generation failed for "C:\Windows\RemComSvc.exe". -Dependent Assembly Microsoft.VC90.CRT,processorArchitecture="x86",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.21022.8" could not be found. -Please use sxstrace.exe for detailed diagnosis. -.... -``` - -To solve this issue install the http://www.microsoft.com/download/en/details.aspx?id=5582[Visual C++ 2008 x86] libraries. - -The Remote Communication Service source code is a -https://github.com/jenkinsci/lib-windows-remote-command[part of Jenkins]. - === Remote Registry Service The Remote Registry service must be running in order to install the Jenkins service, but it may be stopped on your computer.
pom.xml+2 −6 modified@@ -60,19 +60,15 @@ <dependencies> <dependency> <groupId>org.jenkins-ci</groupId> - <artifactId>windows-remote-command</artifactId> - <version>1.4</version> + <artifactId>jinterop-wmi</artifactId> + <version>1.1</version> <exclusions> <exclusion> <groupId>org.jvnet</groupId> <artifactId>tiger-types</artifactId> </exclusion> </exclusions> </dependency> - <dependency> - <groupId>org.jenkins-ci.plugins</groupId> - <artifactId>jdk-tool</artifactId> - </dependency> <dependency> <groupId>io.jenkins</groupId> <artifactId>configuration-as-code</artifactId>
README.adoc+4 −8 modified@@ -18,7 +18,10 @@ image:docs/images/configuration.png[] This section goes into the details of how the managed Windows agent launcher actually works. -This launcher uses several protocols that has been around for a quite some time. +Jenkins uses multiple protocols to install the actual agent as a Windows service and then start it. These protocols have been around for a quite some time. + +IMPORTANT: The installation process assumes that the JRE is installed and accessible on the agent. Please see link:docs/troubleshooting.adoc[Troubleshooting WMI Windows Agents] if you need further help. + * It first uses http://en.wikipedia.org/wiki/Server_Message_Block[CIFS] (also known as "Windows file share protocol") to push files into the agent. @@ -29,13 +32,6 @@ http://en.wikipedia.org/wiki/Distributed_Component_Object_Model[DCOM] to talk to http://en.wikipedia.org/wiki/Windows_Management_Instrumentation[WMI] to install and start a service remotely. -* Jenkins uses two services, one is called -https://github.com/jenkinsci/lib-windows-remote-command[Remote Communication Service] and this provides a general-purpose remote command execution capability. -Jenkins uses this to check if Java is available and if not install it. -A failure to do this is not fatal problem, as Jenkins will proceed by assuming that Java is available in a reasonable place. -This service is destroyed after it is used so as not to cause any harm to the security. -The communication between Jenkins controller and this service happens over a named pipe, which itself is protected by access control. -* Jenkins then installs the actual agent as a Windows service, by using the WMI over DCOM, then it starts this service. == Troubleshooting
src/main/java/hudson/os/windows/ManagedWindowsServiceLauncher.java+0 −37 modified@@ -37,9 +37,6 @@ import hudson.remoting.Channel.Listener; import hudson.remoting.SocketChannelStream; import hudson.slaves.*; -import hudson.tools.JDKInstaller; -import hudson.tools.JDKInstaller.CPU; -import hudson.tools.JDKInstaller.Platform; import hudson.util.DescribableList; import hudson.util.Secret; import hudson.util.jna.DotNet; @@ -48,7 +45,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.URL; import java.net.UnknownHostException; import java.util.Date; import java.util.logging.Level; @@ -67,7 +63,6 @@ import org.jinterop.dcom.common.JIDefaultAuthInfoImpl; import org.jinterop.dcom.common.JIException; import org.jinterop.dcom.core.JISession; -import org.jvnet.hudson.remcom.WindowsRemoteProcessLauncher; import org.jvnet.hudson.wmi.SWbemServices; import org.jvnet.hudson.wmi.WMI; import org.jvnet.hudson.wmi.Win32Service; @@ -221,38 +216,6 @@ public void launch(final SlaveComputer computer, final TaskListener listener) th String java = resolveJava(computer); - try {// does Java exist? - logger.println("Checking if Java exists"); - WindowsRemoteProcessLauncher wrpl = new WindowsRemoteProcessLauncher(name, auth); - Process proc = wrpl.launch("\"" + java + "\" -version", "c:\\"); - proc.getOutputStream().close(); - StringWriter console = new StringWriter(); - IOUtils.copy(proc.getInputStream(), console); - proc.getInputStream().close(); - int exitCode = proc.waitFor(); - if (exitCode == 1) {// we'll get this error code if Java is not found - logger.println("No Java found. Downloading JDK"); - JDKInstaller jdki = new JDKInstaller("jdk-6u16-oth-JPR@CDS-CDS_Developer", true); - URL jdk = jdki.locate(listener, Platform.WINDOWS, CPU.i386); - - listener.getLogger().println("Installing JDK"); - copyStreamAndClose(jdk.openStream(), new SmbFile(remoteRoot, "jdk.exe").getOutputStream()); - - String javaDir = path + "\\jdk"; // this is where we install Java to - - WindowsRemoteFileSystem fs = new WindowsRemoteFileSystem(name, createSmbAuth()); - fs.mkdirs(javaDir); - - jdki.install(new WindowsRemoteLauncher(listener, wrpl), Platform.WINDOWS, - fs, listener, javaDir, path + "\\jdk.exe"); - } else { - checkJavaVersion(logger, java, new BufferedReader(new StringReader(console.toString()))); - } - } catch (Exception e) { - e.printStackTrace(listener.error("Failed to prepare Java")); - return; - } - // this just doesn't work --- trying to obtain the type or check the existence of smb://server/C$/ results in "access denied" // {// check if the administrative share exists // String fullpath = remoteRoot.getPath();
src/main/java/hudson/os/windows/WindowsRemoteFileSystem.java+0 −65 removed@@ -1,65 +0,0 @@ -package hudson.os.windows; - -import hudson.tools.JDKInstaller.FileSystem; -import jcifs.smb.NtlmPasswordAuthentication; -import jcifs.smb.SmbFile; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.util.List; - -import static java.util.Arrays.asList; - -/** - * {@link FileSystem} implementation for remote Windows system. - * - * @author Kohsuke Kawaguchi - */ -public class WindowsRemoteFileSystem implements FileSystem { - private final String hostName; - private final NtlmPasswordAuthentication auth; - - public WindowsRemoteFileSystem(String hostName, NtlmPasswordAuthentication auth) { - this.hostName = hostName; - this.auth = auth; - } - - private SmbFile $(String path) throws MalformedURLException { - return new SmbFile("smb://" + hostName + "/" + path.replace('\\', '/').replace(':', '$') + "/", auth); - } - - @Override - public void delete(String file) throws IOException, InterruptedException { - $(file).delete(); - } - - @Override - public void chmod(String file, int mode) throws IOException, InterruptedException { - // no-op on Windows - } - - @Override - public InputStream read(String file) throws IOException { - return $(file).getInputStream(); - } - - @Override - public List<String> listSubDirectories(String dir) throws IOException, InterruptedException { - return asList($(dir).list()); - } - - @Override - public void pullUp(String from, String to) throws IOException, InterruptedException { - SmbFile src = $(from); - SmbFile dst = $(to); - for (SmbFile e : src.listFiles()) { - e.renameTo(new SmbFile(dst, e.getName())); - } - src.delete(); - } - - public void mkdirs(String path) throws IOException { - $(path).mkdirs(); - } -}
src/main/java/hudson/os/windows/WindowsRemoteLauncher.java+0 −140 removed@@ -1,140 +0,0 @@ -package hudson.os.windows; - -import hudson.FilePath; -import hudson.Launcher; -import hudson.Proc; -import hudson.Util; -import hudson.model.Computer; -import hudson.model.TaskListener; -import hudson.remoting.Channel; -import hudson.remoting.ChannelBuilder; -import hudson.util.StreamCopyThread; -import org.jinterop.dcom.common.JIException; -import org.jvnet.hudson.remcom.WindowsRemoteProcessLauncher; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Map; - -import static java.util.Arrays.asList; - -/** - * Pseudo-{@link Launcher} implementation that uses {@link WindowsRemoteProcessLauncher} - * - * @author Kohsuke Kawaguchi - */ -public class WindowsRemoteLauncher extends Launcher { - private final WindowsRemoteProcessLauncher launcher; - - public WindowsRemoteLauncher(TaskListener listener, WindowsRemoteProcessLauncher launcher) { - super(listener, null); - this.launcher = launcher; - } - - private String buildCommandLine(ProcStarter ps) { - StringBuilder b = new StringBuilder(); - for (String cmd : ps.cmds()) { - if (b.length() > 0) b.append(' '); - if (cmd.indexOf(' ') >= 0) - b.append('"').append(cmd).append('"'); - else - b.append(cmd); - } - return b.toString(); - } - - @Override - public Proc launch(ProcStarter ps) throws IOException { - FilePath pwd = ps.pwd(); - if (pwd == null) { - throw new IOException("Cannot access the process location."); - } - maskedPrintCommandLine(ps.cmds(), ps.masks(), pwd); - - // TODO: environment variable handling - - String name = ps.cmds().toString(); - - final Process proc; - try { - proc = launcher.launch(buildCommandLine(ps), pwd.getRemote()); - } catch (JIException | InterruptedException e) { - throw new IOException(e); - } - final Thread t1 = new StreamCopyThread("stdout copier: " + name, proc.getInputStream(), ps.stdout(), false); - t1.start(); - final Thread t2 = new StreamCopyThread("stdin copier: " + name, ps.stdin(), proc.getOutputStream(), true); - t2.start(); - - return new Proc() { - - @Override - public boolean isAlive() throws IOException, InterruptedException { - try { - proc.exitValue(); - return false; - } catch (IllegalThreadStateException e) { - return true; - } - } - - @Override - public void kill() throws IOException, InterruptedException { - t1.interrupt(); - t2.interrupt(); - proc.destroy(); - } - - @Override - public int join() throws IOException, InterruptedException { - try { - t1.join(); - t2.join(); - return proc.waitFor(); - } finally { - proc.destroy(); - } - } - - @Override - public InputStream getStdout() { - throw new UnsupportedOperationException(); - } - - @Override - public InputStream getStderr() { - throw new UnsupportedOperationException(); - } - - @Override - public OutputStream getStdin() { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public Channel launchChannel(String[] cmd, OutputStream out, FilePath _workDir, Map<String, String> envVars) throws IOException, InterruptedException { - if (_workDir == null) { - throw new IOException("Cannot access local process workdir."); - } - printCommandLine(cmd, _workDir); - - try { - Process proc = launcher.launch(Util.join(asList(cmd), " "), _workDir.getRemote()); - - return new ChannelBuilder("channel over named pipe to " + launcher.getHostName(), Computer.threadPoolForRemoting) - .withMode(Channel.Mode.BINARY) - .build(proc.getInputStream(), new BufferedOutputStream(proc.getOutputStream())); - } catch (JIException e) { - throw new IOException(e); - } - } - - @Override - public void kill(Map<String, String> modelEnvVars) throws IOException, InterruptedException { - // no way to do this - } -}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-p566-wpxx-574mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-30951ghsaADVISORY
- www.openwall.com/lists/oss-security/2022/05/17/8ghsamailing-listx_refsource_MLISTWEB
- github.com/jenkinsci/windows-slaves-plugin/commit/4638cf0e56caf839eadfdf0fab545abd2a9ac65eghsaWEB
- www.jenkins.io/security/advisory/2022-05-17/ghsax_refsource_CONFIRMWEB
News mentions
1- Jenkins Security Advisory 2022-05-17Jenkins Security Advisories · May 17, 2022