Foojay Today

You’re Running Untrusted Code!

January 17, 2022

Last December, Log4Shell shortened the nights of many people in the JVM world. Worse, using the earthquake analogy caused many aftershocks after the initial quake.

I immediately made the connection between Log4Shell and the Security Manager. At first, I didn't want to write about it, but I've received requests to do so, and I couldn't walk away.

As a reminder, the Oracle team deprecated the Security Manager in Java 17. One of the arguments it based its decision on is that it was initially designed to protect against applets. Applets were downloaded from the Internet, so they had to be considered untrusted code. Hence, we had to run them in a sandbox.

Though they never said so, there's an implicit consequence of this statement: because applets are now deprecated, we run only trusted code. Ergo, we can let go of the Security Manager. It's plain wrong, and I'll explain why in this post.

The premise that the code that runs inside your infrastructure can be trusted is dangerous - on-premise or in the Cloud. Let me enumerate some arguments that support this claim.

Libraries can't be trusted

Wise developers don't reinvent the wheel: they use existing libraries and/or frameworks.

Obviously, from a security point of view, it means users of such third-party code should carefully audit it. We should look for flaws: both bugs and vulnerabilities.

In two decades in the industry, I've never seen such an audit happen.

One could argue in favor of custom code. Unfortunately, it doesn't solve anything. Custom code suffers from the same issues, bugs, and vulnerabilities. Worse, it doesn't get the same attention as standard libraries, so researchers cannot spend their time to find these issues, which costs nothing.

Builds can't be trusted

Imagine that you have all resources necessary to audit the code - time, money, and skills. Imagine further that the audit reveals nothing fishy. Finally, imagine that the audit's conclusion is 100% reliable.

The issue is that nothing guarantees that the JAR is the result of the build from the source code, even if the build is public. A malicious provider could replace the genuine JAR with another one.

Identities can't be trusted

A provider can sign a JAR to guarantee it's genuine. The signature is based on asymmetric cryptography:

  1. The provider signs the JAR with its private key
  2. It generates a public key with the private key
  3. One can read the signature using the public key and check that the provider signed the JAR.

Hence, anybody can verify that a JAR comes from a specific provider.

The JDK provides the jarsigner tool to sign JARs. Unfortunately, most libraries don't use it. As an example, I've verified the following dependencies:

  • org.slf4j:slf4j-api:1.7.32
  • com.fasterxml.jackson.core:jackson-core:2.13.0
  • org.mockito:mockito-core:4.1.0
  • org.junit.jupiter:junit-jupiter-api:5.8.2
  • org.apache.commons:commons-collections4:4.4
  • org.eclipse.collections:eclipse-collections:10.4.0
  • com.google.protobuf:protobuf-java:3.18.0
  • com.itextpdf:itextpdf:5.5.13.2
  • com.zaxxer:HikariCP:5.0.0
  • com.vladmihalcea.flexy-pool:flexy-pool-core:2.2.3
  • org.springframework:spring-beans:5.3.13
  • jakarta.platform:jakarta.jakartaee-api:9.1.0

Among the twelve JARs above, only a single one is signed with jarsigner. If you're interested, it's Eclipse Collections.

However, to counter supply-chain attacks, artifact repositories have started to require signed artifacts. For example, Sonatype requires a signature for each uploaded file, i.e., the POM, the JAR, the sources JAR, the JavaDocs JAR, etc.

One can verify the signature with Maven:

mvn org.simplify4u.plugins:pgpverify-maven-plugin:show -Dartifact=com.zaxxer:HikariCP:5.0.0

It outputs the following:

Artifact:
        groupId:     com.zaxxer
        artifactId:  HikariCP
        type:        jar
        version:     5.0.0

PGP signature:
        version:     4
        algorithm:   SHA256withRSA
        keyId:       0x4CC08E7F47C3EC76
        create date: Wed Jul 14 04:49:52 CEST 2021
        status:      valid

PGP key:
        version:     4
        algorithm:   RSA (Encrypt or Sign)
        bits:        2048
        fingerprint: 0xF3A90E6B10E809F851AB4FC54CC08E7F47C3EC76
        create date: Wed Sep 18 02:51:23 CEST 2013
        uids:        [Brett Wooldridge (Sonatype) <[email protected]>]

However, none of this amounts to much. Signing doesn't assert the identity of the provider. It tells that a private key with the referenced email signed it with a private key with the referenced email. Nothing prevents a malicious actor from creating another private key with the same email or a similar one.

Features can't be trusted

At this point, I think the picture looks pretty gloomy. But it's even worse than that. None of the above explains the Log4J vulnerability. The core reason is that it provides features that most developers neither need nor use.

I don't want to delve into too much detail, as it already has been explained in many places. Suffice to say that Log4J provides lookups. A lookup is an integration with another system, which allows enriching the log beyond the mere message. For example, the Spring Boot lookup allows getting Spring Boot properties. It makes sense to enrich the log, for example, with spring.application.name.

In all available lookups, some seem a bit fishy. For example, environment variables, system properties, or even JNDI. It's the latter that is the root cause of the Log4J vulnerability.

This kind of hidden features is not specific to Log4J. I happen to know there's a Swing-based GUI administration application inside the H2 database driver. I learned about it just by chance.

The problem is that developers use a library for their core capability, e.g., logging. If one stops at that, one will never know all the library's capabilities. Hence, one will be surprised when the library does something it was not assumed to do, e.g., read from a remote JNDI resource tree.

The JVM can't be trusted

I admit the section's title is misleading, but I couldn't find a good one following the series. It's a follow-up to the previous section, this time applied to the JVM.

The JVM provides tons of features, of which you use a handful or two. The most blatant problem is the Attach API. This API, available since Java 1.6, allows a JVM to update the bytecode already loaded into another JVM. Yes, you read it correctly: you can change the bytecode of an application that's running. Worse, if you restart the JVM, the code will be loaded again, leaving no trace.

It's a cool feature if you want to quickly monkey-patch a fix in production.
However:

  • Most people don't use it
  • Most people don't know about it
  • The feature needs to be explicitly disabled. It's on by default.

May I suggest that the first thing you do tomorrow is to check your infrastructure and disable it?

The Security Manager could be trusted

I hope that at this point, you understand the problem. A lot of code that you're running can't be trusted. Worse, I'm only considering regular applications: software built on a plugin architecture run untrusted code by definition.

The Security Manager was a JVM component that allowed you to define a white list of what an application could do, regardless of the application code. It solved all the above issues: you could run any code but only allowed it to do a limited number of things.

The Security Manager came with several drawbacks, chief amongst them is that it was a bore to configure permissions. However, there are tools to generate the policy file. Since they are automated, you need to review the discovered permissions carefully. It's easier to read through ~500 lines of configuration than 10k or 100k lines of code.

Since many didn't know about tools, few did use the Security Manager. But when it was, it was very beneficial. To prove my claim, you can read this post or jump to the conclusion: though Elasticsearch embeds a vulnerable Log4J version, it's not susceptible to Log4Shell!

Conclusion

Security is a Non-Functional Requirement. NFRs don't bring any competitive advantage and cost money. In short, they divert the budget from business requirements to /dev/null. That's at least how most business departments see it.

I think we should handle security through the lenses of risk assessment. It requires first to list all possible risks. I'm afraid the deprecation of the Security Manager just added several lines to that risk, all linked to running untrusted code.

Note that the debate regarding the deprecation of the Security Manager has not been a civil one. Since I took side against the deprecation, I've been publicly attacked, even to the point of plain bullying. Other voices that backed me up received similar treatment.

I don't expect reactions to this post to be any different. However, I have to tell community members what happened and what we lost.

Thanks to Peter Firmstone and Geertjan Wielenga for their help in reviewing this post.

To go further:

Originally published at A Java Geek on January 22th, 2022

Topics:

Related Articles

View All
  • JEP 411: What it Means for Java’s Security Model and Why You Should Apply the Principle of Least Privilege

    Java, like most platforms or languages has layers of security. This article intends to look at Java’s Authorization layer, which is unlike in other languages.

    We will also distinguish between two different ways this layer is typically utilized and why one is effective while the other isn’t.

    Furthermore, we investigate why JEP 411 only considers the least effective method and hopefully we will increase awareness of the Principle of Least Privilege as it is applied to Java Authorization, improve adoption and encourage people to take advantage of the improved security it provides.

    We hope to prolong its support and possibly even improve it in future.

    Read More
    Jun 03, 2021
  • The Principle of Least Privilege and How JEP 411 Will Have a Negative Impact on Java Security

    The SecurityManager and associated infrastructure are the foundations upon which to build secure software, but by themselves are insufficient for limiting users and Java software to the principles of least privilege.

    JEP 411 removes the SecurityManager and AccessController.

    In doing so, your library code will be able to run with the full permissions of its Java process, which is the same as running with none of the permission checks that were used to harden Java’s API.

    If an attacker breaks into your Java process via some other vulnerability, they will be able to load their own byte codes, and pretty much do whatever the process permissions permits them and possibly more if your system has other vulnerabilities.

    Read More
    May 22, 2021
  • Log4Shell / Leak4J

    Over the last couple of days (and nights) I’ve been studying the new (extremely dangerous) vulnerability in log4j2 called Log4Shell.

    Read More
    Dec 15, 2021

Author(s)

  • Nicolas Frankel

    Nicolas is a developer advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). ... Learn more

Comments (1)

Your email address will not be published.

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Вы используете ненадежный код

[…] Автор оригинала: Nicolas Frankel […]

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard