原理
以制作免杀马为例:
在制作免杀马的过程, 根据 PHP 的语言特性对字符进行! 运算会将字符类型转为 bool 类型, 而 bool 类型遇到运算符号时, true 会自动转为数字 1,false 会自动转为数字 0, 如果将 bool 类型进行计算, 并使用 chr()函数转为字符, 使用 "." 进行连接, 便可以绕过 preg_match 匹配.
详情了解 PHP 不同于其他语言部分
但是很多的 preg_match 会过滤掉 ".", 所以需要使用异或运算进行绕过, 很多的免杀马都是这样制作的. PHP 对字符进行异或运算是先将字符转换成 ASCII 码然后进行异或运算, 并且 PHP 能直接对一串字符串进行异或运算, 例如 "123"^"abc" 是 "1" 与 "a" 进行异或然后 "2" 与 "b" 进行异或, 以此类推, 在异或结束后就获得了想要的字符串.
注意点: 进行异或运算时要将数字转换成字符形式, 如果数字 (int) 和字符异或的话, 结果只会是数字, 例如 1^"a"=1,"a"^2=2, 将数字转换成字符串可以使用 trim()函数.
拓展:
PHP 特性 use of undefined constant, 会将没有引号的字符都自动视为字符串, ASCII 码大于 0x7F 的都会被当作字符串, 由此可知可以简化异或过程, 任何字符与 0xff 异或都会取相反, 这样就能减少运算量了.
以 GET 或 POST 传入字符绕 preg_match 为例:
PHP 的 eval()函数在执行时如果内部有类似 "abc"^"def" 的计算式, 那么就先进行计算再执行, 我们可以利用再创参数来实现更方便的操作, 例如传入? a=$_GET[b], 由于 b 不受限制就可以任意传值了, 不过
注意 1: 在测试过程中发现问题, 类似 phpinfo(); 的, 需要将后面的 (); 放在第个参数的后面, 例如 url?a={_GET}{b}();&b=phpinfo, 也就是? a=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo, 在传入后实际上为 ${????^????}{?}(); 但是到了 eval() 函数内部就会变成 ${_GET}{?}(); 成功执行.
注意 2: 测试中发现, 传值时对于要计算的部分不能用括号括起来, 因为括号也将被识别为传入的字符串, 可以使用 {} 代替, 原因是 PHP 的 use of undefined constant 特性, 例如 ${_GET}{a}这样的语句 PHP 是不会判为错误的, 因为 {} 使用来界定变量的, 这句话就是会将_GET 自动看为字符串, 也就是 $_GET['a']
- Demo
- Suctf easyphp
- <?PHP
- $hhh = @$_GET['_'];
- if (!$hhh){
- highlight_file(__FILE__);
- }
- if(strlen($hhh)>18){
- die('One inch long, one inch strong!');
- }
- if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
- die('Try something else!');
- $character_type = count_chars($hhh, 3);
- if(strlen($character_type)>12) die("Almost there!");
- eval($hhh);
- ?>
用户传入?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
成功显示 phpinfo 页面
来源: https://www.cnblogs.com/cimuhuashuimu/p/11546422.html