安服仔的首次PHP代审实践

前言:


是的,我就是那个从20年10月就开始说要学php代审的那个人,拖了一年的时间终于开始步入正轨。
本文水得一批,谨慎阅读

前期准备:


确认目标为zbzcms,听说这套系统漏洞很多,且没有用到MVC架构,很适合小白练手。个人采取黑盒加白盒的方式进行代审,代码量不多,也可以考虑通读源码。

环境搭建 + rips审计工具 + burp配合xray + 数据库监控+ 文件监控

  • XSS:

漏洞位于:/zbzcms/cms/cms/admin/art.php

if(isset($_GET['aid'])){
    $aid=$_GET['aid'];
    $arts=$c_sql->select("select * from art where id={$aid}");
    $arts=$arts[0];
    $tid=$arts['tid'];
}
else{
    $aid=0;
    $paixu=50;
    if(isset($_GET['tid'])){$tid=$_GET['tid'];}
    else if(!isset($tid)){$tid=0;}
<form id="edit">
        <table class='from'>
            <input id="id" name="id" type="hidden" value="<?php echo $aid; ?>">
            <input id="aid" name="aid" type="hidden" value="<?php echo $glid; ?>">
            <input id="tid" name="tid" type="hidden" value="<?php echo $tid; ?>">
  • SSRF:

漏洞位于:/zbzcms/cms/cms/include/function.php

function getImage($url,$save_dir='',$filename='',$type=0){
    if(trim($url)==''){
        return array('file_name'=>'','save_path'=>'','error'=>1);
    }
    if(trim($save_dir)==''){
        $save_dir='./';
    }
    if(trim($filename)==''){//保存文件名
        $ext=strrchr($url,'.');
        if(!stristr($ext,'.gif') && !stristr($ext,'.jpg') && !stristr($ext,'.png')){
            return array('file_name'=>'','save_path'=>'','error'=>3);
        }
        $filename=strrchr($url,'/');
        $filename=str_ireplace('/','',$filename);
    }
    if(0!==strrpos($save_dir,'/')){
        $save_dir.='/';
    }
    //创建保存目录
    if(!file_exists($save_dir)&&!mkdir($save_dir,0777,true)){
        return array('file_name'=>'','save_path'=>'','error'=>5);
    }
    //获取远程文件所采用的方法
    if($type){
        $ch=curl_init();
        $timeout=5;
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
        $img=curl_exec($ch);
        curl_close($ch);
    }else{
        ob_start();
        readfile($url);
        $img=ob_get_contents();
        ob_end_clean();
    }
    //$size=strlen($img);
    //文件大小
    $fp2=@fopen($save_dir.$filename,'a');
    fwrite($fp2,$img);
    fclose($fp2);
    unset($img,$url);
    return $save_dir.$filename;

可以发现当$url可控,$type=0的时候,会执行readfile($url),且并没有对$url进行过滤处理,最后将其保存到$save_dir所在的位置。追踪所有引入了getImage函数的php文件,最终发现了如下代码:

//编辑栏目
if($run=='edit_type'){
    //下载远程图片到本地S
    preg_match_all('/<img .*?src=["|\'](.*?)["|\']/ism',$_POST['lanmuneirong'],$tupians);
    if(count($tupians[1])>0){
        foreach($tupians[1] as $v){
            if(strstr($v,'http://') || strstr($v,'https://')){
                $tupian_src=getImage($v,"../../upload");
                $_POST['lanmuneirong']=str_ireplace($v,$tupian_src,$_POST['lanmuneirong']);
            }
        }
    }
    //下载远程图片到本地E
    $where="id=".$_POST['id'];
    unset($_POST['id']);
    echo $c_sql->update('type',$_POST,$where);

我们可以通过post传入一个lanmuneirong的值并通过正则匹配执行getImage()函数,最终造成SSRF:

这里也可以利用不同浏览器的解析机制不同,造成存储型XSS

  • 任意文件读取:

漏洞位于:/zbzcms/cms/cms/admin/run_ajax.php

//文件
if($run=='wenjian'){
    $path=$_POST['path'];
    echo get($path);
}

然后定位get函数,动态变量可控且无任何过滤:

//读取文件方便编码处理
function get($path){
    if(!file_exists($path)){
        return '文件<b>'.$path.'</b>不存在!';
    }
    $get=file_get_contents($path);
    return $get;
}
  • SQL注入:

后台注入点太多了,这里就列举几例:
公共函数:

    public function digui($table,$ziduan,$tid=0,&$result=array(),$spac=0){
        $spac=$spac+1;
        $sql="select * from $table where $ziduan='$tid' order by paixu,id asc";
        $res=mysqli_query($this->link(),$sql);
        while($row=mysqli_fetch_assoc($res)){
            $row['jibie']=$spac;
            $result[]=$row;
            $row['jibie']=$spac;
            $this->digui($table,$ziduan,$row['id'],$result,$spac);
        }
        return $result; 

后台注入点一:/zbzcms/cms/cms/admin/art_list.php

if(isset($_GET['tid'])){
    $tid=$_GET['tid'];
    $tid_zis=$c_sql->select("select id from type where tid={$tid}");

后台注入点二:/zbzcms/cms/api/caiji.php

//审核并自动更新
else if($run=='shenhe'){
    $tid=$_GET['lanmu'];
    $up_arr=array('fabushijian',time());
    $ids=$c_sql->select("select id from art where (fabushijian=1 and tid={$tid}) limit 1");
    if(isset($ids[0]['id'])){$id=$ids[0]['id'];}
    else{exit('待审核文章库没有文章了!');}
    $up_arr=array('fabushijian'=>time());
    $up=$c_sql->update('art',$up_arr,"id={$id}");
    $up=1;
    if($up!=1){
        exit('审核系统出错');
    }
}
else{
    exit("run参数有误");

后台注入点三:/zbzcms/cms/cms/admin/run_ajax.php

//新增栏目提交
if($run=='type_add'){
    $_POST['fulanmumingcheng']=pinyin($_POST['lanmumingcheng'],$lx='head');
    $_POST['youhuabiaoti']=$_POST['lanmumingcheng'];
    $_POST['youhuaguanjianci']=$_POST['lanmumingcheng'];
    $_POST['youhuazhaiyao']=$_POST['lanmumingcheng'];
    $_POST['paixu']=50;
    $jibie=$_POST['jibie'];
    unset($_POST['jibie']);
    $typeid=$c_sql->insert('type',$_POST);
    
    if($_POST['tid']==0){
        $red['id']=$typeid;
        $red['tid']=$_POST['tid'];
        $red['jibie']=$jibie+1;
        $red['weizhi_id']=0;
    }
    else{
        $diguis=$c_sql->digui('type','tid',$_POST['tid'],$result); //$tid可控

后台注入点四:此处会进行delete操作

//删除联动
if($run=='delliandong'){
    $id=$_POST['id'];
    $liandongids=$c_sql->digui('liandong','lid',$id,$result,$spac=0);
    $ids[]=$id;
    if(count($liandongids)>0){
        foreach($liandongids as $arr){
            $ids[]=$arr['id'];
        }   
    }
    $id=implode(',',$ids);
    $sql="delete from liandong where id in ({$id})";
    $res=$c_sql->zhixing($sql);
    if($res==1){
        echo json_encode($ids);
    }
    else{
        exit(0);
    }
}

后台注入点五:

//增或改
if($run=='addedit'){
    $table=$_GET['table'];
    $where="id=".$_POST['id'];
    $cha=$c_sql->select("select id from $table where $where");
    if(isset($cha[0]['id'])){
        $res=$c_sql->update($table,$_POST,$where);
    }
    else{
        $res=$c_sql->insert($table,$_POST);
    }
    echo $res;
  • 任意文件删除:

触发点一:/zbzcms/cms/cms/include/function.php

/*删除目录及目录下所有文件或删除指定文件($path待删除目录路径,$delDir1或true删除目录,0或false则只删除文件保留目录*/
function delDirAndFile($path, $delDir = FALSE) {
    if($path=='' || $path=='/' || $path=='./' || $path=='../' || $path=='../../' || $path=='../../../'){
        exit('严禁该操作');
    }
    $handle = opendir($path);
    if ($handle) {
        while (false !== ( $item = readdir($handle) )){
            if ($item != "." && $item != "..")
                is_dir("$path/$item") ? delDirAndFile("$path/$item", $delDir) : unlink("$path/$item");
        }
        closedir($handle);
        if ($delDir)
            return rmdir($path);
    }else {
        if (file_exists($path)) {
            return unlink($path);
        } else {
            return false;
        }
    }
}
//删除文件
if($run=='delpath'){
    $path=$_POST['path'];
    delDirAndFile($path, $delDir = true);

$delDir为真时,会删除整个目录内容

触发点二:/zbzcms/cms/cms/include/up.php

//删除文件
if($run=='del'){
    $url=$_POST['url'];
    if(!unlink($url)){
        echo 0;
    }
    else{
        echo 1;
    }
}

触发点三:/zbzcms/cms/cms/include/upload.php

//删除
else if(isset($_GET['del'])){
    $src=$_GET['del'];
    if(!unlink($src)){
        echo 0;
    }
    else{
        echo 1;
    }
    exit;
}
  • 任意文件上传:
  • 上传点一:/zbzcms/cms/cms/admin/ajax.php

//图片上传
if($run=='youad_pic'){
    $path='../../upload/';
    $path_res='../../upload/';
    $res='';
    foreach($_FILES as $i=> $arr){
        $tmp_name=$arr['tmp_name'];//临时文件
        
        if($data_pic_name==0){
            //后缀
            $houzhuis=explode('.',$arr['name']);
            $houzhui=$houzhuis[count($houzhuis)-1];
            $pathurl=$path.time().'_'.$i.'.'.$houzhui;
        }
        else{
            $pathurl=$path.$arr['name'];
        }
        
        is_dir($path) OR mkdir($path, 0777, true);//文件夹不存在创建文件夹
        $pathurl=iconv("UTF-8","gb2312",$pathurl);//目标路径
        if(move_uploaded_file($tmp_name,$pathurl)){
            $pathurl_res=str_ireplace($path,$path_res,$pathurl);
            
            $res.=$pathurl_res;
        }
    }
    print_r($_FILES);
    echo $res;
//图片命名,0改名,1原名
if(isset($_GET['data_pic_name'])){$data_pic_name=$_GET['data_pic_name'];}
else{$data_pic_name='0';}
  • 上传点二:zbzcms/cms/cms/include/upload.php
if(isset($_FILES) && isset($_GET['path']) && isset($_GET['name']) && isset($_GET['id'])){
    $path = $_GET['path'];//文件上传根目录
    $name = $_GET['name'];//文件命名规则
    $id = $_GET['id'];//上传插件的id
    if (!file_exists($path)) {mkdir($path, 0777, true);}//上传目录不存在则创建
    $res='';//返回的参数
    $i=1;//递增的文件名
    foreach($_FILES as $arr){
        //文件名S
        if($name==1){$file_name=$arr['name'];}
        else{
            if($arr['type']=='image/png'){$file_name=time().$i.'.png';}
            if($arr['type']=='image/jpeg'){$file_name=time().$i.'.jpg';}
            if($arr['type']=='image/gif'){$file_name=time().$i.'.gif';}
        }
        //文件名E
        $tmp_name=$arr['tmp_name'];//临时文件
        $url=$path.$file_name;//新文件路径
        if(move_uploaded_file($tmp_name,$url)){
            $res.="<img id='$id{$i}00' onclick=\"del('$id{$i}00','{$id}')\" src='{$url}' />";
        }
        $i++;
    }
    print_r($_FILES);
    echo $res;

可通过动态变量$path将文件上传至任意处,当$name=1时即可上传任意文件:

  • 上传点三:zbzcms/cms/cms/include/up.php
//上传
if($run=='file'){
    $res=array();
    $path=$_GET['path'];//上传的路径
    $filename=$_GET['filename'];//1不更改,0更改
    is_dir($path) OR mkdir($path, 0777, true);//文件夹不存在创建文件夹
    foreach($_FILES as $k=>$arr){
        $path=$_GET['path'];//上传的路径
        $name=$arr['name'];//文件名
        $type=$arr['type'];//文件类型
        $tmp_name=$arr['tmp_name'];//临时文件
        $size=$arr['size'];//文件大小
        
        /*目标地址*/
        if($filename==1){
            $path.=$name;
        }
        else{
            $path.=time().$k.hz($name);
        }
        
        /*上传*/
        if(move_uploaded_file($tmp_name,$path)){
            $res[]=$path;
        }
    }
    echo json_encode($res);
}
  • 上传点四:/zbzcms/cms/cms/zbzedit/php/zbz.php
<?php 
//批量上传txt文章
if(isset($_GET['run'])){$run=$_GET['run'];}
else{exit('error');}

//图片上传路径
if(isset($_GET['path'])){$path=$_GET['path'];}
else{$path='../up';}

//图片回调路径
if(isset($_GET['path_res'])){$path_res=$_GET['path_res'];}
else{$path_res='../up';}

//图片命名,0改名,1原名
if(isset($_GET['data_pic_name'])){$data_pic_name=$_GET['data_pic_name'];}
else{$data_pic_name='0';}

if($run=='uptxt'){
    $res=array();
    foreach($_FILES as $i=> $arr){
        $tmp_name=$arr['tmp_name'];//临时文件
        
        if($data_pic_name==0){
            //后缀
            $houzhuis=explode('.',$arr['name']);
            $houzhui=$houzhuis[count($houzhuis)-1];
            $pathurl=$path.time().'_'.$i.'.'.$houzhui;
        }
        else{
            $pathurl=$path.$arr['name'];
        }
        
        is_dir($path) OR mkdir($path, 0777, true);//文件夹不存在创建文件夹
        $pathurl=iconv("UTF-8","gb2312",$pathurl);//目标路径
        if(move_uploaded_file($tmp_name,$pathurl)){
            $res[]=str_ireplace($path,$path_res,$pathurl);
        }
    }
    echo json_encode($res);//返回json
}
?>
  • 条件竞争:

漏洞位于:/zbzcms/cms/cms/admin/run_ajax.php

//批量上传txt文章
if($run=='uptxt'){
    $id=$_GET['id'];
    $chatu=$_GET['chatu'];
    $fabushijian=$_GET['fabushijian'];
    
    foreach($_FILES as $k=>$arr){
        $tmp_name=$arr['tmp_name'];//临时文件
        $path='uptxt';
        is_dir($path) OR mkdir($path, 0777, true);//文件夹不存在创建文件夹
        $pathurl=$path.'/'.$arr['name'];
        $pathurl=utf8($pathurl);
        if($arr['error']==0){
            if(!move_uploaded_file($tmp_name,iconv("UTF-8","gb2312",$pathurl))){
                move_uploaded_file($tmp_name,$pathurl);
            }
            $gml='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];//当前文件夹
            $gml=dirname($gml);
            $neirong=file_get_contents($gml.'/'.$pathurl);
            $neirong=utf8($neirong);
            $neirong='<p>'.str_ireplace("\r\n","</p><p>",$neirong).'</p>';
            if($chatu==1){
                $tuku=ii('tuku');//图库
                $tukus=explode(";",$tuku);
                $tupians=array();
                $suoluetu='';
    
                $tupianshu=rand(1,3);
                for ($x=1; $x<=$tupianshu; $x++) {
                    $k=rand(0,count($tukus)-1);
                    if($x==1){
                        $suoluetu=$tukus[$k];
                    }
                    $tupians[]="\r\n<p style='text-align:center'><img alt='{$key}' src='".$tukus[$k]."' /></p>";
                    unset($tukus[$k]);
                    $tukus = array_values($tukus);
                }
                    
                $neirongs=explode('</p>',$neirong);
                if(count($tupians)>0){
                    foreach($tupians as $k=>$v){
                        $neirongs[$k]=$neirongs[$k].$v;
                    }
                }
                $neirong=implode('',$neirongs);
            }
                
            $art['tid']=$id;
            $art['biaoti']=str_ireplace('.txt','',$arr['name']);
            $art['neirong']=$neirong;
            $art['zhaiyao']=jiequ($neirong,100);//截取摘要
            $art['suoluetu']=$suoluetu;
            $art['paixu']=50;
            $art['fabudao']=0;
            $art['zuozhe']=$_SESSION['guanliyuan'];
            if($fabushijian==0){
                $art['fabushijian']=1;
            }
            else{
                $art['fabushijian']=time();
            }
            $c_sql->insert('art',$art);
        }
    }
    delDirAndFile($path,1);
}

这里也是一处任意文件上传,但是执行到最后的时候又将其进行了删除,从文件监控日志中也可以看出来:

由于上传的路径以及文件名都是确定的,所以可以利用条件竞争的方式进行利用:

  • 任意文件写入:

漏洞位于:/zbzcms/cms/cms/admin/run_ajax.php

//文件编辑保存
if($run=='wenjian_edit'){
    $path=$_POST['path'];
    $neirong=$_POST['neirong'];
    //如果get_magic_quotes_gpc()是打开的
    if(get_magic_quotes_gpc()){
        $neirong=stripslashes($neirong);//将字符串进行处理
    }
    echo file_put_contents($path,$neirong);
}
  • 任意文件/目录重命名:

漏洞位于:/zbzcms/cms/cms/admin/run_ajax.php

//文件安全检测
if($run=='anquan_run'){
    $cms_oldname=$_POST['cms_oldname'];
    $admin_oldname=$_POST['admin_oldname'];
    $cms=$_POST['cms'];
    $admin=$_POST['admin'];
    $admin_url=ii('电脑站网址');
    
    if(stristr($admin_url,'http://') || stristr($admin_url,'https://')){$admin_url.=cms.'/';}
    else{$admin_url='http://'.$_SERVER['HTTP_HOST'].ii('电脑站网址').cms.'/';}

    if($_GET['i']==1){
        rename("../{$admin_oldname}","../{$admin}");
        echo 2;
    }
    else if($_GET['i']==2){
        if(rename("../../{$cms_oldname}","../../{$cms}")){
            echo $admin_url.$cms.'/'.$admin;//返回新后台地址
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • LFI、RFI、PHP封装协议安全问题学习 本文希望分享一些本地文件包含、远程文件包含、PHP的封装协议(伪协议)...
    Otis4631阅读 2,361评论 0 0
  • 一套实用的渗透测试岗位面试题,你会吗? 1.拿到一个待检测的站,你觉得应该先做什么? 收集信息 whois、网站源...
    g0阅读 4,833评论 0 9
  • 打算在代码审计上入下坑。本来找了个不知名cms想审计一下的。偶然间看到了星盟王叹之师傅在用php-audit-la...
    byc_404阅读 1,480评论 0 1
  • ​ 思路流程: 信息收集 服务器的相关信息(真实ip,系统类型,版本,开放端口,WAF等) 网站指纹识别(包括,c...
    黑战士安全阅读 1,383评论 0 4
  • DVWA实践 Web漏洞原理 1. DVWA环境搭建 Warmpserver+DVWA 2. Brute Forc...
    JasonChiu17阅读 3,753评论 0 19