Exploitation of the Psychic Signatures CVE-2022-21449
Vulnerability “Psychic Signatures†CVE-2022-21449 affects Oracle Java SE: 17.0.2 and 18; Oracle GraalVM Enterprise Edition: 21.3.1 and 22.0.0.2 and allows to bypass ECDSA-signature verification. Detailed description of issue available at the great blog post of Neil Madden. Unfortunately initial prof of concept do not cover all potentially vulnerable libraries. At this article I will demonstrate the sample vulnerable application that use JWT tokens signed with ES256 algorithm and the way how it can be exploited. Malicious user can bypass authentication process and get access to the web server on behalf of any user. Before we jump to the real life exploit lets talk about basics.
Background: ECDSA signatures
I don't want to copy paste Wikipedia, in a few words an ECDSA signature consists of two values, called?r?and?s. To?verify an ECDSA signature, the verifier checks an equation involving?r,?s, the signer’s public key, and a hash of the message. If the two sides of the equation are equal then the signature is valid, otherwise it is rejected.?One side of the equation is?r?and the other side is multiplied by?r?and a value derived from?s. If both r and s are equal zero it would obviously be a really bad thing. Initial proof of concept demonstrates exploit only for InP1363Format. Signatures in ASN.1 DER format sere said can be exploited but without evidence.
Distinguished Encoding Rules (DER)
DER is standard for encoding data into a binary format. The standard specifies how to "convert" those definitions of collections of data into actual "bytes on the wire". For this purposes a "tag-length-value" (TLV) construction was introduced. TLV is a byte array that has a tag indicating what the data is, a length specifying its length (in bytes/octets), and the value itself. Following tags are required to describe digital signature: SEQUENCE (0x30) and INTEGER (0x02). Let's take a look on following example: [0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00] - sequence starts with byte 0x30 and have length of six bytes and value is two integers each of them are equal zero.
A large?prime N
There are several curves over prime fields recommended by the National Institute of Standards and Technology (NIST). Full list can be found at the article. Pseudo-random?curves are Curve P-192, Curve P-224, Curve P-256, Curve P-384, Curve P-521. We are interested at Curve P-256. Its large prime number N is
115792089210356248762697446949407573529996955224135760342422259061068512044369
In elliptic curve cryptography that arithmetic is carried out modulo?n?is also why you need to check that?r?and?s?are both not equal a large prime number N.
Vulnerability exploitation
All known exploits for "Psychic Signatures" vulnerability has DER signature format. For example signature algorithm implementation at JSON Web Token for Java and Android library can be found at Github source code page. Zero r and s JWT token signature at Base64 encoded DER format is MAYCAQACAQA what means:
0:d=0? hl=2 l= ? 6 cons: SEQUENCE
2:d=1? hl=2 l= ? 1 prim: INTEGER: 0
5:d=1? hl=2 l= ? 1 prim: INTEGER: 0 ? ? ?
More details about exploit can be found at OWASP?page and at sample application from DataDog. This exploit have one disadvantage it works only for one JWT library and can not be used with others. Let's take a look at Google IAP, authentication is based on JWT-tokens with ES256 signature, keys Key ID are available publicly.
Library com.google.auth.oauth2
Class com.google.auth.oauth2.TokenVerifier is used for digital signature verification. Known exploit MAYCAQACAQA will fail with exception: signature.length == 64 DerEncoder.encode. Google oauth2 library expects that r and s will be simply concatenated, all required decoding is done later after length checks. To seculessfully exploit vulnerability we need two byte arrays initialised with zero. Base64 encoded signature:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
This exploit has its own limitation — 0 used for r and s. To better understand why zero is not great enough for exploit let's check following library com.nimbusds.
领英推è
Library com.nimbusds
Library com.nimbusds.jose implement own security mechanisms to protect against such attacks, in our exploit we will work with version before 9.22.?Nimbusds (same as Google's library) expects that r and s will be simply concatenated, and we can use our previous exploit:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?
Unfortunately application will throw unexpected exception om.nimbusds.jose.JOSEException: Index 64 out of bounds for length 64. Mistake at transcodeSignatureToDER function protect library from such exploits, but there is another way to encode zero for Nimbusds library. Is it secure by mistake? No, another mistake at decoding algorithm allows us to decode negative number -1 to the zero for a both r and s. For example signature DER format—? [0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00] will bypass security checks. This issues was fixed at version 9.4.2.?There is another way to exploit vulnerability. Let's remember that a large prime number N is equal 0 modulo N. Our exploit can be improved by changing r and s from zero to
115792089210356248762697446949407573529996955224135760342422259061068512044369
Following code will generate our signature:
ar keys = KeyPairGenerator.getInstance("EC").generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) keys.getPrivate();
var genPrimeN = privateKey.getParams().getOrder();
var doublePrimeSignature = new byte[64];
var xPart = genPrimeN.toByteArray();
var yPart = genPrimeN.toByteArray();
System.arraycopy(xPart, 1, doublePrimeSignature, 0, 32);
System.arraycopy(yPart, 1, doublePrimeSignature, 32, 32);
_____wAAAAD__________7zm-q2nF56E87nKwvxjJVH_____AAAAAP__________vOb6racXnoTzucrC_GMlUQ
Other libraries
Libraries com.auth0 java-jwt, org.bitbucket.b_c jose4j will fail with the same exception: Index 64 out of bounds for length 64 for a signature
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA??
Libraries java-jwt and jose4j implement the same algorithm. Protection methods we introduced at jose4j after the April 2022, exploit with negative number will work till the 0.7.11 version. Libraries io.jsonwebtoken and jjwt-root implements another signature decoding algorithm and for exploitation DER encoding: MAYCAQACAQA= should be used.
Fix
Library com.google.auth.oauth2 didn't introduce additional protection mechanisms, to avoid such issues at future. Fix for library com.nimbusds.jose available after 9.22 version, com.auth0 java-jwt was fixed at 3.19.2, and org.bitbucket.b_c jose4j at 0.7.12