Skip to content
README ↗ (opens in a new tab)

README ↗ (opens in a new tab)

github.com • October 27, 2026

Proof-of-concept for the unauthenticated arbitrary file read in RadImageEditor (CVE-2026-18672) and the DialogHandler UploadPaths tampering vulnerability (CVE-2026-19219) in Telerik UI for ASP.NET AJAX, fixed in 2026.3.812.

The exploit reads any file reachable by the IIS worker process, including web.config , by poisoning the RadImageEditor download cache. It then escalates to remote code execution through one of two paths depending on the target's configuration:

ViewState (default): forges a signed __VIEWSTATE with a BinaryFormatter gadget that writes a web shell to disk through certutil . Requires a static machineKey in web.config . Supports both the .NET 4.5 format (SP800-108 derived keys) and the legacy format (raw machineKey). With --rce-exec the gadget runs a single command directly instead of dropping a shell (blind execution, no output returned).

Upload (CVE-2026-19219): forges a DocumentManager dialog whose UploadPaths and ViewPaths point at the web root, then uploads a web shell through the file explorer. Requires the Telerik.Web.UI.DialogParametersEncryptionKey .

The web shell is self-encrypted. The inner logic is XOR-encrypted with the shell's own filename as the key, so the shell only works under the exact name the exploit chose. The exploit generates a random password per run.

RadImageEditor stores client-side control state in a hidden _ClientState form field. On postback, RadWebControl.LoadPostData deserialises this field with JavaScriptSerializer . The field has no message authentication code, so the client controls its content.

The LoadClientState override in RadImageEditor reads the imageUrl key from the deserialised dictionary and assigns it directly to the ImageUrl property. No path validation or canonicalisation runs.

During pre-render, ControlPreRender calls StoreDownloadKey(ImageUrl) , which inserts the attacker-controlled file path into HttpRuntime.Cache keyed by a freshly generated GUID. The server encrypts this GUID and writes it into the page response as the _downloadKey property. The attacker extracts the encrypted GUID from the response. They never need to know the encryption key because the server encrypts it for them.

A second request to the ImageEditorCacheHandler (reached via the WebResource.axd?type=iec route) presents the encrypted GUID in the encryptedDownloadKey form field. The handler decrypts the GUID, retrieves the cached file path, resolves it to a physical path with MapPath , and reads the file from disk. The only path check in ResolveFilePathFromKey is StartsWith(PhysicalApplicationPath) , which passes for any file inside the web application directory. web.config is inside the application directory, so it passes.

The result is a two-request unauthenticated arbitrary file read of any file within the web application root.

DialogHandler vector (CVE-2026-19219)

Pages that render a RadEditor control emit a _dialogDefinitions JSON block containing a pre-encrypted SerializedParameters ( dp ) blob for each editor dialog, including the ImageEditor dialog. An attacker harvests this dp from any RadEditor page and POSTs it to Telerik.Web.UI.DialogHandler.aspx?DialogName=ImageEditor to render a standalone ImageEditor dialog. The same cache-poison chain works against this dialog instance. The dp blob is minted by the server, so the attacker does not need the DialogParametersEncryptionKey .

The dialog parameters also carry UploadPaths , ViewPaths , and DeletePaths . These fields are encrypted but lack integrity protection. An attacker who has read web.config via the file read and recovered the DialogParametersEncryptionKey can forge a dialog whose UploadPaths points at the web root, then upload a web shell through the file explorer.

The target path is sent application-relative ( ~/web.config ). It is cached as a raw string and resolved twice: at poison time the image fetch resolves it from the requesting page's folder, while the cache handler later resolves it from the application root. A bare or ../ path resolves differently in the two stages and misses the file. ~/ anchors to the application root in both, so it is deployment-agnostic (the exploit's normalize_target_path does this).

DialogHandler variant

Client-state trust gap

Unsigned _ClientState (RadWebControl.cs)

LoadPostData reads the _ClientState hidden field and deserialises it with JavaScriptSerializer . There is no HMAC, no ViewState MAC, and no encryption. The field is an ordinary form value that the client controls entirely.

Trusted imageUrl assignment (RadImageEditor.cs)

The imageUrl value from the client state is assigned to ImageUrl with no path check, no extension whitelist, and no canonicalisation.

Cache poisoning (RadImageEditor.cs)

ControlPreRender calls StoreDownloadKey(ImageUrl) , which inserts the attacker-controlled path into the server cache:

The server encrypts the GUID and embeds it in the page. The attacker reads the encrypted value from the response without needing any key material.

File read sink (ImageEditorCacheHandler.cs)

ResolveFilePathFromKey retrieves the cached path, resolves it to a physical path via MapPath , and checks only that the result starts with the application's physical root. There is no extension whitelist, no filename format check, and no content-type restriction. The allowCrossAppMapping: false parameter prevents reads outside the application root, but every file inside it, including web.config , is reachable.

A page rendering (standalone mode) or (dialog mode, with --via-dialog )

pycryptodome and requests (see requirements.txt )

Discover the Telerik.Web.UI version from the page source (assembly-qualified reference or CDN path).

Poison the RadImageEditor download cache by posting a forged _ClientState with imageUrl set to the target file path. The server caches the value under a GUID and returns an encrypted download key. In --via-dialog mode, the exploit first harvests the signed ImageEditor dp from any RadEditor page and replays it to DialogHandler.aspx .

Present the encrypted download key to WebResource.axd?type=iec , which reads the cached path from disk and returns the raw bytes.

If --rce is set, parse web.config from the recovered file to extract keys.

For --rce-method viewstate : forge a signed __VIEWSTATE carrying a TextFormattingRunProperties gadget chain whose Process.Start writes the web shell to disk. The shell's physical path comes from the web root, which the exploit recovers from the RadAsyncUpload configuration the target page embeds (its TempTargetFolder ), falls back to probing a missing file through the read, and can be supplied directly with --webroot . With --rce-exec , the gadget runs cmd.exe /c directly instead; nothing is written, and no output is returned (blind execution).

For --rce-method upload : forge a DocumentManager dialog whose UploadPaths cover the web root and upload the shell through the file explorer.

Verify the shell by sending whoami , then enter an interactive session.

The exploits and proof-of-concept code in this repository are published for security research, defensive use, and authorised testing. All of the vulnerabilities demonstrated here are already patched; we strongly recommend updating Telerik UI for ASP.NET AJAX to the latest version.

Parts of exploit.py are ported from ysoserial.net by Alvaro Muñoz and used under the MIT License. The copyright and license notice is reproduced in THIRD_PARTY_LICENSES and at the top of the ported section in exploit.py .

Released under the MIT License .

Extracted Entities