3rsh1
/single dog/college student/ctfer
3rsh1's Blog

php反序列化

反序列化

序列化就是把变量的数据转化为字符串以便于保存,目的是将数据存储在本地,因此这就叫数据序列化(变量持久化)。这样的话在程序间传输数据会比较方便。其实也是对象的另外一种表现形式。

相反的行为就是反序列化,把序列化之后的内容转化为代码片段。

serialize(mix value):string //产生一个可存储值的表示,即序列化
unserialize(string $str):mixed //从存储的表示中创建php的值,即反序列化
<?php
class test1{
    public data='first';
    public function print_data(){
        echothis->data;
    }
}

a=new test1();a->print_data();
?>

对象就像电脑。电脑类内有无数台电脑对象,创建一个个人电脑即看作类的一个对象。序列化此对象就像拷贝电脑的数据。而序列化则是利用电脑的数据在一定的条件下还原此电脑。数据内的特点也会体现在此台新电脑上。

O:4:"user":2:{s:3:"age";i:18;s:4:"name";s:3:"neo";}
o       //对象
4       //类名的长度
"user"  //类名
2       //两个变量
s       //字符串
3       //变量名的长度
"age"   //变量名
i       //变量值的类型 int类型
18      //变量的值 18 ,如果只是int类型的话好像没有表名值的长度
s:4:"name"; //同上
s:3:"neo"   //string类型的值,长度为3,值为neo。

序列化之后的值中没有提到函数。似乎更接近本质,也没有当初序列化的变量名,也就意味着反序列化后的数据可以付给一个任意的变量。

<?
class user{
    public age=0;
    publicname='';
    function print_data(){
        echo "this->name isthis->age years old!"."\n";
    }
}

a=new user();a->name='candy';
a->age=17;a->print_data();//candy is 17 years old!
#echo serialize(a);c=unserialize('O:4:"user":2:{s:3:"age";i:18;s:4:"name";s:3:"neo";}');
$c->print_data();//neo is 18 years old!
?>

对于序列化和反序列化的过程有种脱衣服然后再穿衣服的感觉,但是本质上都是同一个人,本质上都是相同的数据。

魔术方法:

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_fbc0947360ce4f57096a94d9dbf899b3.jpg

反序列化结束后会额外调用一次析构函数,脚本结束后会再调用一次析构函数。

补充以下:

__toString()        //在echo 输出对象前会自动调用对象所属类的此函数。

附加一个学习链接:

https://www.bbsmax.com/A/VGzlV41V5b/
<?php
class user{
    public age=0;
    publicname='';
    function print_data(){
        echo "this->name isthis->age years old!"."\n";
    }
    function __toString(){
        return "__tostring!"."\n";
    }
}
a=new user();a->name='candy';
a->age=17;a->print_data();
echo $a;//会输出__tostring!

注意点:

private变量和protect变量序列化的话:

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_d5a9354851f6205141b642db29a46f47.jpg
private变量的变量名序列化之后会变成 \x00+类名+\x00+变量名
protected变量的变量名序列化之后会变成 \x00+*+\x00+变量名
如果不说明的话默认public 就是普通的变量名
其中的\x00 就是十六进制的0x00

还有一点就是O之后的4因为是int整数,那么也可以表示为+4;在url编码中+会被认为是个空格,这个在sql注入的时候有体现,所以传递加号的时候需要进行url编码。

phpbug72663

如果在序列化字符串中,对象属性的个数大于真实对象属性的个数。__wakeup()函数的执行就会被跳过。

php_session序列化以及反序列化:

虽然没有相关函数但是还是会进行序列化以及反序列化的操作。php内置了多种处理器用于存取$_session数据时对其进行序列化和反序列化,常用的有以下三种:

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_21fabe6e636af39b8f0fad5dbad75884.jpg
<?php
ini_set('session.serialize_handler','php');//设置处理器,php_serialize,php_binary
session_start();//新建一个session
_SESSION['a']=_GET['a'];//

如果php在存储数据时序列化时的处理器和反序列化的处理器不同的话,就会导致数据无法正确反序列化,因此可以对序列化字符串进行一些构造来实现数据伪造。要注意session_start()函数会对session内原有的数据进行一个反序列化。

session.auto_start=off时,两个脚本注册session会话时使用的处理器不同就会有安全问题。

如果等于on的话,在代码还未执行前就已经进行了自动创建session的操作,代码中设置的处理器不会生效。此时可能需要先删除自动创建的session会话,再调用session_start函数设置新的session。当处理器和php.ini配置中的处理器不同时就会产生安全问题。

复现过程中奇怪的一点是当本机是php_serialize时,代码设置为php会导致文件上传不上去。

主要是利用__wakeup()函数或者__tostring()或者__destruct()函数。引用session的过程中会进行反序列化的操作。貌似key点是不是session_start()函数。经证可得,需要有引用session的文件才可实现session的反序列化。

脚本如下,上传文件,生成缓存文件。session反序列化可用:

<!doctype html>
<html>
<body>
    <form action="http://localhost/phpinfo.php" method="POST" enctype="multipart/form-data">
    <h3>test upload tmp file</h3>
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ryat"/>
    <input type="file" name="file"/>
    <input type="submit" />
</form>
</body>
</html>

phar反序列化:

在使用phar伪协议读文件的时候,文件会被解析成phar对象,不需采用unserialize()函数就可对其meta data信息进行反序列化处理。

phar文件会以序列化的形式存储meta data的内容,配合phar://伪协议可以在不依赖unserialize()的情况下进行反序列化的操作。

原理分析:

phar文件结构:

主要由四个部分组成:

stub:

可以理解为一个标志,xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>结尾。否则会识别不出来这是一个phar文件。

manifest describing the contents:

因为phar文件也是一个压缩文件。其内肯定也会有很多部分。这个部分的信息描述的是各个部分的权限属性等信息。meta data就在这部分中。

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_40719efe4708627fed02e8b5940efc70.jpg

the file contents:

被压缩文件的内容。

[optional] a signature for verifying Phar integrity (phar file format only):

签名,放在文件末尾。

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_d17bd91ed6c39fe416ce2add24edd29c.jpg

demo:

<?php
/**
 * @Author: Ershisan
 * @Date:   2020-03-18 18:00:15
 * @Last Modified by:   Ershisan
 * @Last Modified time: 2020-03-18 18:21:39
 */
ini_set("phar.readonly","Off");
class test{
}
@unlink("phar.phar");//删除已经存在的phar.phar文件
phar=new Phar('phar.phar');//新建一个phar对象phar->startBuffering();//开始phar写操作
phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stubo=new test();
phar->setMetadata(o);
phar->addFromString('test.txt','hahahaha');//以字符串的形式添加到phar文件内,test.txt是phar中的文件路径,第二个参数是文件内容phar->stopBuffering();//停止phar写操作,并保存。
?>

大部分php的文件系统函数在利用phar伪协议读取文件的时候,会将meta-data块的内容反序列化。如下是受影响函数列表。

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_bd3d7443f83338f4a92d1edb1ba77381.jpg
<?php
    class test {
        public function __wakeup() {
            echo 'Wakeup called';
        }
    }

filename = 'phar://phar.phar/test.txt';
#file_get_contents(filename);
#fopen(filename,'r');
#fileatime(filename);
readfile($filename);//Destruct called
?>

上面的demo需要注意的是,读取的phar文件内的压缩的文件,并非读取的整个phar压缩包。

phar识别的标志是前面的__HALT_COMPILER(); ?>这部分内容,即只要有这部分内容即可。我们也可以在前面添加其他文件的文件头来伪造别文件。可以绕过大部分检测。

<?php
/**
 * @Author: Ershisan
 * @Date:   2020-03-18 18:00:15
 * @Last Modified by:   Marte
 * @Last Modified time: 2020-03-18 19:03:15
 */
ini_set("phar.readonly","Off");
class test{
}
@unlink("phar.phar");
phar=new Phar('phar1.phar');phar->startBuffering();
phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stubo=new test();
phar->setMetadata(o);
phar->addFromString('test.txt','hahahaha');phar->stopBuffering();


?>
https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_0df20c37f2e80080e72d12218aee8323.jpg

还有一些高深的东西:

https://seaii-blog.com/index.php/2018/08/23/86.html
https://www.anquanke.com/post/id/157439
https://www.codercto.com/a/38202.html

发表评论

textsms
account_circle
email

3rsh1's Blog

php反序列化
反序列化 序列化就是把变量的数据转化为字符串以便于保存,目的是将数据存储在本地,因此这就叫数据序列化(变量持久化)。这样的话在程序间传输数据会比较方便。其实也是对象的另外一种…
扫描二维码继续阅读
2020-04-20