hunt-aspnet

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Crown Jewel Targets

核心高价值目标

ASP.NET deserialization bugs pay among the highest amounts in bug bounty when they reach RCE. Even when patched, the disclosure-tier findings (signed-only ViewState, dual-parser differential, request-validator quirks) reliably pay Low-Medium.
Highest-value targets:
  • SharePoint farms (any version — 2013/2016/2019/SE) — sign-only ViewState + permissive ToolPane.aspx + anonymous FormDigest creates the CVE-2025-53770 ToolShell precondition chain
  • Telerik UI for ASP.NET AJAX —
    Telerik.Web.UI.WebResource.axd
    is a documented RCE sink when keys leak (CVE-2017-11317, CVE-2017-11357, CVE-2019-18935)
  • Classic ASP.NET Webforms enterprise apps — banking portals, dealer portals, HR systems left on .NET Framework 4.x
  • WCF services (
    *.svc?WSDL
    ) — often forgotten admin endpoints with looser auth than the main app
  • Sitecore CMS — ViewState + Sitecore-specific deserialization chains (CVE-2021-42237)
  • DotNetNuke (DNN) — historic ViewState RCE chains
  • Umbraco CMS — ViewState + custom deserialization sinks
Asset types that pay most: internet-reachable ASP.NET Webforms apps > WCF admin services > Telerik-integrated sites > Classic ASP.NET MVC with VSF (very rare)

ASP.NET 反序列化漏洞一旦可达到 RCE,在漏洞赏金中通常价值最高。即使漏洞已经修补,披露级别的问题(仅签名 ViewState、双解析器差异、请求验证器异常行为)通常仍可获得低至中等奖金。
最高价值目标:
  • SharePoint 集群(任意版本——2013/2016/2019/SE)——仅签名 ViewState + 权限宽松的 ToolPane.aspx + 匿名 FormDigest 可形成 CVE-2025-53770 ToolShell 前置条件链
  • Telerik UI for ASP.NET AJAX——当密钥泄露时,
    Telerik.Web.UI.WebResource.axd
    是一个已知 RCE 利用入口(CVE-2017-11317、CVE-2017-11357、CVE-2019-18935)
  • 经典 ASP.NET WebForms 企业应用——仍运行在 .NET Framework 4.x 上的银行门户、经销商门户和 HR 系统
  • WCF 服务(
    *.svc?WSDL
    )——经常被遗忘的管理端点,其认证配置通常比主应用更宽松
  • Sitecore CMS——ViewState + SitePoint 特定反序列化链(CVE-2021-42237)
  • DotNetNuke (DNN)——历史上的 ViewState RCE 链
  • Umbraco CMS——ViewState + 自定义反序列化入口
**奖金最高的资产类型:**互联网可达的 ASP.NET WebForms 应用 > WCF 管理服务 > 集成 Telerik 的站点 > 使用 VSF 的经典 ASP.NET MVC(非常少见)

Attack Surface Signals

攻击面特征

Response headers indicating ASP.NET:
X-AspNet-Version: 4.0.30319          (classic — disclosure on its own)
X-Powered-By: ASP.NET
X-AspNetMvc-Version: 5.2
Server: Microsoft-IIS/10.0
Set-Cookie: ASP.NET_SessionId=...
Set-Cookie: .ASPXAUTH=...            (Forms auth cookie)
Set-Cookie: .ASPXFORMSAUTH=...
Set-Cookie: ASP.NET_SessionId=...; SameSite=None  (suggests cross-origin embedding)
Body signals (in form HTML):
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="..." />
<input type="hidden" name="__VIEWSTATEENCRYPTED" id="__VIEWSTATEENCRYPTED" value="" />
                                        ↑ EMPTY = signed-only, not encrypted = exploitable if key leaks
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="..." />
<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0x...,...">
                                        ↑ SharePoint CSRF token; if anon-issued, see hunt-sharepoint
URL patterns to probe:
/trace.axd                            (per-app trace viewer; sometimes anon-accessible)
/elmah.axd                            (ELMAH error log viewer)
/elmah.axd/?id=...                    (ELMAH RCE / stack-trace leak)
/*.svc                                (WCF services)
/*.svc?wsdl                           (WCF WSDL)
/*.svc/mex                            (Metadata Exchange)
/*.asmx                               (legacy SOAP)
/*.asmx?WSDL                          (legacy SOAP description)
/*.asmx?disco                         (legacy discovery)
/Telerik.Web.UI.WebResource.axd       (Telerik AJAX components)
/ChartImg.axd                         (DataVisualization controls; historic deserialization)
/ScriptResource.axd                   (script resource handler; sometimes leaks paths)
/WebResource.axd                      (web resource handler)
/_vti_bin/*                           (SharePoint Web Service Forwarder)
/api/                                 (Web API 2.x is ASP.NET on classic framework)
/signin                               (often FedAuth / WS-Federation)
Tech-stack signals:
  • Server: Microsoft-IIS/10.0
    (or
    /8.5
    ,
    /7.5
    ) — confirmed Windows + IIS
  • X-AspNet-Version
    header — classic .NET Framework (4.x); .NET Core/5+ does NOT emit this
  • Cookies with
    ASP.NET_SessionId
    ,
    .ASPXAUTH
    ,
    FedAuth
    — Forms or claims auth
  • __VIEWSTATE
    in form bodies — Webforms (NOT MVC, NOT Razor Pages, NOT Blazor)
  • MicrosoftSharePointTeamServices
    header (sometimes stripped by ELB but leaks in
    start.aspx
    body) — SharePoint

表明 ASP.NET 的响应头:
X-AspNet-Version: 4.0.30319          (classic — disclosure on its own)
X-Powered-By: ASP.NET
X-AspNetMvc-Version: 5.2
Server: Microsoft-IIS/10.0
Set-Cookie: ASP.NET_SessionId=...
Set-Cookie: .ASPXAUTH=...            (Forms auth cookie)
Set-Cookie: .ASPXFORMSAUTH=...
Set-Cookie: ASP.NET_SessionId=...; SameSite=None  (suggests cross-origin embedding)
正文特征(位于表单 HTML 中):
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="..." />
<input type="hidden" name="__VIEWSTATEENCRYPTED" id="__VIEWSTATEENCRYPTED" value="" />
                                        ↑ EMPTY = signed-only, not encrypted = exploitable if key leaks
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="..." />
<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0x...,...">
                                        ↑ SharePoint CSRF token; if anon-issued, see hunt-sharepoint
应探测的 URL 模式:
/trace.axd                            (per-app trace viewer; sometimes anon-accessible)
/elmah.axd                            (ELMAH error log viewer)
/elmah.axd/?id=...                    (ELMAH RCE / stack-trace leak)
/*.svc                                (WCF services)
/*.svc?wsdl                           (WCF WSDL)
/*.svc/mex                            (Metadata Exchange)
/*.asmx                               (legacy SOAP)
/*.asmx?WSDL                          (legacy SOAP description)
/*.asmx?disco                         (legacy discovery)
/Telerik.Web.UI.WebResource.axd       (Telerik AJAX components)
/ChartImg.axd                         (DataVisualization controls; historic deserialization)
/ScriptResource.axd                   (script resource handler; sometimes leaks paths)
/WebResource.axd                      (web resource handler)
/_vti_bin/*                           (SharePoint Web Service Forwarder)
/api/                                 (Web API 2.x is ASP.NET on classic framework)
/signin                               (often FedAuth / WS-Federation)
技术栈特征:
  • Server: Microsoft-IIS/10.0
    (或
    /8.5
    、
    /7.5
    )——已确认使用 Windows + IIS
  • X-AspNet-Version
    响应头——表明使用经典 .NET Framework(4.x);.NET Core/5+ 不会发送此响应头
  • 包含
    ASP.NET_SessionId
    、
    .ASPXAUTH
    、
    FedAuth
    的 Cookie——使用 Forms 或 Claims 认证
  • 表单正文中包含
    __VIEWSTATE
    ——属于 WebForms(不是 MVC、Razor Pages 或 Blazor)
  • MicrosoftSharePointTeamServices
    响应头(有时会被 ELB 移除,但可能泄露在
    start.aspx
    正文中)——表明使用 SharePoint

Step-by-Step Hunting Methodology

分步排查方法

  1. Fingerprint the framework version. Trigger any 500 error (stale ViewState POST is a reliable way) and look for
    Version Information: Microsoft .NET Framework Version:X.X.XXXXX; ASP.NET Version:X.X.XXXX.X
    in the error body. This banner discloses both the runtime and ASP.NET-version-specific patch level. .NET 4.0.30319 + ASP.NET 4.8.x is the most common modern combination.
  2. Locate every form with
    __VIEWSTATE
    .
    Spider the target and grep for
    name="__VIEWSTATE"
    . Each is a candidate sink for deserialization attacks if MAC / encryption is bypassable.
  3. Check
    __VIEWSTATEENCRYPTED
    value.
    Empty (
    value=""
    ) means ViewState is signed-only via
    <machineKey>
    but NOT encrypted. Recovery of the validation key → arbitrary deserialization. Non-empty (
    value="something"
    ) means ViewState is BOTH signed and encrypted; both keys needed to forge.
  4. Test the ViewState parser-error differential (the dual-parser anti-pattern). Send 7+ ViewState shapes and classify responses:
    • Trivial garbage (
      AAAA
      ) →
      "Validation of viewstate MAC failed"
    • Real prefix from current page →
      "Validation of viewstate MAC failed"
    • Flipped-bit real ViewState →
      "Validation of viewstate MAC failed"
    • Oversize (
      A * 100000
      ) →
      "Validation of viewstate MAC failed"
    • XML-shaped (
      <xss/>
      ) → "The state information is invalid for this page and might be corrupted" ← different parser path
    • LosFormatter-style prefix (
      /wEPDwUKMTcxNzgyOTQwMmRkkz9p4lzA...
      ) → "The state information is invalid for this page and might be corrupted"
    The differential proves there are two distinct deserialization entry points, one of which dispatches BEFORE the MAC check on some payload shapes. Historically this enables MAC-before-parse-bypass exploits.
  5. Look for load-balanced cross-node ViewState MAC failures. If POST gets a 500 with
    "Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey..."
    , the farm has multiple WFEs WITHOUT machineKey sync, or without sticky-session affinity. Operationally this breaks legit users; security-wise it confirms farm topology.
  6. Probe
    trace.axd
    and
    elmah.axd
    .
    If either returns 200 anonymously, it's a Critical finding (trace leaks every request + headers + form data; ELMAH leaks every server error including stack traces).
  7. Enumerate WCF services (
    .svc
    ).
    For each, fetch
    ?wsdl
    and
    ?mex
    (metadata exchange). MEX endpoints sometimes return full service contracts including admin operations.
  8. Test request-validator bypass. ASP.NET's request validator blocks
    <
    in query strings by default. Bypass categories that may still get through:
    • HTML-entity-encoded payloads (
      &lt;script&gt;
      — but these don't execute)
    • Encoded inside JSON / XML POST bodies (different content-type ≠ same validator)
    • In path segments (not query) — validator scope depends on framework version
    • In Cookie / Referer headers (varies)
    • Inside
      <%@ ... %>
      ASP directives if reached via WebDAV PUT (rare)
  9. Check
    customErrors
    mode.
    If 500s expose full stack traces, framework versions, file paths, internal method names →
    customErrors mode="Off"
    is set. Should be
    RemoteOnly
    for production.
  10. Look for Telerik components.
    Telerik.Web.UI.WebResource.axd?type=rau
    is the historic upload-to-RCE chain (CVE-2017-11317). The
    dialogParametersHolder
    parameter chain (CVE-2019-18935) requires the encryption key but is otherwise RCE.
  11. SharePoint-specific deserialization paths — see
    hunt-sharepoint
    skill for the ToolPane.aspx + anonymous FormDigest + unencrypted ViewState chain.
  12. SafeControl enumeration via reflection. SharePoint's
    Picker.aspx?PickerDialogType=<TypeName>
    (and DNN-equivalent endpoints) accept class names and return DIFFERENT error messages for "type exists but not whitelisted" vs "type does not exist." Feed a wordlist of
    Microsoft.SharePoint.*.WebControls.*
    types to enumerate the SafeControl list — useful for CVE-2019-0604-family hunting.

  1. 识别框架版本。 触发任意 500 错误(发送无效的 ViewState POST 是一种可靠方式),并在错误正文中查找
    Version Information: Microsoft .NET Framework Version:X.X.XXXXX; ASP.NET Version:X.X.XXXX.X
    。该版本信息会泄露运行时版本以及适用于特定 ASP.NET 版本的补丁级别。
    .NET 4.0.30319 + ASP.NET 4.8.x
    是最常见的现代组合。
  2. 定位所有包含
    __VIEWSTATE
    的表单。
    爬取目标并搜索
    name="__VIEWSTATE"
    。如果 MAC 或加密可以被绕过,每个表单都是反序列化攻击的候选入口。
  3. 检查
    __VIEWSTATEENCRYPTED
    的值。
    空值(
    value=""
    )表示 ViewState 通过
    <machineKey>
    仅签名但未加密。恢复 validation key 后即可进行任意反序列化。非空值(
    value="something"
    )表示 ViewState 同时经过签名和加密;伪造它需要同时获得两类密钥。
  4. 测试 ViewState 解析器错误差异(双解析器反模式)。发送至少 7 种 ViewState 形态并对响应进行分类:
    • 无意义垃圾数据(
      AAAA
      )→
      "Validation of viewstate MAC failed"
    • 当前页面的真实前缀 →
      "Validation of viewstate MAC failed"
    • 翻转某一位的真实 ViewState →
      "Validation of viewstate MAC failed"
    • 超大载荷(
      A * 100000
      )→
      "Validation of viewstate MAC failed"
    • XML 形态(
      <xss/>
      )→ "The state information is invalid for this page and might be corrupted" ← 不同的解析器路径
    • LosFormatter 风格前缀(
      /wEPDwUKMTcxNzgyOTQwMmRkkz9p4lzA...
      )→ "The state information is invalid for this page and might be corrupted"
    这种差异证明存在两个不同的反序列化入口,其中某个入口会在部分载荷形态下先于 MAC 检查进行调度。历史上,这可以实现 MAC-before-parse-bypass 漏洞利用。
  5. 查找负载均衡环境中的跨节点 ViewState MAC 失败。 如果 POST 返回 500,并出现
    "Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey..."
    ,则说明集群中存在多个未同步 machineKey 的 WFE,或者未启用粘性会话亲和性。运维上,这会导致合法用户无法正常使用;安全上,则确认了集群拓扑。
  6. 探测
    trace.axd
    和
    elmah.axd
    。
    如果其中任意一个允许匿名访问并返回 200,则属于严重发现(trace 会泄露每个请求、响应头和表单数据;ELMAH 会泄露包含堆栈跟踪在内的所有服务器错误)。
  7. 枚举 WCF 服务(
    .svc
    )。
    对每个服务获取
    ?wsdl
    和
    ?mex
    (Metadata Exchange)。MEX 端点有时会返回完整服务契约,其中包括管理操作。
  8. 测试请求验证器绕过。 ASP.NET 请求验证器默认阻止查询字符串中的
    <
    。可能仍能通过的绕过类别包括:
    • HTML 实体编码的载荷(
      &lt;script&gt;
      ——但不会执行)
    • 编码后置于 JSON/XML POST 正文中(不同的 Content-Type 并不意味着使用相同的验证器)
    • 位于路径段而非查询字符串中——验证范围取决于框架版本
    • 位于 Cookie 或 Referer 响应头中——具体行为有所不同
    • 通过 WebDAV PUT 触达时位于
      <%@ ... %>
      ASP 指令内部(较少见)
  9. 检查
    customErrors
    模式。
    如果 500 错误暴露完整堆栈跟踪、框架版本、文件路径和内部方法名称,则说明设置了
    customErrors mode="Off"
    。生产环境应使用
    RemoteOnly
    。
  10. 查找 Telerik 组件。
    Telerik.Web.UI.WebResource.axd?type=rau
    是经典的上传至 RCE 链(CVE-2017-11317)。
    dialogParametersHolder
    参数链(CVE-2019-18935)需要加密密钥,但除此之外可直接实现 RCE。
  11. SharePoint 特定反序列化路径——有关 ToolPane.aspx + 匿名 FormDigest + 未加密 ViewState 链,请参阅
    hunt-sharepoint
    skill。
  12. 通过反射枚举 SafeControl。 SharePoint 的
    Picker.aspx?PickerDialogType=<TypeName>
    (以及 DNN 中的同类端点)接受类名,并针对“类型存在但不在白名单中”和“类型不存在”返回不同的错误消息。向其提供
    Microsoft.SharePoint.*.WebControls.*
    类型词表,即可枚举 SafeControl 列表——这有助于排查 CVE-2019-0604 系列漏洞。

Payload & Detection Patterns

载荷与检测模式

Stack-trace fingerprint (trigger via stale ViewState POST):
bash
curl -sk -X POST "https://target.example/page.aspx" \
  --data "__VIEWSTATE=AAAA&__VIEWSTATEGENERATOR=AAAA"
堆栈跟踪指纹(通过无效 ViewState POST 触发):
bash
curl -sk -X POST "https://target.example/page.aspx" \
  --data "__VIEWSTATE=AAAA&__VIEWSTATEGENERATOR=AAAA"

Inspect body for:

Inspect body for:

- "Validation of viewstate MAC failed" → confirms signed ViewState

- "Validation of viewstate MAC failed" → confirms signed ViewState

- "The state information is invalid for this page" → confirms ALTERNATE parser path

- "The state information is invalid for this page" → confirms ALTERNATE parser path

- "Version Information: Microsoft .NET Framework Version:X.X.XXXXX" → exact patch level

- "Version Information: Microsoft .NET Framework Version:X.X.XXXXX" → exact patch level

- "Microsoft.SharePoint.Client.ServerStub..." → SharePoint farm

- "Microsoft.SharePoint.Client.ServerStub..." → SharePoint farm


**ViewState parser-error differential probe (Python):**
```python
import requests, re, json
S = requests.Session(); S.verify = False

**ViewState 解析器错误差异探测(Python):**
```python
import requests, re, json
S = requests.Session(); S.verify = False

Get fresh form

Get fresh form

r = S.get("https://target.example/path/page.aspx") real_vs = re.search(r'__VIEWSTATE" id="__VIEWSTATE" value="([^"]+)', r.text).group(1) real_vsg = re.search(r'__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="([^"]+)', r.text).group(1)
r = S.get("https://target.example/path/page.aspx") real_vs = re.search(r'__VIEWSTATE" id="__VIEWSTATE" value="([^"]+)', r.text).group(1) real_vsg = re.search(r'__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="([^"]+)', r.text).group(1)

Test 7 payload shapes

Test 7 payload shapes

for label, vs in [ ("trivial", "AAAA"), ("real", real_vs), ("flipped-bit", real_vs[:50] + "X" + real_vs[51:]), ("oversize", "A" * 100000), ("base64", "VGVzdE1hcmtlcjY3OFhZWg=="), ("xml-shaped", "<xss/>"), ("losformatter", "/wEPDwUKMTcxNzgyOTQwMmRkkz9p4lzA" + "A"*50), ]: r = S.post("https://target.example/path/page.aspx", data={"__VIEWSTATE": vs, "__VIEWSTATEGENERATOR": real_vsg}) title = re.search(r'<title>([^<]+)</title>', r.text) title = title.group(1)[:100] if title else "—" print(f" [{label:14s}] {r.status_code} {title}")

**`trace.axd` anonymous check:**
```bash
curl -sk -o /dev/null -w "%{http_code}\n" "https://target.example/trace.axd"
for label, vs in [ ("trivial", "AAAA"), ("real", real_vs), ("flipped-bit", real_vs[:50] + "X" + real_vs[51:]), ("oversize", "A" * 100000), ("base64", "VGVzdE1hcmtlcjY3OFhZWg=="), ("xml-shaped", "<xss/>"), ("losformatter", "/wEPDwUKMTcxNzgyOTQwMmRkkz9p4lzA" + "A"*50), ]: r = S.post("https://target.example/path/page.aspx", data={"__VIEWSTATE": vs, "__VIEWSTATEGENERATOR": real_vsg}) title = re.search(r'<title>([^<]+)</title>', r.text) title = title.group(1)[:100] if title else "—" print(f" [{label:14s}] {r.status_code} {title}")

**`trace.axd` 匿名访问检查:**
```bash
curl -sk -o /dev/null -w "%{http_code}\n" "https://target.example/trace.axd"

200 = full trace dump exposed → Critical

200 = full trace dump exposed → Critical

403 = mod set to localhost-only → check via X-Forwarded-For: 127.0.0.1

403 = mod set to localhost-only → check via X-Forwarded-For: 127.0.0.1


**WCF service enumeration:**
```bash

**WCF 服务枚举:**
```bash

Find all .svc files

Find all .svc files

curl -sk "https://target.example/" -o body.html grep -oE '/[a-zA-Z0-9/_-]+.svc' body.html | sort -u
curl -sk "https://target.example/" -o body.html grep -oE '/[a-zA-Z0-9/_-]+.svc' body.html | sort -u

For each found:

For each found:

curl -sk "https://target.example/Service.svc?wsdl" | xmllint --format - | head -60

**Request-validator bypass categories:**
curl -sk "https://target.example/Service.svc?wsdl" | xmllint --format - | head -60

**请求验证器绕过类别:**

Default: <script>alert(1)</script> in ?q= → "Potentially dangerous Request.QueryString value detected"

Default: <script>alert(1)</script> in ?q= → "Potentially dangerous Request.QueryString value detected"

Bypasses that sometimes work:

Bypasses that sometimes work:

?q=%3cscript%3e (URL-encoded — depends on validator config) ?q=<svg/onload=alert(1)> (depends on validator version) ?q=<%00script> (NUL-byte; older validators) ?q=javascript:alert(1) (no < at all — passes validator) Cookie: foo=<script> (cookie body not validated by default) Referer: http://x.com/<script> (referer not validated in classic ASP.NET)

**Telerik exploit gate (CVE-2019-18935 — requires encryption keys):**
```bash
?q=%3cscript%3e (URL-encoded — depends on validator config) ?q=<svg/onload=alert(1)> (depends on validator version) ?q=<%00script> (NUL-byte; older validators) ?q=javascript:alert(1) (no < at all — passes validator) Cookie: foo=<script> (cookie body not validated by default) Referer: http://x.com/<script> (referer not validated in classic ASP.NET)

**Telerik 利用门槛(CVE-2019-18935——需要加密密钥):**
```bash

Fingerprint Telerik

Fingerprint Telerik

If response is RadAsyncUploadHandler-style → Telerik present; try keys

If response is RadAsyncUploadHandler-style → Telerik present; try keys

Public exploits require leaked machineKey AND telerikEncryptionKey

Public exploits require leaked machineKey AND telerikEncryptionKey


---

---

Common Root Causes

常见根因

  1. viewStateEncryption="Auto"
    defaults to signed-only on pages without sensitive ViewState data.
    Many SharePoint pages are configured this way. When
    __VIEWSTATEENCRYPTED
    is empty, ViewState is signed-only — recovery of
    validationKey
    alone enables forgery.
  2. <machineKey>
    AutoGenerate in a Web Farm.
    Each WFE generates a different key on first boot; ViewState issued by one WFE fails MAC validation on another. Operationally produces 500s; security-wise broadcasts the topology (the error message names the cluster).
  3. <customErrors mode="Off">
    left from development.
    Stack traces with full method names, file paths, version banners exposed to anonymous internet users.
  4. trace.axd
    /
    elmah.axd
    left enabled in production.
    Often forgotten in
    <system.web><trace enabled="true">
    blocks.
  5. Forgotten WCF
    .svc
    admin endpoints.
    Built for internal admin tooling, never disabled when the main app went to internet exposure.
  6. Dual-parser anti-pattern:
    ObjectStateFormatter
    (legacy) vs
    LosFormatter
    (modern) deserialize in different orders relative to MAC validation.
    Some payload shapes hit the legacy parser BEFORE MAC check.
  7. Request validator only applies to URL-encoded body and querystring. Headers, cookies, XML/JSON bodies, and multipart fields are NOT validated by default. Developers assume validator is universal; it is not.
  8. <machineKey>
    checked into source repos.
    Configuration check-ins to GitHub frequently leak validation/decryption keys. Combine with
    hunt-misc
    source-recon for Telerik / SharePoint / DNN keys.
  9. SafeControls
    web.config entries trusted to gate deserialization.
    SharePoint's
    <SafeControl>
    list determines which classes Picker.aspx can instantiate. Bypasses exist when the inheritance check is the only gate (CVE-2019-0604 family).

  1. 在不含敏感 ViewState 数据的页面上,
    viewStateEncryption="Auto"
    默认为仅签名。
    许多 SharePoint 页面都以此方式配置。当
    __VIEWSTATEENCRYPTED
    为空时,ViewState 仅经过签名——只需恢复 validation key 即可进行伪造。
  2. Web Farm 中的
    <machineKey>
    使用 AutoGenerate。
    每个 WFE 首次启动时都会生成不同的密钥;由一个 WFE 签发的 ViewState 在另一个 WFE 上会因 MAC 验证失败而失效。运维上会产生 500 错误;安全上则会暴露集群拓扑,因为错误消息会直接指出集群的存在。
  3. 开发环境中遗留
    <customErrors mode="Off">
    。
    完整方法名称、文件路径和版本横幅会暴露给匿名互联网用户。
  4. 生产环境中遗留已启用的
    trace.axd
    /
    elmah.axd
    。
    这些功能通常被遗忘在
    <system.web><trace enabled="true">
    配置块中。
  5. 被遗忘的 WCF
    .svc
    管理端点。
    这些端点原本用于内部管理工具,但主应用暴露到互联网后从未被禁用。
  6. 双解析器反模式:
    ObjectStateFormatter
    (旧版)与
    LosFormatter
    (现代)相对于 MAC 验证采用了不同的反序列化顺序。
    某些载荷形态会在 MAC 检查之前进入旧版解析器。
  7. 请求验证器仅适用于 URL 编码正文和查询字符串。 默认情况下,响应头、Cookie、XML/JSON 正文及 multipart 字段均不会被验证。开发者往往误以为验证器覆盖所有上下文,实际上并非如此。
  8. <machineKey>
    被提交到源代码仓库。
    提交到 GitHub 的配置经常泄露 validation/decryption 密钥。可结合
    hunt-misc
    源码侦察来查找 Telerik、SharePoint 或 DNN 密钥。
  9. 依赖
    SafeControls
    的 web.config 条目来限制反序列化。
    SharePoint 的
    <SafeControl>
    列表决定了
    Picker.aspx
    可以实例化哪些类。当仅依赖继承关系检查作为门槛时便存在绕过方式(CVE-2019-0604 系列)。

Bypass Techniques

绕过技术

DefenseBypass
__VIEWSTATEENCRYPTED
non-empty (encrypted)
Recover both decryption + validation keys from any source-code leak / config-disclosure / VS forge primitive; without keys, deserialization cannot be triggered
Request validator blocks
<
in querystring
Move payload to Cookie / Referer / JSON body / multipart filename — validator doesn't reach those contexts in classic ASP.NET
EnableViewStateMac="true"
enforced
Recover
validationKey
from web.config disclosure or
<machineKey>
AutoGenerate fingerprinting (ysoserial.net
--minify --islegacy
mode generates ViewState that passes some MAC-validation gaps)
trace.axd
localhost-only
Set
X-Forwarded-For: 127.0.0.1
if the trace mode is
localOnly
and the validation uses Request.UserHostAddress (some apps use Forwarded-For instead)
WCF
.svc
401 on anonymous
Try
?wsdl
and
?mex
first; metadata is sometimes anonymously enumerable even when service ops require auth
Telerik upload patchedCheck the Telerik version: anything pre-2017Q1 (build 2017.1.118 or earlier) is the original RAU RCE. Check 2017Q3 - 2019Q3 for CVE-2019-18935
SafeControl
whitelist enforced
Inheritance gate (
instanceof PickerDialog
) IS the gate on patched SP — bypass requires finding a SafeControl subclass with a deserialization sink; enumerate via Picker.aspx
customErrors mode="On"
(no stack traces)
Force a different error path: invalid Content-Length, malformed ViewState that triggers a parser-level exception below the customErrors handler

防御措施绕过方式
__VIEWSTATEENCRYPTED
非空(已加密)
从任何源码泄露、配置泄露或 ViewState 伪造原语中恢复 decryption key 和 validation key;如果没有密钥,则无法触发反序列化
请求验证器阻止查询字符串中的
<
将载荷移动到 Cookie、Referer、JSON 正文或 multipart 文件名——在经典 ASP.NET 中,验证器不会覆盖这些上下文
强制启用
EnableViewStateMac="true"
从 web.config 泄露或
<machineKey>
AutoGenerate 指纹识别中恢复 validationKey;
ysoserial.net
的
--minify --islegacy
模式可生成能够通过部分 MAC 验证缺口 的 ViewState
trace.axd
仅限 localhost
如果 trace 模式为
localOnly
,且验证使用 Request.UserHostAddress,可尝试设置
X-Forwarded-For: 127.0.0.1
;部分应用会改用 Forwarded-For
WCF
.svc
匿名访问返回 401
先尝试
?wsdl
和
?mex
;即使服务操作需要认证,元数据有时仍可被匿名枚举
Telerik 上传漏洞已修复检查 Telerik 版本:2017Q1 之前的任何版本(build 2017.1.118 或更早)都存在原始 RAU RCE;检查 2017Q3 至 2019Q3 是否存在 CVE-2019-18935
强制执行
SafeControl
白名单
在已修复的 SharePoint 中,继承关系门槛(
instanceof PickerDialog
)本身就是门槛;绕过需要找到一个带反序列化入口的 SafeControl 子类,可通过 Picker.aspx 枚举
customErrors mode="On"
(不显示堆栈跟踪)
强制进入其他错误路径,例如无效 Content-Length,或触发自定义错误处理器下方的解析器级异常的畸形 ViewState

Gate 0 Validation

Gate 0 验证

Before writing the report, confirm:
  1. What can the attacker DO right now with the disclosed information?
    • trace.axd
      200 with full request dump → Critical (PII / session cookies / Authorization headers exposed)
    • elmah.axd
      200 with error log → High (stack traces + internal paths + sometimes credentials)
    • __VIEWSTATEENCRYPTED
      empty + recoverable machineKey via separate finding → Critical chain to RCE
    • __VIEWSTATEENCRYPTED
      empty without key recovery → Low-Medium (primitive present, not exploitable on its own)
    • Stack traces in 500s → Low unless they include credentials / connection strings
  2. Have you reproduced the full chain to attacker-attainable impact, or only the primitive?
    • Cross-reference
      triage-validation
      Pre-Severity Gate. "Primitive confirmed" is not Critical until the chain ends in impact.
  3. Can a triager reproduce in <10 min from your report?
    • Each step copy-pasteable curl / Python.
    • For RCE chains: link the public exploit tool (ysoserial.net, viewgen, telerik-revda) and the specific gadget chain.

编写报告前,请确认:
  1. 攻击者目前可以利用已披露的信息做什么?
    • trace.axd
      返回 200 并暴露完整请求转储 → Critical(暴露 PII、Session Cookie 和 Authorization 响应头)
    • elmah.axd
      返回 200 并暴露错误日志 → High(暴露堆栈跟踪、内部路径,有时还包含凭据)
    • __VIEWSTATEENCRYPTED
      为空,且可通过单独发现恢复 machineKey → Critical,构成通向 RCE 的链
    • __VIEWSTATEENCRYPTED
      为空,但无法恢复密钥 → Low-Medium(存在原始漏洞,但单独无法利用)
    • 500 错误中包含堆栈跟踪 → Low,除非其中包含凭据或连接字符串
  2. 你复现的是攻击者可实际利用的完整影响链,还是仅确认了原始漏洞?
    • 对照
      triage-validation
      的 Pre-Severity Gate。除非完整链最终能够产生实际影响,否则仅确认原始漏洞不能评为 Critical。
  3. 审核人员能否根据报告在 10 分钟内完成复现?
    • 每个步骤都应可以通过复制粘贴运行 curl 或 Python 命令。
    • 对于 RCE 链:提供公开漏洞利用工具(ysoserial.net、viewgen、telerik-revda)链接及具体 gadget 链。

Real Impact Examples

真实影响示例

Scenario A — Signed-only ViewState + permissive ToolPane on EoL SharePoint 2013

场景 A——EoL SharePoint 2013 上的仅签名 ViewState + 宽松 ToolPane

https://target-portal.example/_layouts/15/ToolPane.aspx?DisplayMode=Edit
returns 200 anonymously. The form contains
__VIEWSTATE
(signed only —
__VIEWSTATEENCRYPTED=""
), and
__REQUESTDIGEST
is anonymously issued via
_api/contextinfo
. Combined with SP2013 being end-of-life (no patch will ever ship), this is the canonical CVE-2025-53770 "ToolShell" precondition chain on a permanently-unpatched code path. Reported severity: Critical. The dual-parser test (Section 4 of Methodology) confirmed that XML-shaped payloads reach the legacy
ObjectStateFormatter
BEFORE MAC validation — additional evidence that the chain is reachable even without full machineKey recovery (though full RCE requires both).
https://target-portal.example/_layouts/15/ToolPane.aspx?DisplayMode=Edit
允许匿名访问并返回 200。表单包含
__VIEWSTATE
(仅签名——
__VIEWSTATEENCRYPTED=""
),并且
__REQUESTDIGEST
可通过
_api/contextinfo
匿名签发。结合 SP2013 已停止支持(不会再发布任何补丁),这构成 CVE-2025-53770 "ToolShell" 在永久未修补代码路径上的典型前置条件链。报告严重级别为 Critical。双解析器测试(方法第 4 节)确认,XML 形态载荷会在 MAC 验证之前进入旧版
ObjectStateFormatter
——这是该链即使在没有完整恢复 machineKey 的情况下也可达的额外证据(不过完整 RCE 仍需要两者)。

Scenario B — Telerik RadAsyncUploadHandler exposed on legacy bank portal

场景 B——传统银行门户暴露 Telerik RadAsyncUploadHandler

/Telerik.Web.UI.WebResource.axd?type=rau
returns the Telerik upload handler. Telerik version (visible in JS bundle metadata) is 2016.3.1027. CVE-2017-11317 applies — keys are baked into the public Telerik DLL of that version. Upload → write
aspx
to
/app_data/
→ request → RCE. Reported severity: Critical.
/Telerik.Web.UI.WebResource.axd?type=rau
返回 Telerik 上传处理器。JS Bundle 元数据中可见的 Telerik 版本为 2016.3.1027。CVE-2017-11317 适用于该版本——密钥已内置在公开提供的 Telerik DLL 中。上传 → 将
aspx
写入
/app_data/
→ 发起请求 → RCE。报告严重级别为 Critical。

Scenario C — trace.axd + elmah.axd both exposed on enterprise HR portal

场景 C——企业 HR 门户同时暴露 trace.axd 和 elmah.axd

trace.axd
200 returns 50 most recent requests, including
Authorization: Bearer eyJ...
headers on API requests.
elmah.axd
200 returns full error log with database connection-string in one of the exceptions. Reported severity: Critical (credentials in plaintext to anonymous internet).

trace.axd
返回 200,并暴露最近 50 个请求,其中包括 API 请求的
Authorization: Bearer eyJ...
响应头。
elmah.axd
返回 200,并暴露完整错误日志,其中一个异常包含数据库连接字符串。报告严重级别为 Critical(明文凭据暴露给匿名互联网用户)。

Related Skills & Chains

相关 Skills 与攻击链

  • hunt-rce
    — ViewState deserialization is the headline ASP.NET RCE path; signed-only ViewState + leaked machineKey = RCE every time. Chain primitive: ASP.NET ViewState dual-parser MAC-bypass anti-pattern detected (signed but not encrypted,
    <%@ Page enableViewStateMac="true" viewStateEncryptionMode="Never" %>
    ) + machineKey recovered (from web.config disclosure,
    elmah.axd
    , source leak, or GitHub) →
    hunt-rce
    ysoserial.net
    TypeConfuseDelegate
    gadget → arbitrary command in
    w3wp.exe
    worker-process identity.
  • hunt-sharepoint
    — SharePoint farms inherit every ASP.NET anti-pattern plus their own surface. Chain primitive: ASP.NET fingerprint reveals SharePoint (X-SharePoint headers +
    /_layouts/
    reachable) → pivot to
    hunt-sharepoint
    for SP-specific RCE paths (ToolShell, SafeControl reflection) before generic ViewState attack.
  • hunt-ntlm-info
    — IIS sites that advertise NTLM/Negotiate anonymously leak AD topology. Chain primitive: ASP.NET app behind IIS with
    WWW-Authenticate: NTLM
    →
    hunt-ntlm-info
    Type-2 challenge capture → internal forest name → cross-reference Entra tenant via
    m365-entra-attack
    discovery.
  • hunt-file-upload
    — Telerik RadAsyncUpload, Kentico, Umbraco, and DotNetNuke all have historical upload-handler RCE. Chain primitive: ASP.NET CMS fingerprinted →
    hunt-file-upload
    bypass matrix against the CMS upload handler →
    .aspx
    written into web-accessible path → request → RCE under app-pool identity.
  • triage-validation
    —
    trace.axd
    /
    elmah.axd
    disclosure is only Critical when it actually leaks live credentials/tokens; pure stack traces are usually Low. Chain primitive: pull every reported finding through
    triage-validation
    7-Question Gate before submission — distinguish "verbose error" (informational) from "live bearer token in error log" (Critical) before writing the report (
    redteam-report-template
    ).
  • hunt-rce
    ——ViewState 反序列化是 ASP.NET RCE 的首要路径;仅签名 ViewState + 泄露的 machineKey = 每次都能实现 RCE。链式原语:检测到 ASP.NET ViewState 双解析器 MAC 绕过反模式(已签名但未加密,
    <%@ Page enableViewStateMac="true" viewStateEncryptionMode="Never" %>
    )+ 已恢复 machineKey(来自 web.config 泄露、
    elmah.axd
    、源码泄露或 GitHub)→
    hunt-rce
    ysoserial.net
    TypeConfuseDelegate
    gadget → 以
    w3wp.exe
    工作进程身份执行任意命令。
  • hunt-sharepoint
    ——SharePoint 集群继承了所有 ASP.NET 反模式,并拥有自己的攻击面。链式原语:ASP.NET 指纹识别发现 SharePoint(存在 X-SharePoint 响应头且
    /_layouts/
    可访问)→ 转向
    hunt-sharepoint
    排查 SP 特有的 RCE 路径(ToolShell、SafeControl 反射),再执行通用 ViewState 攻击。
  • hunt-ntlm-info
    ——允许匿名使用 NTLM/Negotiate 的 IIS 站点会泄露 AD 拓扑。链式原语:ASP.NET 应用位于 IIS 后面并提供
    WWW-Authenticate: NTLM
    →
    hunt-ntlm-info
    捕获 Type-2 challenge → 获取内部 forest 名称 → 通过
    m365-entra-attack
    侦察关联 Entra 租户。
  • hunt-file-upload
    ——Telerik RadAsyncUpload、Kentico、Umbraco 和 DotNetNuke 均存在历史遗留的上传处理器 RCE。链式原语:完成 ASP.NET CMS 指纹识别 → 使用 CMS 上传处理器绕过矩阵 → 将
    aspx
    写入 Web 可访问路径 → 发起请求 → 以应用池身份实现 RCE。
  • triage-validation
    ——
    trace.axd
    /
    elmah.axd
    信息泄露只有在实际泄露有效凭据或 Token 时才是 Critical;单纯的堆栈跟踪通常为 Low。链式原语:提交前将每个发现都经过
    triage-validation
    的 7 问 Gate——在编写报告(
    redteam-report-template
    )前,区分“冗长错误”(信息级别)与“错误日志中的有效 Bearer Token”(Critical)。