previously prevented exploit now possible with JDK 18
Rick Hillegas
rick.hillegas at gmail.com
Thu Nov 4 15:45:03 UTC 2021
Thanks for thinking about this issue, Bernd. The case I am thinking
about is an application which verifies the identity of the signer but
which lets the classloader verify the checksum.
On 11/3/21 5:16 PM, Bernd Eckenfels wrote:
> Rick if you fear an attacker can modify and install a JAR with a broken signature which you don’t trust anyway, what should stop the attacker to provide a valid but untrusted signature or no checksums at all? It might be a undesirable change for your case but I see no trustmodel where you would need default java classloader to do more than treating it unsigned.
>
> But if you really want to re-enable SHA1 you can do so (or implement a classloader which rejects any SHA1 codebases). Of course much easier would be to only install verified jars.
>
> BTW the fact that you actually get answers about a weakness report is amazing :)
>
> Gruss
> Bernd
> --
> http://bernd.eckenfels.net
> ________________________________
> Von: security-dev <security-dev-retn at openjdk.java.net> im Auftrag von Rick Hillegas <rick.hillegas at gmail.com>
> Gesendet: Wednesday, November 3, 2021 6:07:00 PM
> An: Sean Mullan <sean.mullan at oracle.com>; security-dev at openjdk.java.net <security-dev at openjdk.java.net>
> Betreff: Re: previously prevented exploit now possible with JDK 18
>
> Thanks for your detailed comments, Sean. We agree wholeheartedly that
> being signed and being trusted/valid are two separate concepts. The JDK
> wisely punts the issue of trust to the application developer. As I
> understand it, we disagree about the following points:
>
> 1) Is the signedness of a jar an immutable fact or is it just an
> opinion? I think that it is an immutable fact, measured by whether the
> jar's manifest contains certain stereotypical files. You maintain that
> it is an opinion which can shift incompatibly between JDK releases.
>
> 2) Should the JDK continue to raise an exception when loading classes
> from clearly compromised jars?
>
> 3) Are javadoc and a release note adequate defenses against an exploit
> which will surface when a mission-critical application upgrades to a JDK
> containing the work on https://bugs.openjdk.java.net/browse/JDK-8269039 ?
>
> Here's how I see it:
>
> a) A jar doesn't cease to be signed when its signer goes to jail and
> authorities revoke the signing certificate.
>
> b) A jar doesn't cease to be signed because someone pokes malware into
> it and breaks the hash.
>
> c) A jar doesn't cease to be signed because serious people distrust its
> old, shoddy cryptography.
>
> In all of these cases, trust has diminished. But the jar is still signed.
>
> Note that the emphatic deprecation of the SecurityManager affects your
> comments below.
>
> Thanks,
> -Rick
>
>
> On 11/2/21 11:34 AM, Sean Mullan wrote:
>> Hello Rick,
>>
>> It is behaving as expected. Let me explain in more detail.
>>
>> First, loading a signed JAR off the classpath only verifies the
>> signature and digests of the JAR file. It does not validate the signer's
>> certificate chain or determine if the signer is trusted. The JarFile API
>> class description includes this warning [1]:
>>
>> "Please note that the verification process does not include validating
>> the signer's certificate. A caller should inspect the return value of
>> JarEntry.getCodeSigners() to further determine if the signature can be
>> trusted."
>>
>> Some frameworks such as Web Start have this additional checking
>> built-in. Or, if you run your code with a Security Manager, then
>> additional steps will be performed at run-time to check that the code is
>> signed and that the signer's public key is trusted before granting
>> permissions to that code.
>>
>> If you don't perform these additional steps, then the JAR can be
>> modified without detection. For example, the signature related files
>> could be removed from the JAR (thus making it an unsigned JAR), or the
>> JAR could be modified and then re-signed with a different key. In either
>> of these cases, the JVM would load the JAR without any exception.
>>
>> It is also possible that a JAR signed with a weak or broken algorithm
>> (such as MD5 or SHA-1) could be modified without detection.
>>
>> This is why the JDK implementation supports several security
>> properties which are used to disable cryptographic algorithms and
>> protocols that are weak or broken. This provides out-of-the-box security
>> and is important to safeguard against crypto algorithms that inevitably
>> become weaker over time. One of these properties is
>> "jdk.jar.disabledAlgorithms". The specification of this property defines
>> the behavior if a signed JAR file is signed with an algorithm that is
>> disabled [2]:
>>
>> "JARs signed with any of the disabled algorithms or key sizes will be
>> treated as unsigned."
>>
>> The JDK determined after step 1 of the JAR verification process [3] that
>> the JAR was signed with SHA-1, and therefore stopped further processing.
>>
>> You may ask why we don't throw an Exception in this case. Although this
>> was considered, the compatibility risk was too high. These
>> restrictions are nearly always backported to earlier JDK update
>> releases. Throwing an Exception would be too high of a risk for
>> applications that happen to load signed JARs off the classpath but don't
>> otherwise behave any differently. Our primary focus is to protect
>> applications that verify that the code is signed by someone they trust.
>>
>> Consider updating and re-signing your signed jar with a stronger,
>> non-broken algorithm such as SHA-2. SHA-2 is the default digest
>> algorithm used by jarsigner when the -digestalg option is not specified.
>>
>> I hope this information is useful. I do think this is an area where our
>> javadocs and guides could be improved to provide more information about
>> how signed JARs are verified including more details on the behavior of
>> the JDK implementation with respect to disabled algorithms. We will be
>> working to try to improve the docs for JDK 18.
>>
>> --Sean
>>
>> [1]
>> https://download.java.net/java/early_access/jdk18/docs/api/java.base/java/util/jar/JarFile.html
>>
>> [2]
>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/conf/security/java.security#L667
>>
>> [3]
>> https://docs.oracle.com/en/java/javase/17/docs/specs/jar/jar.html#signed-jar-file
>>
>>
>>
>> On 10/28/21 3:14 PM, Rick Hillegas wrote:
>>> As a canary in the mineshaft, I built and tested Apache Derby with the
>>> recent build 18-ea+20-1248 of Open JDK 18. I tripped across the
>>> following issue when running Derby's regression tests. The problem is
>>> explained in more detail at
>>> https://issues.apache.org/jira/browse/DERBY-7126, where a simple repro
>>> (DERBY_7126_A) can be found. The problem is almost surely the result of
>>> work done on https://bugs.openjdk.java.net/browse/JDK-8269039 (Disable
>>> SHA-1 Signed JARs).
>>>
>>> Under previous versions of the JDK, the JVM would raise an error if you
>>> tried to load a class from a jar file which had been signed with SHA-1
>>> but later hacked by inserting malware via "jar -uf". This was the error:
>>>
>>> SHA1 digest error for $corruptedJarFileName
>>>
>>> However, under JDK 18 the hacked class loads, no error is raised, and
>>> the malware can now be executed. I was surprised that a previously
>>> prevented exploit now works. I think it would be better if the JVM still
>>> refused to load the hacked class even though SHA-1 has been deprecated.
>>>
>>> Thanks,
>>> -Rick
>>>
>
More information about the security-dev
mailing list