skip to content
Back to GitHub.com
Home Bounties CodeQL Research Advisories Get Involved Events
August 11, 2020

Fuzzing sockets, part 2: FreeRDP

Antonio Morales

In the first part of this series I showed how, using AFL++, I found multiple vulnerabilities in three open-source File Transfer Protocol (FTP) servers: Pure-FTPd, Bftpd, and ProFTPd. I also detailed the code changes required to perform the fuzzing as well as three of the most interesting vulnerabilities that resulted from this work.

In this second installment, I’ll delve into the research conducted on FreeRDP. FreeRDP is the widely used open source implementation of the Remote Desktop Protocol (RDP). Similar to the last post, we will document the entire fuzzing process from start to finish!

Let’s get fuzzing!

Putting ourselves in context

In April 2020, the GitHub Security Lab had an internal three-day hackathon with the goals of finding and fixing as many vulnerabilities as possible in Apache Guacamole. For those who aren’t familiar, Apache Guacamole is a browser application that allows you to access remote machines through standard protocols like VNC, RDP, and SSH. It’s become increasingly popular especially in the current work-from-home environment.

Although the hackathon only lasted three days, it planted the seeds for this follow-up fuzzing effort. During the hackathon, each team member was focused on a different part of the code. In my case, I chose to focus on the Remote Desktop Protocol (RDP) protocol.

If there is still someone who haven’t heard of RDP, it is a protocol that allows a user to remotely manage a Windows machine using a graphical interface as if they were actually sitting in front of the desktop.

Fig. 1 – FreeRDP example (Source: www.kali.org)

Results

As a result of this research, I reported the following twelve bugs in FreeRDP:

CVE GHSL Description PoC
CVE-2020-13396 GHSL-2020-100 OOB Read in ntlm_read_ChallengeMessage PoC
CVE-2020-13397 GHSL-2020-101 NULL dereference in FreeRDP FIPS routines PoC
CVE-2020-13398 GHSL-2020-102 Heap overflow in FreeRDP crypto_rsa_common PoC
CVE-2020-11099 GHSL-2020-103 OOB read vulnerability in license_read_new_or_upgrade_license_packet PoC
CVE-2020-11097 GHSL-2020-104 OOB read vulnerability in FreeRDP ntlm_av_pair_get PoC
CVE-2020-11098 GHSL-2020-105 OOB read vulnerability in FreeRDP glyph_cache_put PoC
CVE-2020-4030 GHSL-2020-106 Integer signedness mismatch leading to OOB read in FreeRDP  
CVE-2020-11096 GHSL-2020-107 OOB read vulnerability in FreeRDP update_read_cache_bitmap_v3_order PoC
CVE-2020-11095 GHSL-2020-124 OOB read vulnerability in FreeRDP update_recv_primary_order PoC
CVE-2020-4032 GHSL-2020-125 Integer signedness mismatch vulnerability in FreeRDP leads to OOB read PoC
CVE-2020-4033 GHSL-2020-128 OOB read vulnerability in FreeRDP RLEDECOMPRESS PoC
CVE-2020-4031 GHSL-2020-129 Use-After-Free in gdi_SelectObject PoC

Creating a “custom” input case

Before we start fuzzing, we need to have (at least) one representative input case. By representative I mean a serialized RDP packet trace that reaches a high rate of code coverage.

While the quickest option is to reuse previously captured packet data (e.g. from Wireshark), in this case I opted to create my own input case from scratch. And when I say from scratch, I literally mean from scratch using just GDB and a hex editor.

Fig. 2 – Become your own RDP library with a hex editor!

Although this method is obviously slower and more painstaking, it has the advantage that it forces us to become intimately familiar with our target code base. Since fuzzing network targets generally requires some degree of code modification to be effective, the knowledge we acquire when handcrafting our RDP packets will make subsequent code modifications much easier for us.

Another important advantage of hand crafting our input case packets is that it differentiates us from the standard layouts and structures implemented by official clients. By building our own input case from scratch we can more easily build packet structures that are outside of the norm and may yield better fuzzing results.

Input case: RDP/TPKT.bin

PDU

Next, I will go through some of the most important structures in the RDP protocol.

First, we should view an RDP connection as a sequence of Protocol Data Units (PDUs). These PDUs can be of two types:

Fig. 3 – TPKT Header structure

In short, Slow-Path packet always starts with a TPKT header, which starts with byte 0x03, while Fast-Path packets zero out the first two least significant bits of the first byte.

Cases where NLA mode is enabled must also be taken into account. Network Level Authentication (NLA) is a feature of RDP that requires the connecting user to authenticate themselves before a session is established with the server. In these cases, we must also know the TSRequest structure. Its structure is as follows:

Fig. 4 – TSRequest structure

The TSRequest structure is the most-used structure by the CredSSP client and CredSSP server. It contains the SPNEGO tokens and may contain Kerberos/New Technology LAN Manager (NTLM) messages that are passed between the client and server, and either the public key authentication messages that are used to bind to the TLS session or the client credentials that are delegated to the server. The TSRequest message is always sent over the TLS-encrypted channel between the client and server in a CredSSP Protocol exchange.

RDP states

RDP is a complex protocol which involves a lot of different requests and states until a connection is fully established. This means checking a large number of protocol constraints.

Since a picture is worth a thousand words, here is a picture that describes the RDP connection process:

Fig. 5 – The RDPBCGR Connection Sequence (source: https://securityboulevard.com/2020/05/analyzing-encrypted-rdp-connections/)

You can find more information about the details of the RDP protocol at: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/023f1e69-cfe8-4ee6-9ee0-7e759fb4e4ee

The FreeRDP implementation of the RDP protocol defines the following states through the CONNECTION_STATE enum:

enum CONNECTION_STATE
{
	CONNECTION_STATE_INITIAL,
	CONNECTION_STATE_NEGO,
	CONNECTION_STATE_NLA,
	CONNECTION_STATE_MCS_CONNECT,
	CONNECTION_STATE_MCS_ERECT_DOMAIN,
	CONNECTION_STATE_MCS_ATTACH_USER,
	CONNECTION_STATE_MCS_CHANNEL_JOIN,
	CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT,
	CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE,
	CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT,
	CONNECTION_STATE_LICENSING,
	CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING,
	CONNECTION_STATE_CAPABILITIES_EXCHANGE,
	CONNECTION_STATE_FINALIZATION,
	CONNECTION_STATE_ACTIVE
};

All the structures defined above, are reflected in our fuzzing dictionary.

Changes made in the code for fuzzing

As we saw above, the RDP protocol implements many integrity checks and restrictions for the purposes of avoiding unexpected failures due to errors in the data stream.

However, for our purpose, such integrity checks would be counterproductive. Our fuzzer will constantly mutate data stream bytes and trigger checksum failures, which would normally cause the RDP connection to abort. To address these limitations, we have to make some changes to the FreeRDP code.

Let’s explore some of the changes I’ve had to make in more detail.

Disabling RC4 encoding

First I disabled the RC4 encryption during the “licensing exchange” stage. License transfers from the server to the client take place in this phase — the client stores the license and on subsequent connections sends the license to the server for validation.

The reason to disable RC4 is that fuzzers such as AFL are unable to find bugs on encrypted streams. This is because flip bit/byte mutations usually corrupt encrypted streams. So, generated inputs will be invalid and may not be decrypted correctly.

libfreerdp/core/license.c

Fig. 6

Disabling TLS encoding

Just as we have done with RC4 encryption, and for similar reasons, we will also disable any TLS encryption on the RDP connection. This includes both handshake and certificate validation.

libfreerdp/crypto/tls.c

Fig. 7

libfreerdp/core/nla.c

Fig. 8

Disabling MAC checking

A Message Authentication Code (MAC) is a cryptographic checksum on data that uses a session key to detect both accidental and intentional modifications of the data.

Since the fuzzer’s mutations will likely introduce MAC check failures, we should also disable those.

libfreerdp/core/license.c

Fig. 9

Disabling NTLM signature verification

NTLM is the authentication protocol used on networks that include systems running the Windows operating system. RDP protocol uses either NTLM or Kerberos to perform its authentication.

In my case, I mainly focused on NTLM authentication. And since the NTLM protocol includes signature verification, any change made by the fuzzer in the NTLM section will cause an error in the authentication process. So we disable NTLM signature verification as well:

winpr/libwinpr/sspi/NTLM/ntlm.c

Fig. 10

Disabling Nhash verification

When Network Level Authentication (NLA) is enabled, we also need to disable SHA256 digest comparisons:

libfreerdp/core/nla.c

Fig. 11

Disabling multi-threading

In the previous installment of this series, we discussed how we generally want to avoid forking target processes since AFL can not handle multi-process targets well.

Although this limitation doesn’t apply to multi-threaded applications, multiple threads in the target process may negatively affect the AFL “stability” score. This is primarily due to the non-deterministic execution order of program instructions, and this in turn will lead to worse code coverage.

For this reason, I recommend that whenever possible, you should limit thread concurrency to a minimum. Below I show some changes made in the FreeRDP code for this purpose:

channels/drdynvc/client/drdynvc_main.c

Fig. 12

channels/cliprdr/client/cliprdr_main.c

Fig. 13

libfreerdp/codec/rfx.c

Fig. 14

Other minor changes

There are some additional minor changes that were made to the FreeRDP code that are mostly in line with the points we already discussed in the previous post in this series.

You can visit my FreeRDP_FUZZ repository for a detailed record of all the changes made to effectively fuzz the FreeRDP library: Browse the commits

Let’s go fuzzing

Now that we have shown the changes made in the code, we can now explain the fuzzing process itself.

First of all, we need to configure and build the project for fuzzing with AFL:

cmake -G "Eclipse CDT4 - Unix Makefiles" -DCHANNEL_URBDRC=ON -DWITH_FFMPEG=ON -DWITH_CUPS=ON -DWITH_PULSE=ON -DWITH_FAAC=ON -DWITH_FAAD2=ON -DWITH_GSM=ON -DWITH_JPEG=ON -DWITH_MBEDTLS=ON -DCMAKE_C_COMPILER=afl-clang-fast -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -g" -DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -g" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address -fno-sanitize-recover=all" DCMAKE_INSTALL_PREFIX=/home/antonio/Downloads/FreeFuzzing/FreeRDP/extLibs/FreeRDP/install/ -DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all" -DCMAKE_BUILD_TYPE=Debug,ASAN,UBSAN -DWITH_SSE2=ON -DMONOLITHIC_BUILD=ON -DBUILD_SHARED_LIBS=OFF .
cmake --build . -j 4

The relevant part is that I’ve enabled Address Sanitizer and Undefined Behavior Sanitizer and that I’ve set “afl-clang-fast” as the default compiler. I’ve also disabled shared libs and have chosen the monolithic build option. And, finally, I’ve enabled the maximum possible external functionalities (DCHANNEL_URBDRC, DWITH_CUPS, DWITH_PULSE, DWITH_FAAC, DWITH_GSM, DWITH_JPEG, DWITH_MBEDTLS).

As you can see in the following code snippet, we’re going to invoke the xfreerdp executable in our fuzzing process:

 ./client/X11/xfreerdp /v:127.0.0.1 /p:whatever /log-level:TRACE /relax-order-checks +glyph-cache +bitmap-cache +menu-anims @@

In order to increase the FreeRDP code coverage, I’ve also enabled the following command-line options:

I also provided the following dictionary to the fuzzer: RDP.dict

Based on the above, my final command line to fuzz FreeRDP with AFL looks like:

ASAN_OPTIONS=verbosity=3,detect_leaks=0,abort_on_error=1,symbolize=0,debug=true,check_initialization_order=true,detect_stack_use_after_return=true,strict_string_checks=true,detect_invalid_pointer_pairs=2 afl-fuzz -t 1500 -m none -i ./AFL/afl_in/ -o './AFL/afl_out' -x './AFL/dictionaries/Basic.txt' -- ./client/X11/xfreerdp /v:127.0.0.1 /p:whatever /log-level:TRACE /relax-order-checks +glyph-cache +bitmap-cache +menu-anims @@

Acknowledgements

I would like to thank akallabeth for their cooperation and professionalism in the process of fixing the bugs. I can say that it has been a pleasure working with you and working on improving FreeRDP security :)

Take a look at the tools and references I used throughout this post for further reading: