题目
源码:
<?php
highlight_file(__FILE__);
$content = @$_GET['content'] ? "---mylocalnote---
" . $_GET['content'] : "";
$name = @$_GET['name'] ? $_GET['name'] : '';
str_replace('/', '', $name);
str_replace('\', '', $name);
file_put_contents("/tmp/" . $name, $content);
session_start();
if (isset($_SESSION['username'])) {
echo "Thank u,{$_SESSION['username']}";
}
//flag in flag.php
访问 flag.php 时提示:u are not admin,only admin can see flag!
** 本题知识点:session**
题目中需要满足 $_SESSION['username']='admin'
,并且我们可以向/tmp中写入文件,
所以只需要写入 username|s:5:'admin';
因为写入的内容包括题中给的"—mylocalnote—
" 会被当做键名,所以我们需要闭合,
最终的payload content=|N;username|s:5:"admin";&name=sess_PHPSESSID
其中PHPSESSID换成自己的
接着访问flag.php得到flag
关于php-Session相关配置的说明
在php.ini中对Session存在许多配置,这里我们通过phpinfo来说明几个重要的点。
session.save_path="" --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式),默认files以文件存储
session.auto_start boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动session.serialize_handler string --定义用来序列化
常见的php-session存放位置
- /var/lib/php5/sess_PHPSESSID
- /var/lib/php7/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSED
Session可能导致的攻击面
- Session序列化攻击
- Session文件包含
- Session伪造用户登录
- Session逻辑漏洞
Session序列化攻击
Serialize_handler
要了解Session序列化攻击,先来了解一下Session机制中对序列化是如何处理的。
在php中存在三种序列化处理引擎
session.serialize_handler对应存储格式
php_serialize | 经过serialize()函数序列化数组(php >= 5.5.4) |
---|---|
php | 键名+竖线+经过serialize()函数处理的值 |
php_binary | 键名的长度对应的ascii字符+键名+serialize()函数序列化的值 |
本地测试如下:
<?php
ini_set("session.serialize_handler", "php");
# test|s:9:"chalan630";
ini_set("session.serialize_handler", "php_binary");
# <0x04>tests:7:"CoCo1er";
ini_set("session.serialize_handler", "php_serialize");
# a:1:{s:4:"test";s:9:"chalan630";}
session_start();
$_SESSION['test'] = "chalan630";
phpinfo();
?>
攻击利用原理
(这里补充说一点,PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。如下)
1. 使用不同引擎来处理session文件
如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。例如:
在这么一种情况下:
假如我们使用php_serialize引擎时进行数据存储时的序列化,可以得到内容
$_SESSION['key'] = 'Boby';
a:1:{s:3:"key";s:4:"Boby";}
这时我们的解析采用了另一种引擎:php
思考一下这时会发生什么情况?(php引擎中以竖线来分隔键和值)
如果像上面我们的payload换一下,传入内容以及得到的存储内容如下:
$_SESSION['key'] = '|O:4:"User":0:{}';
a:1:{s:3:"key";s:16:"|O:4:"User":0:{}";}
这时候a:1:{s:3:"key";s:16:"被当作了key,
而后续的O:4:"User":0:{}";}被当作了value从而被反序列化。这里可能有人会问了,为什么会被反序列化?
看看官方文档
这里可能还会有人问?那串value不符合"正常"的被反序列化的字符串规则。这个也不用担心,这里提到一个unserialize的特性,之前也做题也遇到过。在执行unserialize的时候,如果字符串前面满足了可被反序列化的规则即后续的不规则字符会被忽略。
总结一下,在php以php_serialize引擎生成session,然而又以php引擎来解析时,我们通过传入类似$_SESSION[‘name’] = |序列化内容
这种形式的payload即有可能触发反序列化漏洞。当然这里只是提到了能够找到反序列化利用的点,至于能不能真正触发反序列化漏洞还需要结合当前环境以及一些魔术函数中是否存在可利用点。这就涉及到php反序列化漏洞的利用知识点了,这里也就不详细讲了。
关于Session反序列化攻击的复杂利用方式,可以参考2018LCTF中的bestphp’s revenge一题。
2. Session文件包含
这个也是一个比较旧的知识点了,其实不仅是Session文件包含,仔细想想,理论上只要能够在文件中写入php代码,再被include包含进来不都可以实现getshell嘛?只不过在这里我们的可控点是Session文件,如果能向其中写入php代码,也是可以实现文件包含漏洞利用的。
作为文件包含的利用这里就不展示了,网上关于这个的基础资料早就烂大街了。
值得一提的是,往往现在的CTF出题不会仅限于文件包含这一个点来出题,而是利用诸如session+lfi的形式来入题获取源码等。而且可能加入open_basedir来限制路径,此时就需要熟悉了解session的机制,通过函数来改变save路径来利用。
这个思路是在XCTF Final中出现的bestphp一题中的考点。
3. Session伪造用户登录
本题正是如此
利用前提:session可控;知道session存储格式。
向/tmp写入文件伪造admin
- payload:select 'username|s:5:"admin";' into outfile '/tmp/sess_PHPSESSID'
- 最后修改成对应设计的PHPSESSID即可伪造admin登录拿到flag。
4. Session逻辑漏洞
很遗憾这个点也没有可以复现的环境。这个是上两周unctf中出现的一道web题考点。这个逻辑漏洞处在重置密码处。过程大致如下。
密码重置分为三个步骤。
- 填写需要重置的用户名
- 用户名绑定的邮箱中收到验证码
- 填写验证码,进入重置密码页面,填写完新密码完成重置。
这里存在的逻辑漏洞在于第一个页面的填写用户名处,猜测后台有设置session。类似:
$_SESSION['name'] = $_POST['name'];
利用方式:重置admin密码。
- 打开一个正常页面完整流程走到最后一步,填写完验证码通过后,填写新密码,此时并不提交。
- 新开另外一个页面完成第一步,重置用户填写admin,此时Session不再是我们之前自己的用户,而变成了admin。
- 这时完成之前页面的提交。成功重置admin密码。
这里逻辑漏洞产生的原因在于对填写验证码后没有对相关用户绑定做记录,在最后一步重置密码时没有对Session的可靠性进行检查就直接执行了功能。而我们都知道Session存储在服务器端,因此我们再开一个页面即可完成对单一session文件内容的修改(保证在同一个PHPSEEID下)。