• Thinkphp3分析与审计


    0x00 前言:

    这篇是去年组内分享的时候给小伙伴写的0基础快速审计tp3系列的文章,主要是对架构做个分析以及审计一些sql注入漏洞~

    现在想想打算放出来,过了一年了,可能里面有一些问题,望看到的大佬不吝指教

    0x01 架构:

    应用入口文件index.php:

    if(version_compare(PHP_VERSION,'5.3.0','<'))  die('require PHP > 5.3.0 !');
    
    // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
    define('APP_DEBUG',True);
    
    // 定义应用目录
    define('APP_PATH','./Application/');
    
    // 引入ThinkPHP入口文件
    require './ThinkPHP/ThinkPHP.php';
    
    app_debug为调试模式,开启app_debug可使thinkphp报错,方便调试和报错注入
    
    thinkphp入口文件中可查看当前的框架版本
    
    const THINK_VERSION     =   '3.2.3';

    应用文件目录在Application(也可能不叫这个名字,在index.php定义应用目录),也就是开发者的应用都是写在这个文件夹里的,我们审计的部分也在这里。

    mvc架构: mvc即模型,视图,控制器

    Model(模型)是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。

    View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。

    Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

    而这个架构中我们的重点又放在controller里面,因为参数的传递是否可控通常是决定是否存在漏洞的根本因素。还有一个原因则是目前很多cms有一个问题就是完全的弱化了model层,controller层不但做了数据的接收,也同样处理了数据逻辑,和数据库进行交互。

    因此一般来说,我们拿到一套thinkphp框架的cms时,逻辑应该是:

    1.在index.php中开启debug方便调试,确认应用目录。
    
    2.确定应用前台模块。(application的下一个目录,通常是Home,还可能存在Api和Admin。那么重点是审计Home和Api这种前台模块,主要后台注入也没啥意思...3.审计controller目录中的所有控制器文件。
    

    0x02 路由和传参

    传参:

    php中获取参数一般是,$_GET,$_POST等,框架中也可以使用这种方式传参数,但更多的是使用框架自定义的函数I函数

    I方法是ThinkPHP用于更加方便和安全的获取系统输入变量,可以用于任何地方,用法格式如下:

    I('变量类型.变量名/修饰符',['默认值'],['过滤方法'],['额外数据源'])

    example:I('id','','intval');

    这种用法是使用intval过滤id参数,这个id参数可以来自get,post,它会自动辨别。

    除了这种获取参数的方法,Thinkphp还有一种获取参数的方法,叫做参数绑定

    example:

    namespace HomeController;
        use ThinkController;
        class BlogController extends Controller{
            public function read($id){
                echo 'id='.$id;
            }
        }

    使用路由http://serverName/index.php?c=Blog&a=read&id=5即是给$id传值为5

    因此我们在查看是否存在可控参数时切不要忘记还有这种方式传参。

    路由:

    thinkphp使用URL_MODEL=2 ;这种方式来指定路由模式,1,2,3分别代表不同的路由模式 具体可参考http://document.thinkphp.cn/manual_3_2.html#url文档来查看,但依据我的经验,除了某些特殊情况,我们可以在搭好cms之后通过黑盒快速找到路由规则。

    0x03 TP常见的sql注入

    1.智障拼接导致的sql注入:

     public function show(){
            $article=M('article');
            $id=I('id');
            $article->where('id='.$id)->setInc('views');
            $data = $article->field('title,content,addtime,views')->find($id);
            $data['content']=htmlspecialchars_decode($data['content']);
            $this->ajaxReturn($data);
        }
    }

    这段代码中,$id参数被直接拼接到where方法中的id字段,导致sql注入。 I函数默认的过滤方法是htmlspecialchars,对sql注入的防御是没有作用的。 而where方法只对数组形式的参数进行过滤 具体可参考官方文档的说明: https://www.kancloud.cn/manual/thinkphp/1844
    因此此处可总结为:
    如果参数获取时没有进行有效过滤又通过字符串拼接的方式带入where方法,则基本可断定存在sql注入。
    补天会收的cms中目前已经很少存在这种智障写法了,但是非常非常小的cms里还是存在这种写法的。还有就是安服的同学们做代码审计很可能会遇到:)

    2.不能预编译的地方未作控制导致的注入:
    tp3.x系列对于这种不能预编译的语句的参数貌似没做什么处理...包括limit order filed table..etc 因为按理来说这些地方是应该写死的。。。 limit注入代码示例:

    public function read(){
        $Customer=M('Customer_abroad);
        $start = $_GET['start'];
        $limit = $_GET['limit'];
            $data['total'] = $Customer->count();
            $data['success'] = true;
            $data['message'] = '读取数据';
            $data['data'] = $Customer->order('id DESC')->limit($start,$limit)->select();
            $this->ajaxReturn($data,'JSON');
        }

    limit方法即对应sql语句中的limit,这里是没办法预编译的,thinkphp也没有对此处有防御方法,因此如果这里的参数可控就会存在limit注入
    代码来源:https://github.com/focalhot/FHCRM/blob/master/System/Lib/Action/CustomerAction.class.php(已经没了)

    表名注入代码示例:

    public function zan_collect()
        {
            $data = $this->request->param();
            $id   = $data['id'];
            $uid  = session('userid');
            if (!session('userid') || !session('username')) {
    
                return json(array('code' => 0, 'msg' => '登录后才能操作'));
            } else {
    
                //状态:
                // 0 用户 1 帖子 2 评论
                $zan_collect = $data['zan_collect'];
    
                $msgsubject                         = '';
                $zan_collect == 'zan' ? $msgsubject = '点赞' : $msgsubject = '收藏';
                $tablename                          = '';
                $type                               = $data['type'];
                switch ($type) {
                    case 1:
                        $tablename = 'forum';
    
                        break;
    
                    case 2:
    
                        $tablename = 'comment';
                        break;
    
                    case 3:
    
                        $tablename = 'article';
                        break;
    
                    default:
                        $msgsubject = '关注';
                        $tablename  = 'user';
                        break;
                }
                $zuid = $id;
                if ($type != '0') {
                    $zuid = Db::name($tablename)->where('id', $id)->value('uid');
    
                }
                if ($zuid == $uid) {
                    return json(array('code' => 0, 'res' => '', 'msg' => '不可以孤芳自赏哦'));
    
                }
    
                $insertdata['type'] = $type;
                $insertdata['uid']  = $uid;
                $insertdata['sid']  = $id;
    
                $n = Db::name($zan_collect)->where($insertdata)->find();

    这段代码的最后一行Db::name($zan_collect),如果$zan_collect是可控的,那么此处就存在一个表名注入。 另外,除了Db::name()之外,thinkphp3.2.x还使用M方法和D方法实例表,因此如果此处的写法为M($zan_collect)或者D($zan_collect)也是一样的效果。

    代码来源:laysns-v2.4 

    注:对于order by ,limit,和表名的注入,一般的防御方法时将此处的参数写死,或者使用switch case语句。

    3.不知道该怪开发还是怪框架的问题

    举个例子,3.x系列不用I函数而直接原生取值基本可以说凉凉... 这里就不细说了,太多了说起来费劲,可以看看先知某师傅写的文章https://xz.aliyun.com/t/2630 

    内容非常全面,我就不赘述了,放一个栗子大家可以练手

     https://github.com/duiying/TP3-CMS

  • 相关阅读:
    Eureka的工作原理以及它与ZooKeeper的区别
    利用javascript判断文件是否存在
    带jsk证书,请求https接口
    C# .net 数组倒序排序
    C#中ArrayList和string,string[]数组的转换
    C#中遍历ArrayList的三种方法
    求其中同一个主叫号码的两次通话之间间隔大于10秒的通话记录ID
    启动tomcat时,一直卡在Deploying web application directory这块的解决方案
    Linux下修改Mysql的用(root的密码及修改root登录权限
    启动MySql提示:The server quit without updating PID file(…)失败
  • 原文地址:https://www.cnblogs.com/escape-w/p/11290034.html
Copyright © 2020-2023  润新知