在学习opcache的时候,看到了这个题目,刚好有环境,就来复现一下,这个题目也让我学到了很多。

创建镜像:

docker build -t 0ctf-ezdoor .

启动容器:

docker run -itd -p 9010:80 --name 0ctf-ezdoor 0ctf-ezdoor

源码如下:

<?php

error_reporting(0);

$dir = 'sandbox/' . sha1($_SERVER['REMOTE_ADDR']) . '/';  //创建一个用户沙盒
if(!file_exists($dir)){
mkdir($dir);
} //每次访问页面不存在该目录时都将重新创建,
if(!file_exists($dir . "index.php")){
touch($dir . "index.php"); //如果index.php不存在,则直接用touch创建
}
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信息
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"];
if (preg_match("/[^a-zA-Z0-9.\/]/", $name) ||
stristr(pathinfo($name)["extension"], "h")) { //取文件的后缀并且过滤了h,
则所有php后缀都不能上传后面的stristr(pathinfo)是用来判断以“.”隔断后的字符串中是否含有“h”字符,在这里pathinfo是以字符串中最后一个“.”来进行隔断。
break;
}
move_uploaded_file($_FILES['file']['tmp_name'], $name);
$size = 0;
foreach (scandir($dir) as $file) {
if (in_array($file, [".", ".."])) {
continue;
}
$size += filesize($dir . $file);
}
if ($size > 100000) {
clear($dir);
}
break;
case 'shell':
ini_set("open_basedir", "/var/www/html/$dir:/var/www/html/flag");
include $dir . "index.php";
break;
default:
highlight_file(__FILE__);
break;
}

最终包含的是$dir."index.php",并且无法上传php后缀,最后有include,那么应该是包含shell,所以我们如果能够通过上传覆盖index.php,就能够getshell,然后根据给出的flag路径去读flag

此时首先读一下phpinfo,这个是个txt的phpinfo信息,并且里面删除了一些配置项, 在里面发现opcache是开启的,

预期解法:

opcache突破口

A网站的网页index.php具有缓存文件index.php.bin
而访问index.php的时候加载缓存index.php.bin
倘若这时候具有上传,我们可以覆盖index.php.bin
是不是就会加载我们的恶意文件了呢?
题目中虽然过滤php类型的结尾,但是却未过滤bin的结尾

通过opcache.file_cache可以看到opcache的存储路径信息在/tmp/cache下

执行docker exec -it bash_name bash 进入docker容器发现实际上目录是cache/systemid,就是每个用户都会有一个id,来鉴别的

所以我们的目的很明确,就是去覆盖此index.php.bin来上传我们自己的index.php.bin,那么当再次访问index.php.bin时实际上就是访问的我们的恶意index.php文件

所以首先要知道服务器缓存文件的目录,计算一下服务器端的systemid,利用https://github.com/GoSecure/php7-opcache-override,因为代码需要指定一个phpinfo的页面,但是其最终解析出来进行计算的一些配置项才是最重要的,因此找到目标服务器的这些配置项

php_version=7.0.28

zend_extension_id=API320151012,NTS

zend_bin_id由这两部分组成

即zend_bin_id=BIN_SIZEOF_CHAR48888

接下来利用公式计算一下就能得到:

systemid=7badddeddbd076fe8352e80d8ddf3e73

但是这个systemid计算出来的跟我在docker容器里面看到的名字不一样,这里查看一下php的版本,题目给的是7.0.28,但是此时我复现的时候从hub库拖过来的php7版本是7.0.33,因此这里计算systemid时要和题目的php版本一致,这里把php的版本改成7.0.33计算一下就行了,得到system_id为0b8bd94e9858e5d32d058dc0acf75014

和我docker是相符的,说明没问题,此时已经得到了服务器端的opcache路径,那么下一步就是通过上传去覆盖此index.php.bin

opcache文件生成

首先要在本地搭一个根目标服务器一样的环境,所以pull一个环境下来:

sudo docker pull php:7.0.-apache

然后通过镜像创建容器:

docker run -itd -p : --name php:7.0.-apache opcache

此时容器已经起来了,进入配置与服务器相同的路径

docker exec -it opcache bash

因为我们的index.php在用户的沙盒中,因此可以使用pwd先看看路径

可以看到路径为sandbox/fac849dc498b60000e200f3f2a2712b54da39b92/,所以首先新建一个文件夹吧,然后开一个index.php,先看看能不能成功

此时文件生成了,需要配置一下php.ini的opcache

直接从docker hub拉来的镜像我发现里面没有加载php.ini,但是看到加载php.ini的路径为/usr/loca/etc/php,所以去这个目录看看,

应该是给了两个ini,选定一个更名为php.ini让apache去加载,所以cp拷贝一份就行了,我配置了如下选项:

zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20151012/opcache.so  //默认存在该扩展,只要把so文件引进来即可
opcache.file_cache => /tmp/cache => /tmp/cache
opcache.enable => On => On
opcache.validate_timestamps => On => On
opcache.file_cache_only => =>

然后重启一下apache,这里不能用service apache2 restart,容器会断掉,因为容器相当于一个cmd环境,重启自己会断,这里用reload重新加载一下配置文件,此时刷新phpinfo看看

opcache扩展也打开了,访问我们模拟环境的index.php试试生成bin文件:

此时,因为目标服务器timestamps为on,因此我们不仅要置换bin里面的systemid还要置换一下timestamps

运行以下代码就能获得最新的文件时间戳:

import requests
print requests.get('http://127.0.0.1:8585/index.php?action=time').content
print requests.get('http://127.0.0.1:8585/index.php?action=reset').content
print requests.get('http://127.0.0.1:8585/index.php?action=time').content

此时只要用010修改一些systemid和timestamps,直接使用https://github.com/GoSecure/php7-opcache-override中提供的模板文件来帮助我们解析bin文件,此时就能看到要修改的两个字段

修改以后保存,然后本地构造上传表单,进行上传,因为我们想要覆盖目标服务器的bin文件,那么路径必须与其相同才行,这里直接将$_GET['name']与沙盒路径拼接在了一起,没有对变量进行过滤

所以此时确定路径可以进行路径穿越,可以穿越到任意目录,所以可以直接通过systemid来构造路径为:

../../../../../tmp/cache/0b8bd94e9858e5d32d058dc0acf75014/var/www/html/sandbox/fac849dc498b60000e200f3f2a2712b54da39b92/index.php.bin
<html>
<body>
<form action="http://127.0.0.1:8585/?action=upload&name=../../../../../tmp/cache/0b8bd94e9858e5d32d058dc0acf75014/var/www/html/sandbox/fac849dc498b60000e200f3f2a2712b54da39b92/index.php.bin" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="upload" />
</form>
</body>
</html>

然后上传我们的bin文件,此时再访问action=shell,来触发index.php加载我们bin文件

由上图可以看到此时已经成功加载了我们的bin文件,我们继续读一下/var/www/html和/var/www/html/flag目录,可以看到服务器做了限制,只能读到flag目录有个奇怪的文件,可以读一读它

接下来读一下这个文件

将其base64解码以后存到本地的flag.php.bin文件中,拖到010进行分析,发现解析出来其systemid出现了错误,对比一下正常的bin文件发现其头部少了一个字节00

所以在其magic头部补充一个字节就行了,此时systemid还原正常,其它字段的值也正常了,接下来就要让bin文件还原成我们可以阅读的代码或者语言,做到这web部分结束,暂时不往下看了==

非预期解:

1.通过条件竞争

因为pathinfo会获取最后一个点之后的扩展,通过index.php/. 就可以绕过pathinfo,但是move_uploaded_file这个函数在调用stat检测到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); //然后删除目录
}

删除时,先删除目录中的文件,再删除目录,那么我们知道如果目录里面有文件调用rmdir将无法删除,所以我们可以给要删除的目录传递大量没用的文件,那么在还在scandir的for循环结束时,原有的正常的index.php

也被删除了,此时沙盒中有还有无用文件,不能删除此沙盒目录,因此我们可以再上传自己的index.php,然后以此来getshell

2.绕过php底层文件操作函数

x/../index.php/. 直接将路径修改为该路径,就可以覆盖原来的index.php,因为首先php调用tsrm_realpath去掉/.将其转换为一个标准路径,然后调用lstst获取文件属性,也就是判断文件存不存在,不存在将写文件,x/../index.php将绕过lstat让其认为index.php不存在所以重新写入,所以可以getshell

参考:

https://www.kingkk.com/2018/04/2018-0ctf-ezdoor%E5%88%86%E6%9E%90/#%E5%8F%A6%E4%B8%80%E7%A7%8D%E9%AA%9A%E6%93%8D%E4%BD%9C

https://www.cdxy.me/?p=790

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://lorexxar.cn/2016/05/27/opcache-jcfx/

http://wonderkun.cc/index.html/?p=626

https://skysec.top/2018/04/11/0ctf-ezdoor/#%E9%A2%84%E6%9C%9F%E8%A7%A3

http://elssm.top/2018/05/04/2018-0ctf-ezdoor%E5%A4%8D%E7%8E%B0/

https://altman.vip/2018/10/10/0ctf-Ezdoor/#%E6%89%A7%E8%A1%8C%E5%91%BD%E4%BB%A4

https://www.angelwhu.com/blog/?p=438

0ctf-ezdoor-复现分析的更多相关文章

  1. 路由器漏洞复现分析第三弹:DVRF INTRO题目分析

    这个项目的目的是来帮助人们学习X86_64之外其他架构环境,同时还帮助人们探索路由器固件里面的奥秘. 本文通过练习DVRF 中INTRO 部分的题目来学习下MIPS 结构下的各种内存攻击. DVRF: ...

  2. CVE-2021-3129:Laravel远程代码漏洞复现分析

    摘要:本文主要为大家带来CVE-2021-3129漏洞复现分析,为大家在日常工作中提供帮助. 本文分享自华为云社区<CVE-2021-3129 分析>,作者:Xuuuu . CVE-202 ...

  3. ref:spring-data-XMLBean XXE复现分析

    ref:https://blog.spoock.com/2018/05/16/cve-2018-1259/ 漏洞信息 看pivotal发布的漏洞信息如下 通过发布的漏洞信息可以知道,漏洞组件是在XML ...

  4. 路由器漏洞复现分析第二弹:CNVD-2018-01084

    1月17日,CNVD公开了D-LinkDIR 615/645/815 service.cgi远程命令执行漏洞(CNVD-2018-01084),freebuf上有前辈写了一篇漏洞复现和poc的文章(h ...

  5. Wordpress4.9.6 任意文件删除漏洞复现分析

    第一章 漏洞简介及危害分析 1.1漏洞介绍 WordPress可以说是当今最受欢迎的(我想说没有之一)基于PHP的开源CMS,其目前的全球用户高达数百万,并拥有超过4600万次的超高下载量.它是一个开 ...

  6. CVE-2020-7961 Liferay Portal 复现分析

    漏洞说明: Liferay是一个开源的Portal(认证)产品,提供对多个独立系统的内容集成,为企业信息.流程等的整合提供了一套完整的解决方案,和其他商业产品相比,Liferay有着很多优良的特性,而 ...

  7. java反序列化——apache-shiro复现分析

    本文首发于“合天智汇”公众号 作者:Fortheone 看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了.分析调试的shiro也是直接使用了cc链.首先先了解一些 ...

  8. 追洞小组 | fastjson1.2.24复现+分析

    出品|MS08067实验室(www.ms08067.com) 本文作者:爱吃芝士的小葵(Ms08067实验室追洞小组成员) 1.靶场搭建 2.漏洞复现 3.漏洞分析 4.漏洞修复 5.心得 靶场搭建 ...

  9. 泛微OA E-cology(CNVD-2019-32204)远程命令执行漏洞复现分析

    漏洞复现 影响版本: E-cology 7.0 E-cology 8.0 E-cology 8.1 E-cology 9.0   直接在网站根目录后加入组件访问路径 /weaver/bsh.servl ...

  10. struts2漏洞复现分析合集

    struts2漏洞复现合集 环境准备 tomcat安装 漏洞代码取自vulhub,使用idea进行远程调试 struts2远程调试 catalina.bat jpda start 开启debug模式, ...

随机推荐

  1. jQuery Validation ,调用valid方法时,不验证remote

    1.问题描述 model代码如下: remote对应的action如下: view代码如下: 单击按钮时,执行如下动作 当点击按钮时,我们发现,虽然后台action验证失败,但 还是执行返回true, ...

  2. ::selection伪元素改变文本选中颜色

    在看张鑫旭大神的文章,刚刚发现了这个,感觉应该后面有用,先存起来. 效果如下: 代码如下: <!DOCTYPE html> <html> <head> <me ...

  3. 1.IOC原理模拟

    Spring两大核心功能,IOC(Inverse  of  Control)  和 AOP(Aspect-Oriented-Programming) IOC原理模拟: 有这样一个beans.xml: ...

  4. 【SpringBoot】自动配置

    一.取值 1.1 @Value 1.2 ConfigurationProperties 二.导入配置文件 2.1 @PropertySource 三.配置文件的加载 3.1 默认的加载顺序 3.2 外 ...

  5. 【2】Git仓库

    一.获取 Git 仓库 初始化仓库 ##基于当前目录初始化仓库 $ git init ##指定demo目录初始化仓库 $ git init demo 克隆现有仓库 ##克隆现有的仓库,默认目录名:li ...

  6. easyUI 布局

    Layout(布局) 布局容器有5个区域:北.南.东.西和中间.中间区域面板是必须的,边缘的面板都是可选的. 每个边缘区域面板都可以通过拖拽其边框改变大小,也可以点击折叠按钮将面板折叠起来.布局可以进 ...

  7. Hadoop_28_MapReduce_自定义 inputFormat

    1. 自定义inputFormat 1.1.需求: 无论hdfs还是mapreduce,对于小文件都有损效率,实践中,又难免面临处理大量小文件,此时就需要有相应解决方案; 1.2.分析: 小文件的优化 ...

  8. Django如何重设Admin密码(转)

      django的admin用户被我多动症一样的测试,给密码弄丢了,需要重置. 从数据库重置的可能性为0,因为django对于密码有保护策略.考虑从运行程序的地方进行重置: 1.在程序的文件夹下,执行 ...

  9. WebRTC基于GCC的拥塞控制算法[转载]

    实时流媒体应用的最大特点是实时性,而延迟是实时性的最大敌人.从媒体收发端来讲,媒体数据的处理速度是造成延迟的重要原因:而从传输角度来讲,网络拥塞则是造成延迟的最主要原因.网络拥塞可能造成数据包丢失,也 ...

  10. JLOI2016 侦查守卫

    侦查守卫 小R和B神正在玩一款游戏.这款游戏的地图由 N 个点和 N-1 条无向边组成,每条无向边连接两个点,且地图是连通的.换句话说,游戏的地图是一棵有 N 个节点的树. 游戏中有一种道具叫做侦查守 ...