背景
OpenWRT

OpenWRT是一款免费的基于linux的操作系统,主要用于嵌入式设备尤其是网络路由器中。从账号数来看,安装量超过百万。
OpenWRT package manager(包管理器)
要在OpenWRT系统中安装或升级软件,就需要使用一个名为opgk的小工具。其功能和作用相当于Debian系系统的apt功能。
Opkg通过非加密的HTTP连接来从downloads.openwrt.org 下载可用的package列表。Package列表是经过数字签名的。这确保了package二五年间在处理前可被验证,如果验证失败,就不进行下一步。
理论上讲,通过使用签名可以在HTTP传输信道本身并不安全的情况下防止package列表或文件被修改。
漏洞
当用户使用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);
pkg_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 }
如果解码失败,就不会保存哈希值。
真正的漏洞其实位于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 }
从中可以看出,s和src变量指向的是相同的地址。
Package列表分析完成后,就会通过HTTP来下载package。
验证步骤如下:
下载的package大小与package列表中指定的相等:
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 }
如果package中指定了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月引入的,距今拥有3年时间,具体参见:
https://git.openwrt.org/?p=project/opkg-lede.git;a=blobdiff;f=libopkg/file_util.c;h=155d73b52be1ac81d88ebfd851c50c98ede6f012;hp=912b147ad306766f6275e93a3b9860de81b29242;hb=54cc7e3bd1f79569022aa9fc3d0e748c81e3bcd8;hpb=9396bd4a4c84bde6b55ac3c47c90b4804e51adaf
漏洞利用
漏洞利用的前提是攻击者入侵了提供package的web服务器。攻击者必须要能够拦截或替换设备和downloads.openwrt.org之间的通信,或控制设备使用的DNS服务器,将downloads.openwrt.org指向攻击者控制的web服务器。
使用包嗅探或ARP缓存投毒的本地网络攻击也是可能的,但是还没有进行测试。
安全建议
研究报告漏洞后,OpenWRT就移除了package列表中SHA256sum的空格。这可以一定程度上缓解对用户带来的危险。checksum_hex2bin中的漏洞在2月1日发布的OpenWRT v18.06.7 和v19.07.1版本中进行了修复。研究人员建议用户尽快更新OpenWRT。
|