安全矩阵

 找回密码
 立即注册
搜索
查看: 3031|回复: 0

关于路由器的CGI漏洞分析及挖掘

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-8-18 09:18:40 | 显示全部楼层 |阅读模式
原文链接:关于路由器的CGI漏洞分析及挖掘



本文主要记述了CGI的含义,以及常见网络设备中使用的CGI框架与流程,最后通过DLINK和Netgear两款路由器的CGI漏洞介绍当前常见的CGI漏洞挖掘和分析流程。
CGI是什么到目前为止,实现动态Web页面有4种技术可供选择:CGI,ASP,PHP和JSP。本节记述关于路由器CGI相关知识。
早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器。随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能够直接运行动态脚本,为了解决Web服务器与外部应用程序(CGI程序)之间数据互通,于是出现了CGI(Common Gateway Interface)通用网关接口。简单理解,可以认为CGI是Web服务器和运行其上的应用程序进行“交流”的一种约定。
CGI不是一门编程语言。它是网页的表单和你写的程序之间通信的一种协议。可以用任何语言写一个CGI脚本,这些语言只要能接收输入输出信息,读取环境变量。所以,几乎所有的编程语言都能写一个CGI脚本,例如:python ,C,甚至是shell脚本。
典型的CGI脚本做了如下的事情:
  •         读取用户提交表单的信息
  •         处理这些信息
  •         输出,返回html响应

设备CGI分析目前IOT设备主要有三个WebServer:httpd,thttpd和Boa。httpd是最简单的一个Web Server,它的功能最弱,不支持认证,不支持CGI。Thttpd和Boa都支持认证、CGI等,功能都比较全。
BoaBoa 是一个单任务的http服务器,源代码开放、性能高。Boa 是一个单任务 HTTP 服务器。这意味着与传统的 Web 服务器不同,它不会为每个传入的连接 fork,也不会 fork自身的许多副本来处理多个连接。它在内部多路复用所有正在进行的 HTTP 连接,并且只为 CGI 程序(必须是单独的进程)、自动目录生成和自动文件压缩进行 fork。
源码分析
在Boa官网下载源码。
Boa首先会调用 process_option_Iine()将一些头部信息填写到request结构中完成这些环境变量的设置,随后 process_header_end()会对用户进行验证。如果验证通过则判断request结构中的is_cgi,非0则是CGI程序,调用 init_cgi()函数进行处理,为0则是静态页面,调用init_get()函数进行处理。
init_cgi
boa程序在解析请求头的收尾函数process_header_end中,translate_uri函数会解析请求的虚拟路径.
  1. * Name: init_cgi
  2. *
  3. * Description: Called for GET/POST requests that refer to ScriptAlias
  4. * directories or application/x-httpd-cgi files.  Ties stdout to socket,
  5. * stdin to data if POST, and execs CGI.
  6. * stderr remains tied to our log file; is this good?
  7. *
  8. * Returns:
  9. * 0 - error or NPH, either way the socket is closed
  10. * 1 - success
复制代码


init_cgi首先通过调用 create_common_env(),complete_env()完成对CGI环境变量的设置

PS3GPD
translate_uri函数中的init_script_alias函数,负责解析ScriptAlias请求,设置请求cgi类型,查看文件是否存在以及具有相关权限。
然后CGI会execve执行相应的程序。
  1. if (req->cgi_type) {
  2.             char *aargv[CGI_ARGC_MAX + 1];
  3.             create_argv(req, aargv);
  4.             execve(req->pathname, aargv, req->cgi_env);
  5.         } else {
  6.             if (req->pathname[strlen(req->pathname) - 1] == '/')
  7.                 execl(dirmaker, dirmaker, req->pathname, req->request_uri,
  8.                       (void *) NULL);
复制代码


固件分析
这里分析vivetok摄像头固件,该摄像头的web是基于Boa进行二次开发的。
uhttpd
uHTTPd作为OpenWrt中默认的HTTP服务器,主要是用来配合LuCI Web接口方便OpenWrt设备的管理,luci就是一个网页界面,包含openwrt配置页面。以cgi的方式被web服务器调用并渲染页面,语言采用lua,支持CGI、Lua和UBUS完成对请求的处理。在IoT设备上使用OpenWrt比较常见的情况是,结合uhttpd使用LuCI框架编写lua处理脚本。
源码分析
下载 uhttpd源代码
首先在uh_handle_request中解析接收到的请求头
  1. void uh_handle_request(struct client *cl)
  2. {
  3.     struct http_request *req = &cl->request;
  4.     struct dispatch_handler *d;
  5.     char *url = blobmsg_data(blob_data(cl->hdr.head));
  6.     char *error_handler, *escaped_url;

  7.     blob_buf_init(&cl->hdr_response, 0);
  8.     url = uh_handle_alias(url);

  9.     uh_handler_run(cl, &url, false);
  10.     if (!url)
  11.         return;

  12.     req->redirect_status = 200;
  13.     d = dispatch_find(url, NULL);
  14.     if (d)
  15.         return uh_invoke_handler(cl, d, url, NULL);

  16.     if (__handle_file_request(cl, url))
  17.         return;

  18.     if (uh_handler_run(cl, &url, true)) {
  19.         if (!url)
  20.             return;

  21.         uh_handler_run(cl, &url, false);
  22.         if (__handle_file_request(cl, url))
  23.             return;
  24.     }

  25.     req->redirect_status = 404;
  26.     if (conf.error_handler) {
  27.         error_handler = alloca(strlen(conf.error_handler) + 1);
  28.         strcpy(error_handler, conf.error_handler);
  29.         if (__handle_file_request(cl, error_handler))
  30.             return;
  31.     }

  32.     escaped_url = uh_htmlescape(url);

  33.     uh_client_error(cl, 404, "Not Found", "The requested URL %s was not found on this server.",
  34.                     escaped_url ? escaped_url : "");

  35.     if (escaped_url)
  36.         free(escaped_url);
  37. }
复制代码


使用dispatch_find函数根据请求的url找到合适的dispatch_handler,cgi_prefix在/etc/config/uhttpd配置文件中的默认值为/cgi-bin,并且程序在main函数中默认添加了cgi_dispatch,当请求的url通过check_cgi_path函数校验,则会调用cgi_handle_request函数回调cgi_main函数execl执行对应的CGI程序
  1. static void cgi_handle_request(struct client *cl, char *url, struct path_info *pi)
  2. {
  3.     unsigned int mode = S_IFREG | S_IXOTH;
  4.     char *escaped_url;

  5.     if (!pi->ip && !((pi->stat.st_mode & mode) == mode)) {
  6.         escaped_url = uh_htmlescape(url);

  7.         uh_client_error(cl, 403, "Forbidden",
  8.                 "You don't have permission to access %s on this server.",
  9.                 escaped_url ? escaped_url : "the url");

  10.         if (escaped_url)
  11.             free(escaped_url);

  12.         return;
  13.     }

  14.     if (!uh_create_process(cl, pi, url, cgi_main)) {
  15.         uh_client_error(cl, 500, "Internal Server Error",
  16.                 "Failed to create CGI process: %s", strerror(errno));
  17.         return;
  18.     }

  19.     return;
  20. }

  21. struct dispatch_handler cgi_dispatch = {
  22.     .script = true,
  23.     .check_path = check_cgi_path,
  24.     .handle_request = cgi_handle_request,
  25. };
复制代码


执行CGI程序:
  1. static void cgi_main(struct client *cl, struct path_info *pi, char *url)
  2. {
  3.     const struct interpreter *ip = pi->ip;
  4.     struct env_var *var;

  5.     clearenv();
  6.     setenv("PATH", conf.cgi_path, 1);

  7.     for (var = uh_get_process_vars(cl, pi); var->name; var++) {
  8.         if (!var->value)
  9.             continue;

  10.         setenv(var->name, var->value, 1);
  11.     }

  12.     if (!chdir(pi->root)) {
  13.         if (ip)
  14.             execl(ip->path, ip->path, pi->phys, NULL);
  15.         else
  16.             execl(pi->phys, pi->phys, NULL);
  17.     }

  18.     printf("Status: 500 Internal Server Error\r\n\r\n"
  19.            "Unable to launch the requested CGI program:\n"
  20.            "  %s: %s\n", ip ? ip->path : pi->phys, strerror(errno));
  21. }
复制代码

}
CGI漏洞D-LINK SERVICE.CGI远程命令执行漏洞
2018年1月17日,CNVD公开了D-LinkDIR 615/645/815 service.cgi远程命令执行漏洞(CNVD-2018-01084)。
D-Link DIR 615/645/815路由器1.03及之前的固件版本存在远程命令执行漏洞。该漏洞是由于service.cgi中拼接了HTTP POST请求中的数据,造成后台命令拼接,导致可执行任意命令。
下载固件
先解压,再使用binwalk分析固件
  1. $ unzip DIR-645_FIRMWARE_1.03.ZIP  
  2. Archive:  DIR-645_FIRMWARE_1.03.ZIP
  3.   inflating: dir645_FW_103.bin      

  4. $ binwalk -Me dir645_FW_103.bin

  5. Scan Time:     2021-08-12 23:03:41
  6. MD5 Checksum:  12a10a6a2fe96e0b7a50c2babd714e3d
  7. Signatures:    410

  8. DECIMAL       HEXADECIMAL     DESCRIPTION
  9. --------------------------------------------------------------------------------
  10. 0             0x0             DLOB firmware header, boot partition: "dev=/dev/mtdblock/2"
  11. 112           0x70            LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4237576 bytes
  12. 1441904       0x160070        PackImg section delimiter tag, little endian size: 3169792 bytes; big endian size: 6172672 bytes
  13. 1441936       0x160090        Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 6170670 bytes, 2216 inodes, blocksize: 262144 bytes, created: 2012-10-09 10:24:09


  14. Scan Time:     2021-08-12 23:03:43
  15. MD5 Checksum:  0eab4114d12c97a4a1ff29d0d307d6da
  16. Signatures:    410

  17. DECIMAL       HEXADECIMAL     DESCRIPTION
  18. --------------------------------------------------------------------------------
  19. 1571792       0x17FBD0        MySQL ISAM index file Version 8
  20. 1621686       0x18BEB6        PGP RSA encrypted session key - keyid: 801000 4C RSA Encrypt-Only 1024b
  21. 3149062       0x300D06        PGP RSA encrypted session key - keyid: 801000 3A RSA Encrypt-Only 1024b
  22. 3149250       0x300DC2        PGP RSA encrypted session key - keyid: 801000 3A RSA Encrypt-Only 1024b
  23. 3338272       0x32F020        Linux kernel version 2.6.33
  24. 3398624       0x33DBE0        CRC32 polynomial table, little endian
  25. 3803284       0x3A0894        Neighborly text, "NeighborSolicitstunnel6 init(): can't add protocol"
  26. 3803304       0x3A08A8        Neighborly text, "NeighborAdvertisementst add protocol"
  27. 3808047       0x3A1B2F        Neighborly text, "neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)(%s)"
复制代码


逆向cgibin
使用IDA逆向cgibin文件
  1. $ file cgibin                              
  2. cgibin: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
复制代码


查看servicecgi_main函数,我们可以看到servicecgi_main调用了lxmldbc_system函数,

CxPDse
此函数调用了system函数。

K9aOqQ
接下来该漏洞的成因。
在servicecgi_main中先判断请求的方法,只处理get和post

VA6EIs
在servicecgi_main中可以处理EVENT,SERVICE,ACTION三个类型的form表单参数域。servicecgi_main会把这三个表单域参数存储在特定区域,然后进行event,service,action的处理。
在处理event请求时没有对EVENT值做必要的处理,然后进入loc_40D038模块,在模块中调用了lxmldbc_system函数,进而调用了system函数。

qnexhC
而lxmldbc_system会调用system执行command,而且是没有任何过滤的,那就存在命令注入的风险了。
Netgear CVE-2016-6277
NETGEAR R6250在1.0.4.6.Beta之前,R6400在1.0.1.18.Beta之前,R6700在1.0.1.14.Beta,R6900,R7000在1.0.7.6.Beta之前,R7100LG在1.0.0.28.Beta之前,R7300DST在1.0.0.46.Beta之前,1.0.1.8.Beta之前的R7900、1.0.3.26.Beta,D6220,D6400,D7000之前的R8000,以及可能的其他路由器,允许远程攻击者通过shell在 cgi-bin/ 的路径中执行任意命令。
下载Netgear R7000固件
使用binwalk解析固件,使用IDA分析httpd文件
通过漏洞提示信息在IDA中查找到对应字符串

dk6EPz

20aCDa
查看v51的来源

b6b5pZ
可以看到v51是v19拷贝来的

xdVxVe
接着溯源可以看到v19来自v12,v12来自v6,v6是一个URL,这也就和上文对应了起来

jF6gE0
所以这里是一个因为对命令没有过滤的命令注入漏洞。



回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 12:40 , Processed in 0.016213 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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