Technical Advisory – OpenJDK – Weak Parsing Logic in java.net.InetAddress and Related Classes | xxxTechnical Advisory – OpenJDK – Weak Parsing Logic in java.net.InetAddress and Related Classes – xxx
菜单

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

十月 6, 2022 - nccgroup
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 <jeff.dileo[at]nccgroup[dot]com> Advisory URL / CVE Identifier: TBD Risk: Low (implicit data validation bypass) 

Summary

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.

Location

Impact

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.

Details

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 address1:

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<slen; i++) {     char c = s.charAt(i);     if (c == ']') {         if (i == percent+1) {             /* empty per-cent field */             return -1;         }         break;     }     ... 

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

This is an issue as no such validation occurs earlier within the private InetAddress::getAllByName. Instead, it uses only a simple check that the first and last characters are [ and ], respectively, the format for using literal IPv6 addresses within URLs, in order to remove them.

if (host.charAt(0) == '[') {     // This is supposed to be an IPv6 literal     if (host.length() > 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:

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

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:

Technical Recommendation

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.234567 It is worth noting that the Android implementation additionally validates interface name IPv6 scoped zone identifiers against the system network interfaces,8 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.

Developer Recommendation

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) 

Vendor Communication

2/17/22: NCC Group disclosed vulnerability to the security email of the OpenJDK          project, vuln-report@openjdk.java.net, using their PGP key. 2/17/22: NCC Group receives a reply from Oracle's Security Alerts team          (secalert_us@oracle.com) 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          secalert_us@oracle.com 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          secalert_us@oracle.com 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          secalert_us@oracle.com 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          secalert_us@oracle.com 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          secalert_us@oracle.com 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          secalert_us@oracle.com 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 secalert_us@oracle.com and          vuln-report@openjdk.java.net 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 vuln-report@openjdk.java.net 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          vuln-report@openjdk.java.net 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          secalert_us@oracle.com 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 vuln-report@openjdk.java.net list that the advisory has          been published. 

Thanks to

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

About NCC Group

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.


  1. https://github.com/openjdk/jdk/blob/jdk-17+35/src/java.base/share/classes/java/net/InetAddress.java#L1261↩︎

  2. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:libcore/ojluni/src/main/java/java/net/InetAddress.java;l=1148↩︎

  3. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java;l=90↩︎

  4. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java;l=113;bpv=1;bpt=1↩︎

  5. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:bionic/libc/dns/net/getaddrinfo.c;l=586↩︎

  6. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:bionic/libc/dns/net/getaddrinfo.c;l=1015↩︎

  7. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:bionic/libc/dns/net/getaddrinfo.c;l=1245↩︎

  8. https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:bionic/libc/bionic/net_if.cpp;drc=android-12.0.0_r31;l=56↩︎

Share this:

Like this:

Like Loading…


Notice: Undefined variable: canUpdate in /var/www/html/wordpress/wp-content/plugins/wp-autopost-pro/wp-autopost-function.php on line 51