OneShell

I fight for a brighter tomorrow

0%

IoT小设备常见webserver分析及漏洞挖掘思路浅析:GoAhead篇

简介

GoAhead是一个轻量化、适用于嵌入式设备的Web服务器,采用C语言编写,代码量不大,具有高度的可移植性和扩展性。GoAhead支持多进程、多线程,能够处理大量的并发连接,支持SSL/TLS加密和基本的身份认证,支持CGI、ASP。

GoAhead由Embedthis Software LLC开发,早年间是完全开源的,可以直接在Github上下载到源码。但是在2022年的时候,似乎转换成商业定制,官方在Github删除了代码库,因此在Github上无法下载,但是在Gitee上还有镜像库。

地址:GoAhead: GoAhead WebServer 采用开放源代码方式,任何人都可以下载、评估并修改代码,目的是为了使GoAhead WebServer成为市场上最领先的嵌入式Web服务器

本文不会完全分析GoAhead的代码实现、架构,而是聚焦于安全研究较为关注、通常由开发者实现的数据包处理部分。文章的大概阐述思路如下:

  1. 首先结合源码说明GoAhead的数据包处理特性,主要涉及鉴权、路由处理
  2. 结合漏洞简述基于GoAhead的server漏洞挖掘思路

源码简单分析请求处理

GoAhead会对数据包按照优先级进行顺序处理,也会根据数据包的路由调用不同的回调函数进行处理。先说说数据包优先级:

  • 优先级为1注册的回调函数:所有数据包都需要首先经过该回调函数进行处理,此处也通常被用来做数据包鉴权、请求路径合法性判断、未授权访问路径定义等等;优先级为0注
  • 优先级为0注册的回调函数:通常用来定义认证后可访问到的接口逻辑实现
  • 优先级为2注册的回调函数:没有回调函数匹配的数据包
1
2
3
int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg,
int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webdir, int arg,
char_t *url, char_t *path, char_t *query), int flags)

比较重要的参数:

  • char_t *urlPrefix:指定URL的前缀,也就是需要处理的URL开头部分
  • int (*handler):URL对应的回调函数
  • int flags:URL处理优先级标志,有如下的两个选择:
    • #define WEBS_HANDLER_FIRST 0x1:所有的数据包都会通过该回调函数进行处理
    • #define WEBS_HANDLER_LAST 0x2:没有回调函数匹配的数据包会通过该回调函数进行处理

如下是一个真实设备的反编译代码,可以看到GoAhead是通过注册一个flags=WEBS_HANDLER_FIRST=1的回调函数websAuthHandler来判断数据包是否通过认证的,这意味着所有的数据包都会通过函数websAuthHandler进行处理,验证数据包发送者的权限。

1
2
3
4
5
6
7
8
9
10
11
websUrlHandlerDefine(&byte_4AA6CC, 0, 0, websAuthHandler, 1);
websUrlHandlerDefine("/goform", 0, 0, websFormHandler, 0);
websUrlHandlerDefine("/cgi-bin", 0, 0, websCgiHandler, 0);
websUrlHandlerDefine("/reboot.asp", 0, 0, sub_464028, 0);
websUrlHandlerDefine("/reboot", 0, 0, sub_464028, 0);
websUrlHandlerDefine("/exception_log", 0, 0, sub_462850, 0);
websUrlHandlerDefine("/nat_log", 0, 0, sub_4628F8, 0);
websUrlHandlerDefine("/attributes_log", 0, 0, sub_4628F8, 0);
websUrlHandlerDefine("/dhcpcliinfo", 0, 0, sub_4628F8, 0);
websUrlHandlerDefine("/auto_ch", 0, 0, sub_4628F8, 0);
websUrlHandlerDefine("/auto_5G_ch", 0, 0, sub_4628F8, 0);

例如对一个请求的完整处理过程:使用POST请求访问/goform/websLogin,

  1. 首先数据包会进入函数webAuthHandler:请求路径鉴权、请求路径合法性判断等
    1
    websUrlHandlerDefine(&byte_4AA6CC, 0, 0, websAuthHandler, 1);
  2. 根据一层路径/goform,匹配回调函数websFormHandler:
    1
    websUrlHandlerDefine("/goform", 0, 0, websFormHandler, 0);
  3. 根据二层路径/websLogin,匹配回调函数websLogin:
    1
    websFormDefine((int)"websLogin", (int)websLogin);

因此,对于GoAhead作为server的设备来说,如果想挖到认证前的RCE的话,可以主要从优先级为0的登录处理回调函数入手,找到绕过认证的方法,或者看回调函数是否存在缓冲区溢出等能够劫持控制流的漏洞。

漏洞挖掘思路

历史版本漏洞

版本号的确定:搜索字符串GoAhead-Webs,可以确定版本,从而判断是否受到历史漏洞的影响
undifined

漏洞编号 影响版本 漏洞概述
CVE-2017-17562 < 3.6.5 初始化CGI时,在HTTPD请求参数中,使用特殊的参数名LD_PRELOAD劫持libc库,进而远程命令执行
CVE-2021-42342 4.x、5.x~5.1.5 对上述的一个绕过

如上的两个漏洞如果要达成认证前RCE,需要/cgi-bin/一层路径可以未授权访问

漏洞案例1:发生在websSecurityHandler中的认证绕过

CVE-2020-15633

该漏洞是发生在LAN口的一个登录认证绕过漏洞,影响设备DIR-867、DIR-878、DIR-882,固件版本1.20B10_BETA。漏洞产生的原因是在处理HNAP请求的过程中,验证用户登录逻辑时处理不当,使用strstr函数来检查无需验证权限的接口,导致可以构造特定URI来绕过身份认证,从而访问敏感接口。

设备采用lighttpd作为webserver,根据配置会将HNAP请求转发到程序/bin/prog.cgi进行处理,prog.cgi是基于GoAhead开发。

1
2
3
4
5
6
7
8
9
10
11
fastcgi.server = ( 
"/HNAP1/" =>
((
"socket" => "/var/prog.fcgi.socket-0",
"check-local" => "enable",
"bin-path" => "/bin/prog.cgi",
"idle-timeout" => 10,
"min-procs" => 1,
"max-procs" => 1
)),
......

漏洞触发过程的函数调用如下:

1
2
3
sub_423ECC -> 0
sub_4249EC -> 0
websSecurityHandler -> 0
  1. 在函数sub_423ECC中,会使用函数strstr比较环境变量REQUEST_URI(也就是请求路径)中是否含有字符串列表actions_list中的字符串,然后触发到return 0。
    1
    2
    3
    4
    5
    if ( a1[57] && strstr(a1[57], &actions_list[32 * index]) )// REQUEST_URI
    {
    if ( strcmp(&actions_list[32 * index], "/HNAP1/") || !a1[50] || strcmp(a1[50], "POST") )
    return 0;
    }
  2. 然后返回到函数sub_4249EC,触发该函数继续返回0;
  3. 再返回到函数websSecurityHandler中,使得该认证函数返回0,达到认证绕过;
    1
    websUrlHandlerDefine("/", 0, 0, websSecurityHandler, 1);

其他认证后的接口(大多存在)

大多基于GoAhead开发的server都存在认证后的漏洞,例如缓冲区溢出、命令执行等等。