NETGEAR 远程代码执行漏洞分析

本文作者: cq674350529 (信安之路荣誉成员)

最近看到一篇安全资讯:

https://securityaffairs.co/wordpress/99177/security/netgear-flagship-nighthawk-router-rce.html

提到 Netgear 修复了其产品中的多个高危漏洞,包括 PSV-2019-0076PSV-2018-0352PSV-2019-0051 等。其中,利用部分漏洞可实现远程代码执行,且无需认证。以 PSV-2019-0076 为例,查看 Netgear 的安全公告,如下,并没有透露过多的细节。

通常来说, IoT 设备上的漏洞相对比较简单,于是打算花点时间看下,尝试通过补丁比对定位具体的漏洞,进一步地得到对应的 PoC

在进行分析时,暂未在网上找到相关漏洞详情或 PoC 等信息。

漏洞定位

由公告可知,该漏洞在 1.0.2.68 版本中修复,下载邻近的两个版本 1.0.2.621.0.2.68 的固件到本地进行分析。通过对两个文件系统进行简单比对,发现这2个版本之间的差异非常多。通常来说,不同版本之间的差异越小,越有助于定位漏洞


$ diff -r _R7800-V1.0.2.62.img.extracted/squashfs-root/ _R7800-V1.0.2.68.img.extracted/squashfs-root/ | grep Binary | wc -l 335

从更新时间来看,这两个版本之间间隔差不多有 11 个月。

在对文件系统进行简单分析后,将比对的目录缩小在 /www/usr/sbin 两个目录中。浏览了下 diff 的结果,其中有几个文件比较有意思,包括 proccgiuhttpd 等。

先对 proccgi 进行分析,借助 Bindiff 插件进行比对,如下。在函数 sub_00008824() 中,仅改变了处理流程的顺序,未发现安全问题。

同样,对 uhttpd 进行分析, Bindiff 比对的结果如下。大部分发生变化的是系统函数,除了 uh_cgi_auth_check() 函数之外。

两个版本中 uh_cgi_auth_check() 函数内的主要差异如下。在新版本中增加了 dni_system() 函数,而在老版本中则使用 snprintf() + system() 的模式,熟悉的人一看可能就知道这是典型的命令注入漏洞。在查看 dni_system() 后,其内部使用 execve() 来执行命令,更加证实了这一点。

现在大体上定位到了漏洞的具体位置(当然也有可能不是...),还需要进一步分析看能否触发以及如何触发。

静态分析

uh_cgi_auth_check() 函数的部分伪代码如下,其主要逻辑为:找到请求头中的 Authorization 部分,获取 "Basic " 后面的内容,在 base64 解码后获取其中的 password ,再传入 snprintf() 中进行格式化,最后调用 system() 执行。典型的命令注入模式,且发生在进行认证的过程中,与安全资讯中提到的的"无需认证"相对应,再一次说明漏洞很可能就是这里(当然还没有完全确定...)。

signed int __fastcall uh_cgi_auth_check(int a1, int a2, int a3)
{
  // ...
  while ( 1 )  // 从HTTP头中找到Authorization部分, 然后获取"Basic "后面的值
  {
    v11 = *(const char **)(v10 + 16);
    // ...
    if ( !strcasecmp(v11, "Authorization") )
    {
      v12 = *(const char **)(v10 + 20);
      if ( strlen(*(const char **)(v10 + 20)) > 6 )
      {
        v13 = strncasecmp(v12, "Basic ", 6u);
        if ( !v13 )
          break;
    // ...
  }
  // ...
  uh_b64decode(&s, 4095, v12 + 6, v23 - 6);
  v24 = strchr(&s, ':');  // base64解码后的内容为"username:password"这种形式
  // ...
  *v24 = v14;
  v15 = (int)(v24 + 1);
  if ( v24 != (char *)-1 )
  {
    // 将password作为参数传入, 然后调用system()执行
    snprintf((char *)&v29, 0x80u, "/usr/sbin/hash-data -e %s >/tmp/hash_result", v15);
    system((const char *)&v29);
    v3 = cat_file((int)"/tmp/hash_result", v25, v26);
  }
  // ...
}

如果手边有真实设备的话,其实就可以直接在设备上进行测试了,然而我手边并没有真实设备:( ... 所以继续对调用路径进行分析。 uh_cgi_auth_check() 函数的调用路径很简单,仅有2处调用,且均在 main() 函数中,如下。对 main() 函数前面的逻辑进行了简单的分析,主要是解析 uhttpd 命令行参数、服务初始化、解析部分HTTP 请求参数之类的,没啥特别的。

现在确信这里是可以触发的,奈何手边没有真实设备,于是又开始折腾固件仿真,想进一步通过动态测试验证。

动态分析

IoT 设备进行固件仿真,常见的方式如下:

1、基于 qemu user mode ,模拟单个服务: D-Link 的很多设备可以采用这种方式

2、基于 qemu system mode , 模拟整个系统:一些第三方工具对 qemu 进行了封装,比如 FirmadyneARM-X

3、"纯软件模拟":如 Qiling

为了方便,首先使用 Firmadyne 框架进行测试,发现无法获取网络配置信息。而 ARM-XQiling 框架暂时未仔细研究,所以还是采用我经常使用的方式:基于 qemu user mode 模拟单个服务,如下。幸运的是,服务成功跑起来了,暂时没有报错,无需手动修复环境。

# '-f' option is used for debugging easily
$ sudo chroot . ./qemu-arm-static /usr/sbin/uhttpd -f -h /www -r R7800  -x /cgi-bin -t 80 -p 0.0.0.0:80 -C /etc/uhttpd.crt -K /etc/uhttpd.key -s 0.0.0.0:443
$ netstat -tlnp
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN

由于对 R7800 这款设备不太熟悉,不知道 url 的前缀,先直接访问 / 路径,并带上对应的 payload ,测试后发现并未成功,难道是没有触发对应的路径?然后在启动时加上 qemu-g 选项,采用 gdb-multiarch 进行附加调试,分析发现在 uh_cgi_auth_check() 函数头部有一个判断没通过,如下。

signed int __fastcall uh_cgi_auth_check(int a1, int a2, int a3)
{
  // ...
  // 经调试得到: 比较"/start.htm" 和"/cgi-bin"
  v9 = strncasecmp(*(const char **)(v6 + 8), *(const char **)(*(_DWORD *)(*(_DWORD *)(v4 + 4104) + 32) + 4132), v8);
  if ( v9 )
    return 1;
  // ...
}

在将 url 改为 /cgi-bin 后,浏览器成功地弹出了认证的对话框,之后在 gdb 中可以看到成功地到达了漏洞点。然而,命令执行完毕之后,本地还是没有生成 hello.txt 文件... (PS:尝试了多种 payload 无果,可能和基于 qemu user mode 仿真有关)

小结

本文从漏洞公告出发,通过固件版本差异分析,再到补丁比对,最终成功定位到漏洞,并结合静态分析和动态仿真的方式对漏洞进行了验证。整体上来说,思路算是完整的,也适用于分析其他的 N day ,区别在于整个过程中每一步的复杂程度不一样。

相关链接

Security Advisory for Unauthenticated Remote Code Execution on R7800, PSV-2019-0076:

https://kb.netgear.com/000061740/Security-Advisory-for-Unauthenticated-Remote-Code-Execution-on-R7800-PSV-2019-0076