欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

对 libssh2 整数溢出漏洞 (CVE-2019-17498)的分析

来源:本站整理 作者:佚名 时间:2019-11-07 TAG: 我要投稿

0x01 漏洞挖掘
在2019年3月18日,Canonical Ltd.的Chris Coulson披露了libssh2中的九个漏洞(CVE-2019-3855至CVE-2019-3863)。这些漏洞已在libssh2 v1.8.1中修复。当时,我的同事Pavel Avgustinov注意到,修复漏洞的报告在LGTM上引入了多个新告警。这些告警是由于像下面的代码:
 if((p_len = _libssh2_get_c_string(&buf, &p))
问题出现在_libssh2_get_c_string返回 -1是一个error code,但是p_len没有符号,因此错误条件将被忽略。libssh2团队已经在后面的的报告中修复了这些问题,但是它促使我们仔细查看代码以查看其包含明显错误的漏洞。我们很快发现了这个用于边界检查的函数:
 int _libssh2_check_length(struct string_buf *buf, size_t len)
 {
     return ((int)(buf->dataptr - buf->data) len - len)) ? 1 : 0;
 }
此函数的问题在于强制转换int可能会溢出。左侧的强制转换是安全的,因为的字段buf是受信任的值,但是右侧的强制转换是不安全的,因为的值len是不受信任的。创建漏洞利用exp并非难事,该漏洞利用使值len大于绕过此溢出检查buf->len + 0x80000000。可以在GitHub上找到PoC 。
我后来了解到,该问题_libssh2_check_length是在1.8.2版发布后在主要开发分支上引入的,因此漏洞范围检查在1.8.2版中不存在。不幸的是,版本1.8.2不包含任何边界检查,因此PoC仍然有效。在1.8.2版中,漏洞的源位置是kex.c:1675。问题在于其中p_len包含一个不受信任的值,因此后续的读取s可能会超出范围。因为_libssh2_check_length在1.8.2版中不存在,所以不需要将值p_len大于0x80000000触发漏洞。这意味着较小的值len可以触发越界读取,该漏洞更有可能被利用来实现远程信息泄露。
0x02  negative error codes 转换为 unsigned
当我和我的同事正在查看漏洞补丁报告时,我们注意到一种常见的错误模式,其中将negative error返回值强制转换为unsigned。这是一个非常容易犯的错误,并且它没有被编译器警告所](https://godbolt.org/z/CvqwDm)捕获。所以我写了这个简单的查询来查找漏洞实例:
 import cpp
 import semmle.code.cpp.dataflow.DataFlow
 import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
 
 from Function f, FunctionCall call, ReturnStmt ret, DataFlow::Node source, DataFlow::Node sink
 where call.getTarget() = f
 and ret.getEnclosingFunction() = f
 and ret.getExpr().getValue().toInt()
查询代码如下:
 r_len = _libssh2_get_c_string(&buf, &r);
 if(r_len
该查询会返回负整数常量的函数。例如,_libssh2_get_c_string在773行上执行此操作,然后,它将查找对该函数的调用,这些调用返回值并将其转换为无符号类型。
0x03 触发漏洞
漏洞的源位置是packet.c:480:
 if(message_len
值datalen不受信任,因为它是由远程SSH服务器控制的。例如,如果使用datalen == 11,则减法将溢出并且的边界检查message_len无效。message_len是一个32位无符号整数,它也由远程SSH服务器控制,因此这可能导致在第485行上越界读取:
 language_len =
     _libssh2_ntohu32(data + 9 + message_len);
越界读取通常只会导致分段错误,但是LIBSSH2_DISCONNECT在 第499行的调用中也有可能导致其他类型的问题:
 if(session->ssh_msg_disconnect) {
     LIBSSH2_DISCONNECT(session, reason, message,
                        message_len, language, language_len);
 }
取决于libssh2库的使用方式,因为session->ssh_msg_disconnect是一个回调函数,默认情况下为null,但可以由该库的用户设置(通过调用libssh2_session_callback_set)。
我编写了一个 漏洞PoC ,其中恶意SSH服务器使用datalen == 11和返回断开连接消息message_len == 0x41414141,会导致libssh2因分段错误而崩溃。
0x04 libssh2中整数溢出的变体分析
我在2019年6月底之前写了关于libssh2的最后一篇博客文章。为了准确了解该博客文章的技术细节,我需要回过头来再看一下libssh2代码。我注意到许多粗心的边界检查代码,例如上述错误。
变体分析是关于漏洞的所有变体,并在可能的情况下,创建可在多个代码库之间重用的查询。但是我的目标集中在libssh2和我发现的漏洞上。
当我向供应商报告安全漏洞时,通常会尝试在报告中包括两点:
1. 一个漏洞的PoC。
2. 一个QL查询,它标识了我认为存在漏洞的的所有代码位置。
QL查询和PoC有几个好处:
1. 如果代码中包含几个非常相似的错误,那么我可以编写一个列举所有漏洞的查询。
2. 该查询使我能够轻松地检查漏洞是否已修复(在90天的最后期限即将到期时,这非常方便)。
3. 我可以将QL查询及其结果列表作为单个URL包括在内,这对我来说很方便,希望对接收者也很方便。
编写PoC通常需要大量工作,因此,如果存在多个非常相似的漏洞,那么我通常只为其中一个编写PoC。我的感觉是,一个PoC足以证明安全影响是真实的。下面的查询针对此用例进行了调整,该查询的目的不是在libssh2中找到所有整数溢出漏洞,而且由于查询中编码了某些libssh2特定的细节,它也不会扩展到其他代码库。
 /**
  * @kind path-problem
  */
 
 import cpp
 import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
 import semmle.code.cpp.dataflow.TaintTracking
 import DataFlow::PathGraph
 
 class Config extends DataFlow::Configuration {
   Config() { this = "_libssh2_ntohl bounds check overflow" }
 
   override predicate isSource(DataFlow::Node source) {

[1] [2]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载