背景
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。
本文翻译自:https://blog.forallsecure.com/uncovering-openwrt-remote-code-execution-cve-2020-7982如若转载,请注明原文地址