Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SECURITY-3911 / SECURITY-3930 Workaround

This is a Java agent for use with Jenkins controllers that do not yet have the fixes for SECURITY-3911 and SECURITY-3930.

SECURITY-3911 affects the Remoting library used for communication with build agents. It allows bypassing the class filter during Java deserialization.

SECURITY-3930 affects tar extraction in FilePath. It allows a crafted tar archive to bypass existing path traversal protections.

Important

Jenkins 2.576, LTS 2.568.2 contain additional fixes for security issue that this agent does not protect from. Those security issues have additional prerequisites that make exploitation more difficult. Update Jenkins to protect from all issues fixed in those releases.

Compatibility

This workaround has been tested with a wide range of Jenkins and Remoting releases:

SECURITY-3911 (Remoting): This Java agent will apply the fix on all Remoting releases used in Jenkins 2.164 and newer (early 2019). The fix is not applied if the class is already patched (e.g., Remoting 3385.vf1123fb_515da_ and newer). If transformation for this issue fails for any reason, Jenkins is forcibly stopped.

SECURITY-3930 (FilePath): This Java agent will apply this fix on Jenkins 2.555 and newer, and Jenkins LTS 2.541.3. Older releases lack the fix for SECURITY-3657, which is a prerequisite for this patch to be applied. The fix is not applied if the class is already patched (Jenkins 2.576 and newer, and LTS 2.568.2 and newer). If transformation fails, a message is logged.

Important

Remove this Java agent as soon as you update to a Jenkins release with the fixes (2.576 or newer, LTS 2.568.2 or newer). It is fairly likely for this Java agent to start failing, potentially in subtle ways, once the Java classes it tries to patch are changed further as part of regular development.

Usage

Download the workaround JAR and add it as a Java agent when starting the Jenkins controller:

java -javaagent:/path/to/security3911-3930-workaround.jar -jar jenkins.war

The Java agent must be on the controller process. It does not need to be deployed to build agents.

Other Jenkins startup methods

If you start Jenkins via a service wrapper, package manager, or jenkins.xml (Windows), add the -javaagent argument to the JVM options in the relevant configuration file before the -jar jenkins.war argument.

For example in /etc/default/jenkins (Debian/Ubuntu packages):

JAVA_ARGS="-javaagent:/path/to/security3911-3930-workaround.jar"

Configuration

The following Java system properties change the Java agent's behavior. Set them as -D JVM arguments before the -javaagent argument.

  • io.jenkins.security.Security39113930Workaround.DISABLE Set to true to disable all class transformations. Not recommended in production.

  • io.jenkins.security.Security39113930Workaround.SKIP_SHUTDOWN Set to true to log a failure rather than stopping Jenkins when the mandatory transformation (SECURITY-3911) fails. Only recommended in specific situations, such as when closely monitoring log output (see below). Jenkins will not be protected if this is set and transformation fails.

Logging

Log messages are written directly to stderr and use the prefix SECURITY-3911/3930 Workaround: to be compatible with the --logfile argument to java -jar jenkins.war. As a result, the log messages from this Java agent do not appear in the System Log UI.

Verification

The following scripts can be used to confirm the workarounds are active (or Jenkins has already been updated). Navigate to the Jenkins script console in "Manage Jenkins", enter a script there, and press "Run". The output will tell you whether Jenkins is vulnerable or protected.

SECURITY-3911

The workaround for SECURITY-3911 is expected to work on Jenkins 2.164 and newer.

def notLoader = new ClassLoader(null) {
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        throw new ClassNotFoundException(name)
    }
}

def blockingFilter = new hudson.remoting.ClassFilter() {
    public boolean isBlacklisted(Class c) {
        return c == java.util.concurrent.atomic.AtomicLong
    }

    public boolean isBlacklisted(String name) {
        return false
    }
}

def baos = new ByteArrayOutputStream()
new ObjectOutputStream(baos).with { writeObject(new java.util.concurrent.atomic.AtomicLong()); close() }

try {
    // We could use ClassFilter.DEFAULT here, but if AtomicLong is added to the allowlist, this will fail unexpectedly
    def ois = new hudson.remoting.ObjectInputStreamEx(new ByteArrayInputStream(baos.toByteArray()), notLoader, blockingFilter)
    def obj = ois.readObject()
    ois.close()
    println "VULNERABLE: readObject returned ${obj?.class?.name} — SECURITY-3911 fix is NOT active"
} catch (SecurityException e) {
    println "PROTECTED: filter.check(Class) blocked in catch branch — SECURITY-3911 fix is active. ${e.message}"
} catch (Exception e) {
    print "Unexpected Exception (unexpected message): "
        e.printStackTrace(out)
}

SECURITY-3930

The workaround for SECURITY-3930 is expected to work on Jenkins 2.555 and newer, and Jenkins LTS 2.541.3.

def baos = new ByteArrayOutputStream()
def tos = new org.apache.tools.tar.TarOutputStream(baos)
def entry = new org.apache.tools.tar.TarEntry(".", org.apache.tools.tar.TarConstants.LF_SYMLINK)
entry.setLinkName("/test")
tos.putNextEntry(entry)
tos.closeEntry()
tos.close()

File tempDir = java.nio.file.Files.createTempDirectory("jenkins-readFromTar-test-").toFile()
try {
    hudson.FilePath.readFromTar("test.tar", tempDir, new ByteArrayInputStream(baos.toByteArray()), java.nio.charset.StandardCharsets.UTF_8)
    println "VULNERABLE to SECURITY-3930: readFromTar returned normally"
} catch (IOException e) {
    Exception actual = e.cause?:e
    if (actual.message?.contains("non-directory entry that resolves to base directory")) {
        println "PROTECTED from SECURITY-3930: Fix is active. Exception: ${e.message}"
    } else {
        print "Unexpected IOException (unexpected message): "
        e.printStackTrace(out)
    }
} finally {
    tempDir.deleteDir()
}

License

Licensed under the terms of the MIT License. Copyright 2026 CloudBees, Inc.

Releases

Packages

Contributors

Languages