开源计算机视觉库 OpenCV 被曝两个严重的任意代码执行漏洞(详情)
聚焦源代码安全,网罗国内外最新资讯!
开源计算机视觉库 OpenCV中修复了两个高危的缓冲区溢出漏洞,它们本可导致任意代码执行的后果。
OpenCV是一款开源库,它由Intel Research 于1999年开发而成,现在由非营利性组织机构OpenCV.org 负责维护。它包含超过2500种优化的计算机视觉和机器学习算法,旨在加速商业产品对机器感知的使用。
OpenCV广泛应用于谷歌、雅虎、微软、英特尔、IBM、索尼、本田、丰田等等厂商中,涵盖面部识别技术、机器人、运动跟踪和其它应用程序。OpenCV具有C++、Python、Java 和 MATLAB 接口,同时支持 Windows、Linux、安卓和 Mac OS 系统。
2019年12月末发布的 OpenCV4.2.0 推出多种改进和修复方案,包括修复了两个由思科Talos 团队发现的两个严重的缓冲区溢出漏洞。
CVE-2019-5063
第一个漏洞是 CVE-2019-5063(CVSS 评分8.8),它是存在于OpenCV 4.1.0 数据结构持久功能中的一个缓冲区溢出漏洞,可导致开发人员从磁盘文件写入并检索OpenCV 数据结构或将OpenCV 数据结构写入并检索到磁盘文件中。文件类型可以是XML、YAML或JSON。
思科 Talos 团队指出,一个特殊构造的 XML 文件可引发缓冲区溢出问题,导致多种堆损坏和潜在的代码执行后果。攻击者能够通过提供特殊构造文件的方式触发该漏洞。
在解析包含潜在的字符实体应用的 XML 文件过程中,遇到“&”号时,API 将继续提取字母数字字符,直到遇到分号为止。如果该字符串和switch 语句中的字符串之一不匹配,则该数据按原样被复制到缓冲区中。
在persistence_xml.cpp 中,我们可以看到将被溢出的缓冲区存在于堆上的一个FileStorageParser 类中。
char strbuf[CV_FS_MAX_LEN+16];
其中,persistence.hpp 将CV_FS_MAX_LEN定义为:
44
因此,我们的缓冲区大小是 0x1010 (4112) 的长度。在persistence_xml.cpp中的如下解析例程中会发生溢出:
583 else if( c == '&' )584 {585 if( *++ptr == '#' )586 {587 int val, base = 10;588 ptr++;589 if( *ptr == 'x' )590 {591 base = 16;592 ptr++;593 }594 val = (int)strtol( ptr, &endptr, base );595 if( (unsigned)val > (unsigned)255 ||596 !endptr || *endptr != ';' )597 CV_PARSE_ERROR_CPP( "Invalid numeric value in the string" );598 c = (char)val;599 }600 else601 {602 endptr = ptr;603 do c = *++endptr;604 while( cv_isalnum(c) );605 if( c != ';' )606 CV_PARSE_ERROR_CPP( "Invalid character in the symbol entity name" );607 len = (int)(endptr - ptr);608 if( len == 2 && memcmp( ptr, "lt", len ) == 0 )609 c = '<';610 else if( len == 2 && memcmp( ptr, "gt", len ) == 0 )611 c = '>';612 else if( len == 3 && memcmp( ptr, "amp", len ) == 0 )613 c = '&';614 else if( len == 4 && memcmp( ptr, "apos", len ) == 0 )615 c = '\'';616 else if( len == 4 && memcmp( ptr, "quot", len ) == 0 )617 c = '\"';618 else619 {620 memcpy( strbuf + i, ptr-1, len + 2 );621 i += len + 2;622 }623 }
溢出发生在第620行。溢出发生的原因在于缓冲区的大小是固定的,但memcpy 的大小被计算为整个XML实体值(第596行)的长度,而不会检查它是否超出了目标缓冲区。
我们可以看到易受攻击的 memcpy 操作发生在此处:
0x41f71f call memcpy@plt <0x406470>dest: 0x91c380 ◂— 0x676e69727400src: 0x911e15 ◂— 0x4242424242422026 ('& BBBBBB')n: 0x2c83
如果缓冲区的大小仅为 0x1010(4112),则memcpy 的大小(实体引用字符串之一的整个值)即0x2c83(11395) 个字节,这样在后续的堆对象中会存在溢出,从而导致潜在的代码执行后果。
目标缓冲区位于“FileStorageParser”对象本身当中:
type = class cv::XMLParser : public cv::FileStorageParser {public:cv::FileStorage_API *fs;char strbuf[4112];XMLParser(cv::FileStorage_API *);virtual ~XMLParser(void);char * skipSpaces(char *, int);virtual bool getBase64Row(char *, int, char *&, char *&);char * parseValue(char *, cv::FileNode &);char * parseTag(char *, std::__cxx11::string &, std::__cxx11::string &, int &);virtual bool parse(char *);} *
在这个具体案例中,该实例的堆对象位于0x91c350处,该缓冲区位于0x91c380处。
Heap chunk: 0x91c358 (malloc address)Heap chunk header: 0x91c350Size: 0x1040 (4160): 0x1050 (4176)Status: in USEPrev size field: 0x0 (0)Raw Size: 0x1041 (4161)Flags: PREV_INUSE000000000091c340: 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............000000000091c350: 00 00 00 00 00 00 00 00 41 10 00 00 00 00 00 00 ........A.......000000000091c360: 48 3f 8e 00 00 00 00 00 01 00 00 00 01 00 00 00 H?..............000000000091c370: 98 3f 8e 00 00 00 00 00 60 04 91 00 00 00 00 00 .?......`...........000000000091d380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000000000091d390: 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 ........!.......
堆中的下一个对象位于 0x91d390 处,距离恰好是 0x1040(4160) 个字节:
Heap chunk: 0x91d398 (malloc address)Heap chunk header: 0x91d390Size: 0x20 (32): 0x30 (48)Status: in USEPrev size field: 0x0 (0)Raw Size: 0x21 (33)Flags: PREV_INUSE000000000091d380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000000000091d390: 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 ........!...........000000000091d3a0: 00 73 74 72 69 6e 67 73 00 00 00 00 00 00 00 00 .strings........000000000091d3b0: 00 00 00 00 00 00 00 00 51 1c 00 00 00 00 00 00 ........Q.......
Memcpy()操作之后的对象如下:
Heap chunk: 0x91c358 (malloc address)Heap chunk header: 0x91c350Size: 0x1040 (4160): 0x1050 (4176)Status: is FREEFD: 0x8e3f48BK: 0x100000001Prev size field: 0x0 (0)Raw Size: 0x1041 (4161)Flags: PREV_INUSE000000000091c340: 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............000000000091c350: 00 00 00 00 00 00 00 00 41 10 00 00 00 00 00 00 ........A.......000000000091c360: 48 3f 8e 00 00 00 00 00 01 00 00 00 01 00 00 00 H?..............000000000091c370: 98 3f 8e 00 00 00 00 00 60 04 91 00 00 00 00 00 .?......`...........000000000091d380: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB000000000091d390: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
0x91d390处的堆对象显然被损坏了:
0x91d390: 0x4242424242424242 0x42424242424242420x91d3a0: 0x4242424242424242 0x42424242424242420x91d3b0: 0x4242424242424242 0x42424242424242420x91d3c0: 0x4242424242424242 0x42424242424242420x91d3d0: 0x4242424242424242 0x42424242424242420x91d3e0: 0x4242424242424242 0x42424242424242420x91d3f0: 0x4242424242424242 0x42424242424242420x91d400: 0x4242424242424242 0x4242424242424242
堆分段从 0x8f9000 延伸到 0x91f000(本实例)。因为尝试将数据复制到对段之外,这种具体的攻击变体将触发访问冲突。
0x8f9000 0x91f000 rw-p 26000 0 [heap]Program received signal SIGSEGV, Segmentation fault.__memmove_sse2_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:481481 VMOVU %VEC(8), (%r11)
CVE-2019-5064
第二个漏洞 CVE-2019-5064(CVSS 评分8.8)也存在于该库的数据结构持久性功能中,可通过一个特殊构造的JSON 文件被触发。它可导致开发人员从磁盘文件写入并检索OpenCV 数据结构或将OpenCV 数据结构写入并检索到磁盘文件中。文件类型可以是XML、YAML或JSON。
问题在于,当解析一个JSON 文件并遇到一个null 字节时,OpenCV将那时的整个值复制到缓冲区。尽管如此,但并未执行检查以确定JSON 值是否会溢出目标缓冲区。
在persistence_json.cpp中,我们可以看到我们可以看到将被溢出的缓冲区存在于堆上的一个FileStorageParser 类中。
847 char buf[CV_FS_MAX_LEN+1024];
其中,persistence.hpp 将 CV_FS_MAX_LEN 定义为:
44
因此,我们的缓冲区大小是 0x1400 (5120) 的长度。在persistence_json.cpp中的如下解析例程中会发生溢出:
565 switch ( *ptr )566 {567 case '\\':568 {569 sz = (int)(ptr - beg);570 if( sz > 0 )571 {572 memcpy(buf + i, beg, sz);573 i += sz;574 }575 ptr++;576 switch ( *ptr )577 {578 case '\\':579 case '\"':580 case '\'': { buf[i++] = *ptr; break; }581 case 'n' : { buf[i++] = '\n'; break; }582 case 'r' : { buf[i++] = '\r'; break; }583 case 't' : { buf[i++] = '\t'; break; }584 case 'b' : { buf[i++] = '\b'; break; }585 case 'f' : { buf[i++] = '\f'; break; }586 case 'u' : { CV_PARSE_ERROR_CPP( "'\\uXXXX' currently not supported" ); break; }587 default : { CV_PARSE_ERROR_CPP( "Invalid escape character" ); }588 break;589 }590 ptr++;591 beg = ptr;592 break;593 }594 case '\0':595 {596 sz = (int)(ptr - beg);597 if( sz > 0 )598 {599 memcpy(buf + i, beg, sz); [0]600 i += sz;601 }602 ptr = fs->gets();603 if ( !ptr || !*ptr )604 CV_PARSE_ERROR_CPP( "'\"' - right-quote of string is missing" );605606 beg = ptr;607 break;608 }
溢出发生在第599行。溢出发生的原因在于缓冲区的大小是固定的,但memcpy 的大小被计算为整个 JSON实体值(第596行)的长度,而不会检查它是否超出了目标缓冲区。
我们可以看到易受攻击的 memcpy 操作发生在此处:
0x417999 call memcpy@plt <0x406470>dest: 0x91c380 ◂— 0x42 /* 'B' */src: 0x911dee ◂— 0x4242424242424242 ('BBBBBBBB')n: 0x1460
如果缓冲区的大小仅为 0x1400 (5120),则 memcpy 的大小(json对之一的整个值)即0x1460 个字节,这样在后续的堆对象中会存在溢出,从而导致潜在的代码执行后果。
目标缓冲区位于“FileStorageParser”对象本身当中:
type = class cv::JSONParser : public cv::FileStorageParser {public:cv::FileStorage_API *fs;char buf[5120];JSONParser(cv::FileStorage_API *);virtual ~JSONParser(void);char * skipSpaces(char *);char * parseKey(char *, cv::FileNode &, cv::FileNode &);virtual bool getBase64Row(char *, int, char *&, char *&);char * parseValue(char *, cv::FileNode &);char * parseSeq(char *, cv::FileNode &);char * parseMap(char *, cv::FileNode &);virtual bool parse(char *);} *
在这个具体案例中,该实例的堆对象位于0x91c350处,该缓冲区位于0x91c380处。
Heap chunk: 0x91c358 (malloc address)Heap chunk header: 0x91c350Size: 0x1430 (5168): 0x1440 (5184)Status: in USEPrev size field: 0x0 (0)Raw Size: 0x1431 (5169)Flags: PREV_INUSE000000000091c340: 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............000000000091c350: 00 00 00 00 00 00 00 00 31 14 00 00 00 00 00 00 ........1.......000000000091c360: c8 3d 8e 00 00 00 00 00 01 00 00 00 01 00 00 00 .=..............000000000091c370: 18 3e 8e 00 00 00 00 00 60 04 91 00 00 00 00 00 .>......`...........000000000091d770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000000000091d780: 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 ........!.......
堆中的下一个对象位于 0x91d780 处,距离恰好是 0x1400 (5120) 个字节:
Heap chunk: 0x91d788 (malloc address)Heap chunk header: 0x91d780Size: 0x20 (32): 0x30 (48)Status: in USEPrev size field: 0x0 (0)Raw Size: 0x21 (33)Flags: PREV_INUSE000000000091d770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................000000000091d780: 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 ........!...........000000000091d790: 00 00 00 00 00 00 00 00 10 e0 8f 00 00 00 00 00 ................000000000091d7a0: 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 ........!.......
Memcpy()操作之后的对象如下:
Heap chunk: 0x91c358 (malloc address)Heap chunk header: 0x91c350Size: 0x1430 (5168): 0x1440 (5184)Status: is FREEFD: 0x8e3dc8BK: 0x100000001Prev size field: 0x0 (0)Raw Size: 0x1431 (5169)Flags: PREV_INUSE000000000091c340: 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............000000000091c350: 00 00 00 00 00 00 00 00 31 14 00 00 00 00 00 00 ........1.......000000000091c360: c8 3d 8e 00 00 00 00 00 01 00 00 00 01 00 00 00 .=..............000000000091c370: 18 3e 8e 00 00 00 00 00 60 04 91 00 00 00 00 00 .>......`...........000000000091d770: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB000000000091d780: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
0x91d780处的堆对象显然被损坏了:
0x91d780: 0x4242424242424242 0x42424242424242420x91d790: 0x4242424242424242 0x42424242424242420x91d7a0: 0x4242424242424242 0x42424242424242420x91d7b0: 0x4242424242424242 0x42424242424242420x91d7c0: 0x4242424242424242 0x42424242424242420x91d7d0: 0x4242424242424242 0x4141414141414141
如果我们继续让它运行,该攻击的具体变体将触发任意的free():
Program received signal SIGSEGV, Segmentation fault.__GI___libc_free (mem=0x4141414141414141) at malloc.c:31093109 p = mem2chunk (mem);
漏洞已修复
2019年7月22日,思科 Talos 团队将问题告知 OpenCV,漏洞已于2019年12月19日修复。
奇安信代码卫士 (codesafe)
国内首个专注于软件开发安全的产品线。
点个“在看”,bounty 多多~
