The exploit works by spraying an IIS server via several large GET HTTP requests, and finishes with a malformed HTTP request. A normal HTTP request has a structure like the following:
GET /some/path HTTP/1.1
After this phrase there is a newline (a “\n” or “\x0a”). On the other hand, the malformed HTTP request in this exploit is missing the “HTTP/1.1” protocol token at the end. We can use Zeek® to detect this anomaly. When the malformed request without the HTTP version occurs, Zeek will log it as a “weird” event of type “HTTP_version_mismatch”. We can detect the version mismatch within Zeek via the log_weird event. At the same time, we do not want to flag legitimate connections with version issues as exploit attempts, so we will only look at connections that include a series of previous requests that repeatedly include headers of a size ≥ 1,750 bytes in size (used to set up the spray), a value derived from empirical analysis of payload sizes, and fuzzed to not give away exploit particulars. We can calculate and log that information in the http_message_done event prior to the log_weird event firing.
Once we see the weird event on a connection with a series of such large requests, we can retrieve the current packet with get_current_packet. Its data field will contain the packet’s TCP payload, against which we can check for the following exploit regular expression:
global malpattern: pattern = /(GET|HEAD|PATCH|POST|PUT) [^\x0a\x20]+\x0a/;
This pattern looks for an HTTP request where the “HTTP/1.1” token is missing before the newline character (\x0a). If we detect this regular expression, the connection likely contained a CVE-2022-21907 exploit, and we generate a new Zeek notice:
$ zeek -Cr your.pcap packages
$ cat notice.log
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions email_dest suppress_for remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
#types time string addr port addr port string string string enum enum string string addr addr port count string set[enum] set[string] interval string string string double double
1641934050.661549 C3zB9u3LtTMmn7XGab 192.168.88.1 55193 192.168.88.149 80 - - - tcp CVE_2022_21907::CVE_2022_21907_EXPLOIT_ATTEMPT Possible CVE_2022_21907 exploit over HTTP, multiple sprays followed by the triggering malformed request get_current_packet data=\x00\x0c)\x9a\x86\xd9\xa6\x83\xe7\xba\xc9g\x08\x00E\x00\x00\xd4\x00\x00@\x00@\x06\x00\x00\xc0
Our regular expression hit on the bolded portion of our log, above. The “HTTP/1.1” you see in the log is part of a prior request and not the one that triggered this notice. You will notice the matched URL finishes without the “HTTP/1.1” token.
(Note: what we’ve sketched above is not an air-tight detection. We’re aware of some potential evasions that attackers might be able to employ, but hold off on discussing those here so as not to help attackers evade our detector.)
Zeek users can install this logic via zkg with the following command: