Vulnerabilities

Technical Advisory – OpenJDK – Weak Parsing Logic in java.net.InetAddress and Related Classes

Vendor: OpenJDK Project
Vendor URL: https://openjdk.java.net
Versions affected: 8-17+ (and likely earlier versions)
Systems Affected: All supported systems
Author: Jeff Dileo
Advisory URL / CVE Identifier: TBD
Risk: Low (implicit data validation bypass)

The private static
InetAddress::getAllByName(String,InetAddress) method is
used internally and by the public static
InetAddress::getAllByName(String) to resolve host or IP
strings to IP addresses. It is also used to implement the public static
InetAddress::getByName(String) and private static
InetAddress::getByName(String,InetAddress) methods. When
these methods are passed IP address strings, they will, per the Java
documentation, validate the format of the address.

However, the OpenJDK implementation of this method does not conform
to the documented API, and does not properly validate the format of a
given IP address string, allowing arbitrary characters within IPv6
address strings, including those representing IPv4 addresses. Due to
this, any uses of this method to validate host names to protect against
injection attacks may be bypassed.

  • src/java.base/share/classes/java/net/InetAddress.java
    • private static int checkNumericZone(String)
    • private static InetAddress[] getAllByName(String,InetAddress)
    • private static InetAddress getByName(String,InetAddress)
    • public static InetAddress getByName(String)
    • public static InetAddress[] getAllByName(String)
  • src/java.base/share/classes/sun/net/util/IPAddressUtil.java
    • public static byte[] textToNumericFormatV6(String)
    • public static byte[] convertFromIPv4MappedAddress(byte[])

An attacker may trivially bypass the use of
InetAddress::getAllByName to validate inputs.

Note: As input validation is not an
appropriate mechanism to protect against injection attacks — as opposed
to output encoding and Harvard architecture-style APIs — this issue is
itself considered to be of Low risk as code relying on the documented
validation for such purposes should be considered insecure regardless of
this issue.

The static InetAddress::getAllByName method, and the
static InetAddress::getByName method it underpins, are used
to resolve host strings to IP addresses in the form of
java.net.InetAddress objects, specifically the
Inet4Address and Inet6Address classes that
subclass InetAddress.

These methods accept strings of IP addresses, and, per the Java
documentation for the methods, are expected only to validate the format
of the address:

Given the name of a host, returns an array of its IP addresses based
on the configured name service on the system.

The host name can either be a machine name, such as
“www.example.com”, or a textual representation of its IP address. If a
literal IP address is supplied, only the validity of the address format
is checked.

For host specified in literal IPv6 address, either the form defined
in RFC 2732 or the literal IPv6 address format defined in RFC 2373 is
accepted. A literal IPv6 address may also be qualified by appending a
scoped zone identifier or scope_id.

However, the underlying implementation for these methods within
OpenJDK, the official reference implementation of Java, does not
properly implement its IP address parser, specifically its handling of
IPv6 scoped address zone identifiers.

Within the InetAddress class implementation, the
underlying parsing flow will attempt to parse for IP address strings,
and fall back to host name lookup. Within this IP address parsing logic,
it will first parse for IPv4 addresses, and then if that parse fails,
treat the string as a potential IPv6 address. However, to handle zone
identifiers, if the private InetAddress::getAllByName
observes a literal percent character (%) within the string,
it will pass the string to the private
InetAddress::checkNumericZone static method.

addr = IPAddressUtil.textToNumericFormatV4(host);
if (addr == null) {
// This is supposed to be an IPv6 literal
// Check if a numeric or string zone id is present
int pos;
if ((pos=host.indexOf (‘%’)) != –1) {
numericZone = checkNumericZone (host);
if (numericZone == –1) { /* remainder of string must be an ifname */
ifname = host.substring (pos+1);
}
}

src/java.base/share/classes/java/net/InetAddress.java

This method incorrectly assumes that a ] character
represents the end of the address string, but does not verify that this
is the case, only checking to ensure that the ] character
does not appear immediately after the %.

for (int i=percent+1; i 2 && host.charAt(host.length()-1) == ‘]’) {
host = host.substring(1, host.length() –1);

src/java.base/share/classes/java/net/InetAddress.java

Following the call to InetAddress::checkNumericZone, the
IPAddressUtil::textToNumericFormatV6 static method is used
to actually parse the IPv6 address string into a byte array
representation. This method specifically ignores zone identifiers by
effectively truncating the content it parses to the last character
before the first % if one exists.

char[] srcb = src.toCharArray();
byte[] dst = new byte[INADDR16SZ];

int srcb_length = srcb.length;
int pc = src.indexOf (‘%’);
if (pc == srcb_length 1) {
return null;
}

if (pc != –1) {
srcb_length = pc;
}

src/java.base/share/classes/sun/net/util/IPAddressUtil.java

As a result of each of these components of the IPv6 address parsing
logic truncating and/or ignoring data beyond certain metacharacters,
InetAddress::getAllByName will accept invalid IPv6 address
strings such as the following:

  • ::1%1]foo.bar baz'”
  • [::1%1]foo.bar baz'”]
  • 2606:4700:4700::1111%1]foo.bar baz'”
  • [2606:4700:4700::1111%1]foo.bar baz'”]

This additionally applies to IPv4-compatible IPv6 addresses, such as
the following:

  • ::1.1.1.1%1]foo.bar baz ‘”
  • [::1.1.1.1%1]foo.bar baz ‘”]
  • ::0101:0101%1]foo.bar baz ‘”
  • [::0101:0101%1]foo.bar baz ‘”]

Furthermore, a separate issue exists in the handling of IPv4-mapped
IPv6 addresses, as, unlike IPv4-compatible IPv6 addresses, which are
parsed into Inet6Address objects, the IPv4-mapped addresses
are returned as Inet4Address objects with no concept of an
IPv6 scope. This occurs between a special case handled by the static
IPAddressUtil::textToNumericFormatV6 method:

if (j != INADDR16SZ)
return null;
byte[] newdst = convertFromIPv4MappedAddress(dst);
if (newdst != null) {
return newdst;
} else {
return dst;
}

src/java.base/share/classes/sun/net/util/IPAddressUtil.java

The static IPAddressUtil::convertFromIPv4MappedAddress
method will return a byte array of size 4 (INADDR4SZ)
containing the IPv4 address bytes from the byte array representation of
the address string, should it match the structure of an IPv4-mapped IPv6
address:

public static byte[] convertFromIPv4MappedAddress(byte[] addr) {
if (isIPv4MappedAddress(addr)) {
byte[] newAddr = new byte[INADDR4SZ];
System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
return newAddr;
}
return null;
}

src/java.base/share/classes/sun/net/util/IPAddressUtil.java

When such a byte array is returned back to the private
InetAddress::getAllByName static method, it will then be
used to return an Inet4Address.

InetAddress[] ret = new InetAddress[1];
if(addr != null) {
if (addr.length == Inet4Address.INADDRSZ) {
ret[0] = new Inet4Address(null, addr);
} else {
if (ifname != null) {
ret[0] = new Inet6Address(null, addr, ifname);
} else {
ret[0] = new Inet6Address(null, addr, numericZone);
}
}
return ret;
}

src/java.base/share/classes/java/net/InetAddress.java

Due to this, any arbitrary scope value can be provided, as the
ifname variable would only be validated in the
Inet6Address(String,byte[],String) constructor, regardless
of if it being set due to InetAddress::checkNumericZone
rejecting the address string. As a result,
InetAddress::getAllByName will additionally accept invalid
IPv4-mapped IPv6 address strings such as the following:

  • ::ffff:1.1.1.1%1]foo.bar baz'”
  • [::ffff:1.1.1.1%1]foo.bar baz'”]
  • ::ffff:0101:0101%1]foo.bar baz'”
  • [::ffff:0101:0101%1]foo.bar baz'”]
  • ::ffff:1.1.1.1%1foo.bar baz'”
  • [::ffff:1.1.1.1%1foo.bar baz'”]
  • ::ffff:0101:0101%1foo.bar baz'”
  • [::ffff:0101:0101%1foo.bar baz'”]

Modify the InetAddress::checkNumericZone static method
to remove the iteration check for ] characters as it should
never be passed a string containing [ or ]
characters. This will force all characters after the % to
be parsed as a non-negative base 10 integer, or rejected.

Additionally, modify the private
InetAddress::getAllByName static method to handle length 4
byte arrays returned by
IPAddressUtil::textToNumericFormatV4 and
IPAddressUtil::textToNumericFormatV6 differently, such that
those returned by the latter do not contain any %
characters.

Additionally, or alternatively to the above remediations, consider
reimplementing the entire public
InetAddress::{getAllByName,getByName} interface along the
lines of the Android implementation, which parses IP addresses extremely
strictly, and allows interface name IPv6 scoped zone identifiers only
for link-local addresses. It
is worth noting that the Android implementation additionally validates
interface name IPv6 scoped zone identifiers against the system network
interfaces, such a construction is, while not
invalid per the InetAddress and Inet6Address
Java documentation, arguably not in the spirit of them either as these
APIs are intended for general-purpose IP address operations, including
address representations that do not necessarily refer to the interfaces
of the host operating on them. Instead, consider introducing an
additional API for the InetAddress class whereby a
getAllByName or getByName operation is
performed with such additional, host-specific validation.

Ensure that hostname and IP address values are handled securely and
output-encoded or sanitized in a context appropriate manner. Do not rely
on methods such as InetAddress::getByName(String) or
InetAddress::getAllByName(String) to validate or sanitize
external inputs.

An example demonstrating vulnerable code
relying on InetAddress::getByName(String) is included for
reference:

Note: When run, an injection will occur in the
ping(String) function, resulting in a file,
/tmp/id2, being created with the output of the
id program on Unix-based systems.

import java.net.InetAddress;

class Ping {
public static boolean validateHost(String host) {
try {
InetAddress address = InetAddress.getByName(host);
} catch (Throwable t) {
return false;
}
return true;
}

public static int ping(String host) {
try {
Process p = new ProcessBuilder(
“/bin/sh”, “-c”, “ping -c 1 ‘” + host + “‘”
).start();
p.waitFor();
return p.exitValue();
} catch (Throwable t) {
t.printStackTrace();
return 1;
}
}

public static void test(String[] hosts) {
for (String host : hosts) {
System.out.println(” testing `” + host + “`:”);
boolean valid = validateHost(host);
System.out.println(” valid?: “ + valid);
if (valid) {
int retcode = ping(host);
boolean reachable = 0 == retcode;
System.out.println(
” reachable?: “ + reachable + ” (“ + retcode + “)”
);
}
}
}

public static void main(String[] argv) throws Throwable {
String[] good_inputs = new String[]{
“127.0.0.1”, “wikipedia.org”
};
String[] bad_inputs = new String[]{
“https://wikipedia.org”, “127.0.0.1; id>/tmp/id”
};
String[] evil_inputs = new String[]{
“::1%1]foo.bar baz’; id>/tmp/id2; exit ’42”
};
System.out.println(“testing good inputs: (these should work)”);
test(good_inputs);
System.out.println(“testing bad inputs: (these should not work)”);
test(bad_inputs);
System.out.println(“testing evil inputs: (these work, but shouldn’t)”);
test(evil_inputs);
}
}

$ java Ping
testing good inputs: (these should work)
testing `127.0.0.1`:
valid?: true
reachable?: true (0)
testing `wikipedia.org`:
valid?: true
reachable?: true (0)
testing bad inputs: (these should not work)
testing `https://wikipedia.org`:
valid?: false
testing `127.0.0.1; id>/tmp/id`:
valid?: false
testing evil inputs: (these work, but shouldn’t)
testing `::1%1]foo.bar baz’; id>/tmp/id2; exit ’42`:
valid?: true
reachable?: false (42)

2/17/22: NCC Group disclosed vulnerability to the security email of the OpenJDK
project, , using their PGP key.
2/17/22: NCC Group receives a reply from Oracle’s Security Alerts team
() indicating that they have received the
disclosure and will get back to NCC Group on it.
2/18/22: The Oracle Security Alerts team emails NCC Group asking about
NCC Group’s 30 day disclosure policy and notes that they release
“Critical Patch Updates 4 times in a year,” and requests an extension
to after the upcoming one on April 19, 2022 (i.e. the July 2022
update).
2/19/22: NCC Group replies, indicating a willingness to wait until April 19th.
2/22/22: The Oracle Security Alerts team replies, thanking NCC Group for the
extension.
2/24/22: NCC Group receives an automated status report email from the
issue tracker, with the description
“Weak Parsing Logic in java.net.InetAddress and Related Classes” and a
status of “Issue addressed in future release, backports in progress
for supported releases, scheduled for a future CPU”
3/3/22: The Oracle Security Alerts team replies indicating that they consider
the vulnerability to be “Security-in-Depth issue”, and additionally
that “the CVSS score for this issue is zero.” They state that it will
be addressed in a future update and then that because they are locking
down changes for the April update, they request an extension to
“postpone the fix to July CPU, to allow more time for testing.”
3/24/22: NCC Group receives an automated issue tracker update email from
with a status of “Issue addressed in future
release, backports in progress for supported releases, scheduled for a
future CPU”.
4/24/22: NCC Group receives an automated issue tracker update email from
with a status of “Issue addressed in future
release, backports in progress for supported releases, scheduled for a
future CPU”.
5/24/22: NCC Group receives an automated issue tracker update email from
with a status of “Issue addressed in future
release, backports in progress for supported releases, scheduled for a
future CPU”.
6/24/22: NCC Group receives an automated issue tracker update email from
with a status of “Issue addressed in future
release, backports in progress for supported releases, scheduled for a
future CPU”.
7/24/22: NCC Group receives an automated issue tracker update email from
with a status of “Closed: Alert or CPU issued”
and an additional note of “Addressed in: Pipeline for CPU”.
8/11/22: NCC Group reviews the July 2022 CPU update
(https://www.oracle.com/security-alerts/cpujul2022.html) and does not
find any mention of the disclosed vulnerability. In further reviewing
associated updates for Java 8 (8u341), 11 (11.0.16), 17 (17.0.4), and
18 (18.0.2), NCC Group identifies a change named “Update
java.net.InetAddress to Detect Ambiguous IPv4 Address Literals” within
the “Other Notes” sections, which refer to a non-public issue,
“JDK-8277608” (https://bugs.openjdk.org/browse/JDK-8277608). NCC Group
identifies and reviews the commit introducing the change to the public
https://github.com/openjdk/jdk repository,
`cdc1582d1d7629c2077f6cd19786d23323111018`, and determines that the
vulnerability has not been fixed and that the commit appears
unrelated, simply introducing a non-security relevant breaking change
that disables alternate numerical textual representations of IP
addresses, such as hexadecimal and octal radixes referred to as
“BSD-style”. This change causes IP address strings such as
“0x7f.016.0.0xa” (127.14.0.10), “0x7f000001” (127.0.0.1), or
“017700000001” (127.0.0.1) to be rejected by default unless the
`-Djdk.net.allowAmbiguousIPAddressLiterals=true` option is passed to
`java`. It should be noted that this validation does not restrict
purely numeric text representations such as “2130706433” or
“02130706433” (both parsed to 127.0.0.1). Single segment octal
representations are restricted when they cannot be parsed into valid
addresses as decimal. This is due to Java’s longtime improper handling
of octal-based IP addresses, which requires at least one segment to be
larger than the maximum value when parsed as decimal to trigger an
octal parse. Due to this, octal-based IP addressed are often parsed
as decimal by Java.
8/12/22: NCC Group emails both the and
lists asking for the current timeline for
the resolution of the issue, and provides the internal issue tracker
ID. In the email, NCC Group includes a brief analysis of the “Update
java.net.InetAddress to Detect Ambiguous IPv4 Address Literals”
change, stating that it does not appear to be related to the disclosed
vulnerability, which is still active in the updated releases of Java.
Lastly, NCC Group states their intention to publish an advisory with
with guidance for developers instead of waiting for a later CPU to
resolve the vulnerability as the Oracle Security Alerts team had rated
it with a CVSS score of 0.
9/14/22: The Oracle Security Alerts team replies to the previous email
informing NCC Group and the list that
they “revisited the original report and learned that the issue
reported was not addressed by the fixes released in the July CPU.”
They also stated that it was “too late to the fix into the upcoming
2022 October CPU”, and that they were “targeting the fix for the 2023
January CPU.” They additionally sought to determine if NCC Group would
delay disclosure until after the January CPU was published.
9/22/22: NCC Group replies to Oracle Security Alerts team and the
list that waiting another 4-5 months far
exceeds our disclosure policy. NCC Group also states their intention
to publish an advisory on Sept 26, 2022, so that developers can
mitigate the vulnerability within their codebases without an upstream
patch.
9/23/22: The Oracle Security Alerts team replies on the thread thanking
NCC Group for informing them of the decision to publish an advisory.
9/23/22: NCC Group receives an automated issue tracker update email from
with a status of “Under investigation / Being
addressed in future and supported releases”.
9/23/22: Late in the day, the Oracle Security Alerts team replies to
NCC Group’s most recent email, requesting additional time until
noon PT on Wednesday, Sept 28, 2022, so that they can “work on a plan
to get the fix into Oct CPU”.
9/26/22: Early in the morning, NCC Group North America CTO, Dave Goldsmith,
replies, stating that NCC Group tries “our best to work positively
with vendors when disclosing vulnerabilities,” and “that we’ve been
pretty flexible” in handling the disclosure for this vulnerability.
He offers an extension until Wednesday, Sept 28, 2022, at noon PT,
but requests that the Oracle Security Alerts team re-evaluates the
0.0 CVSS score of the vulnerability, as, if that remains Oracle’s
calculation, “then we don’t think it will be contentious to publish
without a patch.”
9/28/22: At 10:40am PT, the Oracle Security Alerts team replies stating that
they “have confirmed the issue based on the report that was
submitted” and that “the issue is a client side issue and there are
ample best practices on input validation.” They include reference
links to an Oracle secure coding guide for Java
(https://www.oracle.com/java/technologies/javase/seccodeguide.html),
and the OWASP Top 10 entry on injection vulnerabilities
(https://owasp.org/Top10/A03_2021-Injection/), the latter of which
states the following as its second bullet on prevention: “Use positive
server-side input validation. This is not a complete defense”.
Additionally, the Oracle Security Alerts requested a proof-of-concept
demonstrating a server-side attack to recalculate the CVSS score.
However, the reply did not contain any mention of “a plan to to get
the fix into Oct CPU” per the Oracle Security Alerts team’s 9/23/22
email. It should be noted that NCC Group considers this vulnerability
to impact code that takes untrusted hostname strings as input, a group
that primarily includes server-side applications and services, as it
enables a trivial bypass to official Java input validation routines
used to protect against injection-type issues.
10/4/22: NCC Group North America CTO, Dave Goldsmith, replies, providing
examples of how server-side input validation based on the vulnerable
API would result in server-side systems being exploitable, including
an example of such vulnerable code implementing input validation for
`ping`. He requests a clear answer from the Oracle Security Alerts on
both re-evaluating the CVSS score given the provided examples, and a
commitment to fix the vulnerability in the October CPU. He
additionally states that if both are provided by close of business on
Wednesday, October 5, 2022, NCC Group will hold off on publishing the
advisory until the October CPU is published; otherwise, the advisory
will be published on Thursday, October 6, 2022.
10/6/22: The Oracle Security Alerts team replies at 12:10am PT, stating that
their “evaluation is that this is an input validation issue and we are
scoring it as a CVSS 0” and that “[a]s mentioned earlier we are
targeting to release defense-in-depth fixes in the January 2022
Critical Patch Update.”
10/6/22: NCC Group publishes this security advisory.
10/6/22: NCC Group replies on the thread informing the Oracle Security Alerts
team and the list that the advisory has
been published.

Jennifer Fernick and Dave Goldsmith for their support throughout the
disclosure process.

NCC Group is a global expert in cyber security and risk mitigation,
working with businesses to protect their brand, value and reputation
against the ever-evolving threat landscape.

With our knowledge, experience and global footprint, we are best
placed to help businesses identify, assess, mitigate and respond to the
risks they face.

We are passionate about making the Internet safer and revolutionizing
the way in which organizations think about cybersecurity.

Like this:

Like Loading…

Source

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button