VYPR
High severityNVD Advisory· Published Mar 8, 2023· Updated Feb 28, 2025

CVE-2023-27899

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.

PackageAffected versionsPatched versions
org.jenkins-ci.main:jenkins-coreMaven
>= 2.376, < 2.387.12.387.1
org.jenkins-ci.main:jenkins-coreMaven
< 2.375.42.375.4
org.jenkins-ci.main:jenkins-coreMaven
>= 2.388, < 2.3942.394

Affected products

9

Patches

1
f39c11fa27b1

[SECURITY-2823]

https://github.com/jenkinsci/jenkinsKevin-CBFeb 23, 2023via ghsa
2 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

News mentions

1