Skip to content
CVE-2019-18935

CVE-2019-18935

bishopfox.com September 7, 2026

All code references in this post are also available in the CVE-2019-18935 GitHub repo .

Telerik UI for ASP.NET AJAX is a widely used suite of UI components for web applications. It insecurely deserializes JSON objects in a manner that results in arbitrary remote code execution on the software's underlying host. The Managed Security Services (MSS) team at Bishop Fox has identified and exploited internet-facing instances of Telerik UI affected by this vulnerability for our clients. Since Telerik has just responded to this issue by releasing a security advisory for CVE-2019-18935 , we're sharing our knowledge it here in an effort to raise awareness the severity of this vulnerability, and to encourage affected users to patch and securely configure this software. Patching instructions are included at the end of this post.

Thanks to Markus Wulftange ( @mwulftange ) of Code White GmbH for initially discovering this insecure deserialization vulnerability and for summarizing his research. Thanks also to Paul Taylor (@bao7uo ) who, after authoring an exploit to break encryption for an unrestricted file upload vulnerability, developed an extended custom payload feature that was instrumental in triggering this deserialization vulnerability.

All code references in this post are also available in the CVE-2019-18935 GitHub repo .

UPDATE: Caleb presented on this topic at 2020 DerpCon, which you can watch below.

Vulnerability Details Overview Overview of Vulnerabilities in RadAsyncUpload CVE-2017-11317Unrestricted File Upload via Weak Encryption CVE-2019-18935Remote Code Execution via Insecure Deserialization What's a Mixed Mode Assembly?

Overview of Vulnerabilities in RadAsyncUpload

CVE-2017-11317Unrestricted File Upload via Weak Encryption

CVE-2019-18935Remote Code Execution via Insecure Deserialization

What's a Mixed Mode Assembly?

CVE-2019-18935 Exploit Details Identify Software Version Verify Deserialization Vulnerability with Sleep() Exploit with Reverse Shell

Identify Software Version

Verify Deserialization Vulnerability with Sleep()

Exploit with Reverse Shell

Vulnerability Details

The following sections will walk through two vulnerabilities in RadAsyncUpload, which is a file handler in Telerik UI for ASP.NET AJAX that enables uploading files asynchronously (i.e., without reloading the existing page). After covering the context of those two CVEs, we’ll dive deeper into the insecure deserialization vulnerability to learn if it affects your system, how the exploit works, and how you can patch systems against this vulnerability.

Overview of Vulnerabilities in RadAsyncUpload

RadAsyncUpload has previously been the subject of a number of vulnerabilities, including CVE-2014-2217, which is a path traversal vulnerability in the handler's file upload POST requests that results in unrestricted file upload. (Don't confuse it with CVE-2017-11317, which also yields unrestricted file upload, but through a different vector…more on that shortly.)

CVE-2014-2217 is outside of the scope of this post, but it's important that we mention it here, since Telerik responded to this issue by encrypting a particular portion of file upload requests to prevent attackers from tampering with sensitive settings. Specifically, Telerik encrypted the rauPostData POST parameter, which contains a serialized object that holds configuration details how the file should be handled (e.g., the destination directory on the web server where the file should be uploaded). If attackers were able to break the encryption protecting the configuration object in rauPostData , they could:

Modify the configuration to allow file uploading anywhere they like on the target web server. This issue (CVE-2017-11317) is a well-known vulnerability and has already been reported on.

Modify the type of the object in rauPostData , allowing them to control the object's behavior while it's being deserialized. This issue (CVE-2019-18935) is the main subject of this post.

In summary, in order to exploit insecure deserialization (CVE-2019-18935) in this file handler, we must first break the encryption that the handler uses to protect file upload POST requests (CVE-2017-11317).

CVE-2017-11317 - Unrestricted File Upload via Weak Encryption

Until R2 2017 SP1 (v2017.2.621), RadAsyncUpload's AsyncUploadHandler was configured with a hard-coded key that was used to encrypt form data in file upload requests. If this encryption key was not changed from its default value of PrivateKeyForEncryptionOfRadAsyncUploadConfiguration , an attacker could use that key to craft a file upload request to /Telerik.Web.Ui.WebResource.axd?type=rau with a custom encrypted rauPostData POST parameter. If an attacker specified an arbitrary value for the TempTargetFolder variable within the encrypted rauPostData POST parameter, it would effectively allow file uploads to any directory where the web server had write permissions. Please refer to @straightblast's write-up for a detailed breakdown of rauPostData 's structure (and of this vulnerability in general), and Telerik's security advisory for how this vulnerability was remediated.

CVE-2019-18935 - Remote Code Execution via Insecure Deserialization

Even though the unrestricted file upload vulnerability had been extensively discussed since its discovery in 2017, Markus Wulftange took a closer look at the way RadAsyncUpload processed the rauPostData parameter in file upload requests in early 2019. He noted that rauPostData contains both the serialized configuration object and the object's type. AsyncUploadHandler uses the type specified within rauPostData to prepare .NET's JavaScriptSerializer.Deserialize() method to properly deserialize the object.

During deserialization, JavaScriptSerializer calls setter methods for the specified object type. If this type is controlled by an attacker, this can lead to a dangerous scenario where the attacker may specify the type to be a gadget . A gadget is a class within the executing scope of the application that, as a side effect of being instantiated and modified via setters or field assignment, has special properties that make it useful during deserialization. A remote code execution (RCE) gadget's properties allow it to perform operations that facilitate executing arbitrary code.

Rather than submitting the usual expected Telerik.Web.UI.AsyncUploadConfiguration type within rauPostData , an attacker can submit a file upload POST request specifying the type as an RCE gadget instead. After using the aforementioned unrestricted file upload vulnerability to upload a malicious mixed mode assembly DLL, an attacker may follow up with a second request to force JavaScriptSerializer to deserialize an object of type System.Configuration.Install.AssemblyInstaller . When deserialized along with an attacker-supplied path property pointing to the uploaded DLL, this will cause the application to load the DLL into its current domain. As long as the mixed mode assembly DLL is of the same architecture as the loading process, its entry-point function DDLMain() will be called when the DLL is loaded. For more details, please refer to Implications of Loading .NET Assemblies and Friday the 13th JSON Attacks .

OK, What's a Mixed Mode Assembly?

According to MSDN, a mixed mode assembly contains "both unmanaged machine instructions and [CIL] instructions." If you're unfamiliar with the .NET framework, then these terms may not mean anything to you. Let's break these down a bit, starting with a useful description from Wikipedia how programs execute when developed in .NET:

Programs written for .NET Framework execute in a software environment (in contrast to a hardware environment) named the Common Language Runtime (CLR). The CLR is an application virtual machine that provides services such as security, memory management, and exception handling. As such, computer code written using .NET Framework is called "managed code."

So, "managed" code is written to run exclusively under the CLR, a layer that wraps native compiled code to prevent some common problems (e.g., buffer overflows) and abstract away some platform-specific implementation details to make code more portable. C# is often considered a managed language as it's typically compiled to CIL (Common Intermediate Language—a platform-independent language between source code and final native machine code) to be run under the CLR. CIL, in turn, is compiled into native code by a just-in-time compiler within the CLR. Conversely, code that does not target the CLR is known as "unmanaged" code (e.g., your average C program).

CVE-2019-18935 Exploit Details

Now with our background knowledge of the prerequisite unrestricted file upload vulnerability (CVE-2017-11317), the deserialization vulnerability itself, and mixed mode assemblies, we can now explore this exploit step by step.

Identify Software Version

Before attempting to exploit Telerik UI for ASP.NET AJAX, confirm first that the file upload handler is registered:

Additionally, you’ll need to confirm that the web application is using a vulnerable version of this software. Conveniently, Telerik publishes a release history that details all major software versions since April 2007.

Without Authentication

If the application using RadAsyncUpload does not require authentication, then you can usually find the UI version buried somewhere in the HTML source of the application's page. The location of the version string isn't consistent, though, so the best method of locating it is to use Burp to for the regular expression 20[0-9]{2}(\.[0-9]*)+ (and make sure you check the "Regex" box). You can also accomplish this with cURL:

If that doesn't work, you can alternatively for the string <script src="/WebResource to identify any JavaScript files that are included in the site's page. Choose one of the static resources there and examine its Last-Modified date in the HTTP response header; that date should roughly match the release date of the software. For example, a JavaScript resource bundled with UI for ASP.NET AJAX Q1 2013 (v2013.1.220, released on February 20, 2013) will read Last-Modified: Wed, 20 Feb 2013 00:00:00 GMT in the HTTP response header for that file.

If the application does require authentication, then you may be able to determine the software version via brute force. Since uploading a file with RadAsyncUpload requires providing the correct version of Telerik UI, you can use Paul Taylor’s RAU_crypto exploit to submit file upload requests with known-vulnerable versions until you find one that works:

When the file upload succeeds, you'll see a JSON response containing some encrypted data the uploaded file:

Now that you’ve verified that the handler is registered and the software is using a vulnerable version, you can proceed to exploit the vulnerability.

Verify Deserialization Vulnerability with Sleep()

In preparing to fully compromise a remote host with a reverse shell, you can initially verify the deserialization vulnerability by uploading and loading a simple mixed mode assembly DLL that causes the web application to sleep for 10 seconds. A simple program, sleep.c , will do just that.

Note that I use C, rather than C++, because I've encountered rare occasions where I was unable to execute compiled C++ code on a remote server. I suspect that this is because the target environment did not have the Microsoft Visual C++ Redistributable installed.

Create a bare C# class in empty.cs to constitute the managed portion of your mixed mode assembly:

Then, in a Windows environment with Visual Studio installed, open a command prompt and run build_dll.bat sleep.c : build_dll.bat

This batch script accomplishes the following:

Sets environment variables to compile both 32- and 64-bit code

Compiles the empty.cs C# program as a .netmodule file (without generating an assembly)

Compiles the specified C program ( sleep.c , in this case) as an .obj file (without linking)

Links the compiled .netmodule and .obj files, which creates a mixed mode assembly DLL with a unique name

A Cautionary Note Assembly Names

The assembly's name as specified in link /out is baked into the assembly's manifest, and will persist even if the file name changes on disk. It's crucial that the assembly is uniquely named at linking time since a .NET application will only load an assembly once with a given name. This means that an assembly " sleep_123.dll " may cause the application to sleep the first time that DLL is loaded through deserialization, but it certainly won't successfully load again; you'll need to rerun build_dll.bat to generate a new assembly for each exploit attempt on the same server.

Before uploading the DLL, it's important to understand what's going to happen on disk on the remote server. RadAsyncUpload will upload your file to a temporary directory whose location is under your control. If you happen to upload two files with the same name (we're talking file names on disk, not assembly names in a manifest), RadAsyncUpload will append (not overwrite!) the new file to the old one. If the application attempts to load the resulting malformed DLL, it can cause the application to crash—so it's extremely important that you use a unique file name each time you upload a file to the target.

The following exploit script leverages the core RadAsyncUpload encryption logic provided by Paul Taylor's RAU_crypto.py to craft an encrypted rauPostData POST parameter; this enables access to the vulnerable AsyncUploadHandler class through which we can upload files and deserialize arbitrary object types. This script also ensures that each uploaded file has a unique name on disk.

Without being able to remotely determine the architecture of the web server's underlying host, you may need to attempt to trigger this vulnerability with both the 32- and 64-bit DLL versions until you find one that works. Invoke the script as follows:

If the application pauses for approximately 10 seconds before responding, you've got a working deserialization exploit!

Exploit with Reverse Shell

Now that we've verified that we can exploit this vulnerable version of Telerik UI for ASP.NET AJAX, we can instead exploit it with a DLL that spawns a reverse shell to connect back to a server that we control. We use rev_shell.c below, a program that launches a reverse shell as a thread when the DLL is loaded; the threaded nature of this program prevents the shell process from blocking the web application's user interface while running: rev_shell.c

Modify rev_shell.c with the hostname and port of the C2 server where you'll be listening for a callback:

Using the same method of compiling and linking described above, generate your mixed mode assembly DLL:

Open a Netcat listener to catch the callback:

Then upload and load your DLL!

The Telerik security advisory tells you what you need to know, but we’ll repeat the most important parts here:

Upgrade Telerik for ASP.NET AJAX to R3 2019 SP1 (v2019.3.1023) or later.

Read Telerik's RadAsyncUpload security guide in its entirety, and configure the control according to the recommended security settings .

This write-up has demonstrated how an attacker can chain exploits for unrestricted file upload (CVE-2017-11317) and insecure deserialization (CVE-2019-18935) vulnerabilities to execute arbitrary code on a remote machine.

In recent years, insecure deserialization has emerged as an effective attack vector for executing arbitrary code in object-oriented programming frameworks. As we continue to identify and understand this class of vulnerabilities, it’s important that vendors and users employ timely communication to combat the risk posed by vulnerable software. Now that Telerik has released a patch and security advisory for this vulnerability, affected users should do their part by updating and securely configuring their applications.

Big thanks again to Markus Wulftange ( @mwulftange ) and Paul Taylor ( @bao7uo ), both of whom paved the way for this work through their prior research.

Be first to learn latest tools, advisories, and findings.