CVE-2023-27899
Description
Jenkins 2.393 and earlier, LTS 2.375.3 and earlier creates a temporary file in the default temporary directory with the default permissions for newly created files when uploading a plugin for installation, potentially allowing attackers with access to the Jenkins controller file system to read and write the file before it is used, potentially resulting in arbitrary code execution.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins creates a plugin upload temp file with default permissions, allowing local attackers to read/write it before installation leading to potential code execution.
Vulnerability
Details
Jenkins 2.393 and earlier, including LTS 2.375.3 and earlier, creates a temporary file in the default temporary directory with default permissions when uploading a plugin for installation [1][2]. The root cause is the use of DiskFileItemFactory without explicitly controlling the repository directory or file permissions, leaving the temporary file readable and writable by other local users during the upload processing window [3].
Exploitation
Conditions
An attacker must have file system access to the Jenkins controller (e.g., a user with an account on the same Linux/Windows host, or via another vulnerability that provides local access). The attack requires that the attacker can read and write files in the default system temporary directory where Jenkins drops the uploaded plugin before validation and installation [1][2]. No authentication is required other than that needed for local system access.
Impact
By reading or modifying the temporary plugin file before it is processed, an attacker could inject malicious code or replace the plugin entirely. When Jenkins later installs the manipulated plugin, the injected code would execute in the context of the Jenkins controller, leading to arbitrary code execution [1][2]. The advisory rates this vulnerability as High severity (CVSS 8.8) [1].
Mitigation
Jenkins 2.394, LTS 2.375.4, and LTS 2.387.1 fix the issue by creating a dedicated temporary directory with restricted permissions (owner-only read/write) for uploads [1][3]. The commit shows that Files.createTempDirectory("uploadDir") is used and permissions are explicitly set to OWNER_READ and OWNER_WRITE only [3]. Users should upgrade to these or later versions.
AI Insight generated on May 20, 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.main:jenkins-coreMaven | >= 2.376, < 2.387.1 | 2.387.1 |
org.jenkins-ci.main:jenkins-coreMaven | < 2.375.4 | 2.375.4 |
org.jenkins-ci.main:jenkins-coreMaven | >= 2.388, < 2.394 | 2.394 |
Affected products
9- osv-coords8 versionspkg:apk/chainguard/jenkinspkg:apk/chainguard/jenkins-compatpkg:apk/chainguard/jenkins-remotingpkg:apk/wolfi/jenkinspkg:apk/wolfi/jenkins-compatpkg:apk/wolfi/jenkins-remotingpkg:bitnami/jenkinspkg:maven/org.jenkins-ci.main/jenkins-core
< 2.395-r0+ 7 more
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.395-r0
- (no CPE)range: < 2.394.0
- (no CPE)range: >= 2.376, < 2.387.1
- Jenkins Project/Jenkinsv5Range: 2.394
Patches
12 files changed · +77 −1
core/src/main/java/hudson/PluginManager.java+20 −1 modified@@ -28,6 +28,8 @@ import static hudson.init.InitMilestone.PLUGINS_LISTED; import static hudson.init.InitMilestone.PLUGINS_PREPARED; import static hudson.init.InitMilestone.PLUGINS_STARTED; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -81,15 +83,18 @@ import java.net.URLConnection; import java.net.http.HttpClient; import java.net.http.HttpRequest; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.security.CodeSource; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -1860,7 +1865,8 @@ public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, Servl String fileName = ""; PluginCopier copier; - ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); + File tmpDir = Files.createTempDirectory("uploadDir").toFile(); + ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, tmpDir)); List<FileItem> items = upload.parseRequest(req); if (StringUtils.isNotBlank(items.get(1).getString())) { // this is a URL deployment @@ -1873,6 +1879,16 @@ public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, Servl copier = new FileUploadPluginCopier(fileItem); } + if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { + Arrays.stream(Objects.requireNonNull(tmpDir.listFiles())).forEach((file -> { + try { + Files.setPosixFilePermissions(file.toPath(), EnumSet.of(OWNER_READ, OWNER_WRITE)); + } catch (IOException e) { + throw new RuntimeException(e); + } + })); + } + if ("".equals(fileName)) { return new HttpRedirect("advanced"); } @@ -1893,6 +1909,9 @@ public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, Servl throw new ServletException(e); } copier.cleanup(); + if (!tmpDir.delete()) { + System.err.println("Failed to delete temporary directory: " + tmpDir); + } final String baseName = identifyPluginShortName(t);
test/src/test/java/hudson/PluginManagerSecurity2823Test.java+57 −0 added@@ -0,0 +1,57 @@ +package hudson; + +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeFalse; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; + +public class PluginManagerSecurity2823Test { + + @Rule + public JenkinsRule r = PluginManagerUtil.newJenkinsRule(); + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + @Issue("SECURITY-2823") + public void verifyUploadedPluginPermission() throws Exception { + assumeFalse(Functions.isWindows()); + + HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); + HtmlForm f = page.getFormByName("uploadPlugin"); + File dir = tmp.newFolder(); + File plugin = new File(dir, "htmlpublisher.jpi"); + FileUtils.copyURLToFile(Objects.requireNonNull(getClass().getClassLoader().getResource("plugins/htmlpublisher.jpi")), plugin); + f.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); + r.submit(f); + + File tmpDir = Files.createTempFile("tmp", ".tmp").getParent().toFile(); + tmpDir.deleteOnExit(); + + Optional<File> lastUploadedPlugin = Arrays.stream(Objects.requireNonNull(tmpDir.listFiles((file, fileName) -> fileName.startsWith("uploaded")))).max(Comparator.comparingLong(File::lastModified)); + Set<PosixFilePermission> filesPermission = Files.getPosixFilePermissions(lastUploadedPlugin.get().toPath(), LinkOption.NOFOLLOW_LINKS); + + assertEquals(EnumSet.of(OWNER_READ, OWNER_WRITE), filesPermission); + } + +}
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-hf9h-vv4m-2f33ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-27899ghsaADVISORY
- www.jenkins.io/security/advisory/2023-03-08/ghsavendor-advisoryWEB
- github.com/CVEProject/cvelist/blob/master/2023/27xxx/CVE-2023-27899.jsonghsaWEB
- github.com/jenkinsci/jenkins/commit/f39c11fa27b14923260c4c9b896f0f373e2a0a17ghsaWEB
News mentions
1- Jenkins Security Advisory 2023-03-08Jenkins Security Advisories · Mar 8, 2023