CVE-2021-42342 GoAhead 远程命令执行漏洞分析

发布时间:2022-01-18 06:28:08 作者:Potato 阅读量:1449

Part1漏洞描述

该漏洞是由于上传过滤器没有设置不受信任的 var 位绕过 CGI 处理程序的前缀检测,攻击者可利用该漏洞在未授权的情况下,构造恶意数据执行远程命令执行攻击,最终获取服务器最高权限。

具体成因则是GoAhead在处理CGI请求时,将用户传入的的参数作为环境变量了。这样,通过参数LD_PRELOAD就可以劫持CGI进程的动态链接库,进而执行任意代码。并且本漏洞实际上是对CVE-2017-17562的一次绕过。

补丁对用户传入参数进行了黑名单过滤,LD_PRELOAD这类参数不再设置为环境变量。但由于这个限制使用错了函数,导致实际上并没有生效

并且补丁还将用户传入的参数名前面增加了前缀,导致无法劫持任意环境变量。但这个限制漏掉了multipart的POST包,所以攻击者通过这个方式仍然可以注入任意环境变量。

在2021年5月份GoAhead默认将CGI相关的配置注释了,所以新版本的GoAhead默认没有开启CGI配置,并且老版本如果没有cgi-bin目录,或者里面没有cgi文件,也不受这个漏洞影响。

Part2危害等级

危害等级:高危

Part3漏洞影响

  • 4.0.0<= GoAhead <= 4.1.2
  • 5.0.0<= GoAhead < 5.1.5

Part4漏洞分析及验证

4.1 漏洞原理分析

这个RCE的关键点在于GoAhead的一个特点,当GoAhead在遇到上传表单的时候,会先将这个上传的文件保存在一个临时目录下,待脚本程序处理完成后删掉这个临时文件,也就是函数WEBS_UPLOAD的处理过程。

其中在文件upload.c中,默认定义了上传文件保存路径为目录tmp中。 原理是如果宏ME_GOAHEAD_UPLOAD_DIR没有定义,则将其定义为tmp。然后将其赋值给uploadDir,ME_GOAHEAD_UPLOAD_DIR一定不是空字符串,所以上传目录就是tmp。

要注意的是源码的上传目录配置,由于是相对路径,相对于的是当前目录,当前目录是在启动GoAhead的时候用--home参数指定的,是存放配置文件的目录,在这里是/etc/goahead。

#ifndef ME_GOAHEAD_UPLOAD_DIR
    #define ME_GOAHEAD_UPLOAD_DIR "tmp"
#endif

PUBLIC void websUploadOpen(void)
{
    uploadDir = ME_GOAHEAD_UPLOAD_DIR;
    if (*uploadDir == '\0') {
#if ME_WIN_LIKE
        uploadDir = getenv("TEMP");
#else
        uploadDir = "/tmp";
#endif
    }
    trace(4"Upload directory is %s", uploadDir);
    websDefineHandler("upload"0, uploadHandler, 00);
}

也就是说,临时文件是存放在/etc/goahead/tmp这个目录下的,如果这个目录不存在或者不可写,那么就会出现上传时500,从而导致该漏洞无法利用。

然后当我们上传时会在函数processUploadHeader中构造此次HTTP请求结构体的uploadTmp值。 函数websTempFile默认将生成一个/tmp/tmp-0.tmp的临时文件名称。然后打开临时文件,将文件句柄赋值给wp->upfd 接着当使用函数processContentData处理上传文件时,将通过函数writeToFile写入文件。 最终将文件内容写入了wp->upfd,保存到了创建的临时文件中。

然后在WEBS_READY阶段函数websPump将通过函数websRunRequest进行处理。 在这里的两个函数websSetQueryVarswebsSetFormVars都使用了函数addFormVars,而函数addFormVars处理的最后会将sp->arg赋值为1,使文件cgi.c中的函数cgiHandler对请求参数进行重命名,从而修复CVE-2017-17562漏洞。

接着我们来看HTTP请求在GoAhead中的状态。 当HTTP请求出现后会使用函数readEvent进行处理。 查看函数websPumpWEBS_BEGIN阶段,使用函数parseIncoming 函数parseIncoming中会使用函数parseHeaders会对HTTP头进行检查与解析,并根据content-type的不同类型,完成对wp->flags的赋值。 当进入WEBS_CONTENT阶段,函数websPump中使用函数processContent进行下一步的处理。 WEBS_READY阶段中,GoAhead对POST请求和GET请求提交的参数都会使用函数addFormVars进行处理,将sp->arg赋值为1,从而使得文件cgi.c中的函数cgiHandler重命名环境变量,但是我们可以看到POST请求使用函数addFormVars的前提是函数wp-flags取值为WEB_FORM,回头来看WEBS_BEGIN处理过程,当content-typemultipart/form-data时,wp-flags将赋值为WEBS_UPLOAD,也就是说,如果HTTP请求为文件上传类型,参数将不会通过函数addFormVars处理,此时s->arg取值仍然为0,从而文件cgi.c中的函数cgiHandler中将不会使用ME_GOAHEAD_CGI_VAR_PREFIX作为前缀进行修改,而是直接进入了其他分支。

4.3 漏洞复现

复现攻击时要注意几点

  • 很多IOT设备并没有文件上传的需求,并没有配置tmp目录,导致实际上攻击者无法通过文件上传的方式向目标写入任意文件,也就无法完成攻击。
  • 攻击时使用的动态链接库不能过大,否则可能导致服务端出错,直接断开链接。
  • 被打开的临时文件描述符在文件上传完成后会被关闭,关闭的原因在于临时文件被删除了。

在了解以上几点后构造测试漏洞的POChack.c

#include <unistd.h>

static void before_main(void) __attribute__((constructor));

static void before_main(void)
{
    write(1"Hello: World\r\n\r\n"16);
    write(1"Hacked\n"7);
}

编译文件,使用-s参数来缩小体积。

gcc -s -shared -fPIC hack.c -o hack.so

构造HTTP请求,证明存在。

原理是,在请求体中加上脏字符构造实际大小比Content-Length大的数据包,导致上传实际上只上传了一半,保存在临时文件中的是完整的payload和一些脏字符。

此时由于上传流程没有结束,所以此时文件描述符是没有关闭的,可以读取到,脏字符也不影响动态链接库的加载和运行,最后即可成功完成劫持。

Part5修复建议

建议受影响的用户及时更新升级到最新版本。

链接:https://github.com/embedthis/goahead


Potato

专注渗透测试技术

全球最新网络攻击技术

支付宝打赏 微信打赏

我要评论

Catfish(鲶鱼) Blog V 4.7.3