从RCTF2018_r-cursive得到的一些收获

open_basedir

介绍

open_basedir是PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法,所有PHP中有关文件读、写的函数都会经过open_basedir的检查。open_basedir实际上是一些目录的集合,在定义了open_basedir以后,php可以读写的文件、目录都将被限制在这些目录中。
设置open_basedir的方法,在linux下,不同的目录由”:”分割,如"/var/www/:/tmp/";在Windows下不同目录由”;”分割,如"c:/www;c:/windows/temp"enter description here这里我把它设成了/tmp/var/www/html/sandbox两个目录,这时候我们无论是从sandbox的文件读取或者是直接访问其他目录的文件,都是不行的。enter description here

r-cursive

这道题目好多个月前的了,不过那时候我太菜了,不知道这道题目的质量高,现在蓦然回首,发现很不错。题目代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?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的沙盒中,打开题目,我们就能看到源码

1
2
3
 <?php
sha1($_SERVER['REMOTE_ADDR']) === 'f6e5575f93a408c5cb709c73eaa822cb09b4d0f7' ?: die();
';' === preg_replace('/[^\W_]+\((?R)?\)/', NULL, $_GET['cmd']) ? eval($_GET['cmd']) : show_source(__FILE__);

这个正则表达式是PHP中的一种递归模式,匹配形如a(b(c()))的字符串,然后在最后加一个;。题目还存在一个open_basedir的限制,我就用我本地的环境来模拟。首先phpinfo()肯定可以执行enter description here通过phpinfo我们也能找到open_basedir的相关信息enter description here接下来就要思考可以去执行什么命令,不能有参数,说明很多函数都是无法执行的。这里用的方法是eval(end(getallheaders())),其中end可以替换为其他函数,反正getallheaders()返回的是一个数组,就看你使用的header在什么位置。然后就可以构造header来进行任意命令执行。题目还仅用了执行系统命令,经过测试,系统命令是不受open_basedir的限制的。由于限制的存在,当我们想列出上一个目录的时候,报错了。enter description hereenter description here所以首先,我们需要绕过限制来列目录,参考了这篇文章用到的payload是这样的$file_list = array();$it = new DirectoryIterator("glob:///*");foreach($it as $f) {$file_list[] =$f->__toString();}print_r($file_list);成功列出根目录enter description here网站根目录enter description here假设我们的flag是网站根目录下的flag.php,要怎么去读到他呢?
这时候我们要想到一个问题,它是怎么去设置open_basedir的?在这道题目里面,每个ip都被分配到了一个根据你的ip sha1加密后的目录,都是独立的,如果这样的话,通过apache静态配置是不可行的,只能用php去动态设置。这里它用到的方法是用auto_prepend_fileenter description here它利用auto_prepend_file去载入/var/www/sandbox/init.php来达到动态设置open_basedir的目的。还可以发现,配置里使用了mod_vhost_alias模块enter description here看一下文档里对这个模块的描述enter description here又因为题目里的Host是长成这样的Host: 39093088bf9a9d33d5dd5b973cc1232e2145ee49.sandbox.r-cursive.ml,联想到,有可能动态设置open_basedir的时候是通过Host去获取的,所以,这里把Host头的39093088bf9a9d33d5dd5b973cc1232e2145ee49.sandbox去掉,就达成了沙箱逃逸的目的。然后拿flag即可enter description here