Java’s Weak Cybersecurity Track Record Continues … This Time It’s A Breach of Trust

Java has always had a bit of troubled security track record, and its framework has often been one of the top targets for malware and…

Java’s Weak Cybersecurity Track Record Continues … This Time It’s A Breach of Trust

Java has always had a bit of a troubled security track record, and its framework has often been one of the top targets for malware and attackers. And a new one is as bad as it gets for trustworthiness. In fact, in the April 2022 Java Critical Patch Update (CPU), there were over 500 patches defined.

With the latest bug, Neil Madden reported a new vulnerability in the signing of ECDSA signatures, and where an adversary can bypass ECDSA signatures within Java 15, 16, 17 and 18 [here]:

The vulnerability has been announced as CVE-2022–21449 , and Oracle has been patching a range of Java versions. Unfortunately, Java updating is not the easiest to implement — especially on embedded systems — and it could mean that many systems go unpatched. The affected application areas include TLS tunnel, FIFO/Web Authentication and JWT (Java Web Tokens).

With ECDSA, we pass values of r and s, and where we check this with the hash of the message and a public key value. Neil found when these values are zero, the signature will be verified.

The following is an outline of some Python code to implement ECDSA creation and verification [here]:

import sys
import random
import hashlib
import libnum
from secp256k1 import curve,scalar_mult,point_add
msg="Hello"
if (len(sys.argv)>1):
msg=(sys.argv[1])
# Alice's key pair (dA,QA)
dA = random.randint(0, curve.n-1)
QA = scalar_mult(dA,curve.g)
h=int(hashlib.sha256(msg.encode()).hexdigest(),16)
k = random.randint(0, curve.n-1)
rpoint = scalar_mult(k,curve.g)
r = rpoint[0] % curve.n
# Bob takes m and (r,s) and checks
inv_k = libnum.invmod(k,curve.n)
s = (inv_k*(h+r*dA)) % curve.n
print (f"Msg: {msg}\n\nAlice's private key={dA}\nAlice's public key={QA}\nk= {k}\n\nr={r}\ns={s}")
# To check signature
inv_s = libnum.invmod(s,curve.n)
c = inv_s
u1=(h*c) % curve.n
u2=(r*c) % curve.n
P = point_add(scalar_mult(u1,curve.g), scalar_mult(u2,QA))
res = P[0] % curve.n
print (f"\nResult r={res}")
if (res==r):
print("Signature matches!")

And a sample run [here]:

Msg: Hello123
Alice's private key=59415685509516291732623466804953470944903908623780094388038002011461202208070
Alice's public key=(24554141300969450064163263426844888628802498988426591124072119496449289448505, 114359388960404339679375420973260643085090792950846610450698101016317921839644)
k= 626370194392365919645426225410460629018059338503019940991523460332833854777
r=49777266633806946836633548434487737353728205925599644723795061274162419287834
s=79380768682391122524878065479189865213298432507682482439352705536832774411973
Result r=49777266633806946836633548434487737353728205925599644723795061274162419287834
Signature matches!

With this, we take the value of s, and do an inverse mod on it, to get s^{-1} (mod n):

inv_s = libnum.invmod(s,curve.n)

This should fail when s=0, but Java implemented it in a non-optimal way, and then allowed the code to continue, and eventually produce a positive verification. For this, Java used the following to calculate the inverse mod of:

x^{-1} = x^(p-2) (mod p)

As we can see, this works well:

>>> p=17
>>> x=6
>>> print(pow(x,-1,p))
3
>>> print(pow(x,p-2,p))
3

But it only works when x is not equal to zero. If we do this, we get:

>>> x=0
>>> print(pow(x,p-2,p))
0

If we use the libnum library, we see that it gives an exception:

>>> x=6
>>> print(libnum.invmod(x,p))
3
>>> x=0
>>> print(libnum.invmod(s,p))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/anaconda3/lib/python3.8/site-packages/libnum/modular.py", line 34, in invmod
raise ValueError("no invmod for given @a and @n")
ValueError: no invmod for given @a and @n

This should have fail, but Java just did a simple implementation.

If you want to know more about ECDSA, try here:

https://asecuritysite.com/ecdsa/