聚焦源代码安全,网罗国内外最新资讯!

编译:奇安信代码卫士团队

开源计算机视觉库 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  #define CV_FS_MAX_LEN 4096

因此,我们的缓冲区大小是 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                                 else
601                                 {
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                                     else
619                                     {
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)Size+Hdr: 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)Size+Hdr: 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)Size+Hdr: 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  0x4242424242424242
0x91d3a0:   0x4242424242424242  0x4242424242424242
0x91d3b0:   0x4242424242424242  0x4242424242424242
0x91d3c0:   0x4242424242424242  0x4242424242424242
0x91d3d0:   0x4242424242424242  0x4242424242424242
0x91d3e0:   0x4242424242424242  0x4242424242424242
0x91d3f0:   0x4242424242424242  0x4242424242424242
0x91d400:   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:481
481     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  #define CV_FS_MAX_LEN 4096

因此,我们的缓冲区大小是 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" );
605
606                             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)Size+Hdr: 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)Size+Hdr: 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)Size+Hdr: 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  0x4242424242424242
0x91d790:   0x4242424242424242  0x4242424242424242
0x91d7a0:   0x4242424242424242  0x4242424242424242
0x91d7b0:   0x4242424242424242  0x4242424242424242
0x91d7c0:   0x4242424242424242  0x4242424242424242
0x91d7d0:   0x4242424242424242  0x4141414141414141

如果我们继续让它运行,该攻击的具体变体将触发任意的free():

Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x4141414141414141) at malloc.c:3109
3109      p = mem2chunk (mem);

漏洞已修复

2019年7月22日,思科 Talos 团队将问题告知 OpenCV,漏洞已于2019年12月19日修复。

推荐阅读

2020年1月1日起,谷歌 Patch Rewards 计划将降低准入门槛,提升开源项目的安全性

突发:俄罗斯警方突袭开源 Web 服务器 NGINX 的莫斯科代表处,拘留两名联合创始人

谷歌开源文件访问漏洞审计工具 PathAuditor(详解)

原文链接

https://talosintelligence.com/vulnerability_reports/TALOS-2019-0853

https://talosintelligence.com/vulnerability_reports/TALOS-2019-0852

题图:Pixabay License

本文由奇安信代码卫士编译,不代表奇安信观点,转载请注明“转自奇安信代码卫士 www.codesafe.cn”

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的产品线。

点个“在看”,bounty 多多~

开源计算机视觉库 OpenCV 被曝两个严重的任意代码执行漏洞(详情)相关推荐

  1. 基于qtc++设计文本编辑器的代码_文本编辑器Vim/Neovim被曝任意代码执行漏洞,Notepad:兄弟等你好久了...

    犹记前些日子,微软的记事本文本编辑器爆出了本地代码执行漏洞. Google Project Zero研究员Tavis Ormandy宣布在微软的记事本文本编辑器中发现代码执行漏洞. 可以看见,他在no ...

  2. 文本编辑器Vim/Neovim被曝任意代码执行漏洞,Notepad:兄弟等你好久了

    犹记前些日子,微软的记事本文本编辑器爆出了本地代码执行漏洞. Google Project Zero研究员Tavis Ormandy宣布在微软的记事本文本编辑器中发现代码执行漏洞. 可以看见,他在no ...

  3. 开源计算机视觉库OpenCV详解

    目录 1.概述 2.OpenCV详细介绍 2.1.OpenCV的起源 2.2.OpenCV开发语言 2.3.OpenCV的应用领域 3.OpenCV模块划分 4.OpenCV源码文件结构 4.1.根目 ...

  4. 开源高性能 RISC-V 处理器“香山”国际亮相;Apache Log4j 远程代码执行漏洞;DeepMind 拥有 2800 亿参数的模型 | 开源日报

    整理 | 宋彤彤 责编 | 郑丽媛 开源吞噬世界的趋势下,借助开源软件,基于开源协议,任何人都可以得到项目的源代码,加以学习.修改,甚至是重新分发.关注「开源日报」,一文速览国内外今日的开源大事件吧! ...

  5. 详细分析开源软件 ExifTool 的任意代码执行漏洞 (CVE-2021-22204)

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士 本文作者详述了自己如何从 ExifTool 发现漏洞的过程. 背景 在查看我最喜欢的漏洞奖励计划时,我发现他们使用ExifTool 从所上 ...

  6. opencv 训练人脸对比_【项目案例python与人脸识别】基于OpenCV开源计算机视觉库的人脸识别之python实现...

    " 本项目是一个基于OpenCV开源库使用python语言程序实现人脸检测的项目,该项目将从[项目基础知识](即人脸识别的基本原理).[项目实践](人脸识别所需要的具体步骤及其python程 ...

  7. OpenCV 2.4.0 正式版发布,开源计算机视觉库

    OpenCV 于近日发布了 2.4.0 正式版. OpenCV是一个基于BSD许可证授权发行的跨平台开源计算机视觉库,可以运行在Linux.Windows和Mac OS操作系统上.作为一款简洁而且高效 ...

  8. Github项目|几行代码即可实现人脸检测、目标检测的开源计算机视觉库

    关注&置顶"算法猿的成长" 每日8:30,干货速递! 2019 年第 73 篇文章,总第 97 篇文章 今天介绍一个简单.易用的开源计算机视觉库,名字是 cvlib,其 G ...

  9. C++计算机视觉库OpenCV在Visual Studio 2022的配置方法

      本文介绍在Visual Studio 2022中配置.编译C++ 计算机视觉库OpenCV的方法. 1 OpenCV库配置   首先,我们进行OpenCV库的下载与安装.作为一个开源的库,我们直接 ...

最新文章

  1. linux下搭建mrbs会议室预定管理系统
  2. 利用spring AOP注解实现日志管理
  3. Rstudio修改背景颜色和源
  4. Spartacus同SAP Commerce Cloud交互的示意图
  5. shell中source与sh区别
  6. 传智播客软件测试第一期_播客:冒险如何推动一位软件工程师的职业发展
  7. 前端开发从项目中获得什么_我如何获得副项目的前10个客户以及从他们那里学到的东西...
  8. LeetCode刷题——279. 完全平方数
  9. charshow需求说明
  10. otdr测试曲线图软件通用,OTDR常见测试曲线
  11. keil4for51与keil4forARM的安装与兼容
  12. BT文件分享服务器,bt是什么意思服务(bt资源库)
  13. Android AVD 存放路径修改
  14. 动态表情包制作?gif动态图怎么制作?
  15. homeassistant 快速入门
  16. 川大《计算机应用基础》第二次作业,川大16秋《计算机应用基础》第二次作业答案.pdf...
  17. 测试人员花样甩锅技巧
  18. Android-圆形头像
  19. lda plda主题模型
  20. uni-app项目配置手机端底部的tab栏(一)

热门文章

  1. 艾伟:如何实现用返回值重载
  2. 如何在三层交换机上实现跨VLAN 的DHCP配置
  3. NLog自定义字段写入数据库表,示例
  4. SGU 274 Spam-filter
  5. 在GridView的行绑定中应用Animation动画效果
  6. Android开发学习之基于ViewPager实现Gallery画廊效果
  7. 之前跳槽面试时整理的一些知识点
  8. mybatis分页应用
  9. String、StringBuilder、StringBuffer 区别
  10. 阿里云服务器购买流程详细2019更新(图文教程)...