严重程度和缓解
不要惊慌!这不是openssh中的漏洞,因此它不会影响我们大家每天使用的ssh。 libssh2是客户端的C库,使应用程序可以连接到SSH服务器。这也不是libssh中的漏洞,它是一个不相关的C库,提供与libssh2类似的功能。
该漏洞存在于libssh2 1.9.0及更低版本中。在撰写本文时,该错误已在master上修复,但尚未发布包含该修复程序的新正式版本。
该漏洞是越界读取,可能导致拒绝服务或远程信息泄露。当使用libssh2连接到恶意SSH服务器时将触发该漏洞。SSH服务器发送断开消息时会发生溢出,这意味着可以在身份验证完成之前在连接过程的早期触发漏洞。
触发漏洞
漏洞的源位置是packet.c:480:
if(message_len < datalen-13) {
datalen
的值不受信任,因为它是由远程SSH服务器控制的。例如如果使用datalen == 11
,则减法将溢出并且针对message_len
的越界检查无效。message_len
是一个32位无符号整数,它也由远程SSH服务器控制,所以这可能导致在第485行上读取越界:
language_len =
_libssh2_ntohu32(data + 9 + message_len);
越界读取通常只会导致分段错误,但是这次的问题也可能会导致调用在 第499行的LIBSSH2_DISCONNECT
:
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
)。
这里编写了一个 概念验证漏洞 ,其中恶意SSH服务器使用datalen == 11
和返回断开连接消息message_len == 0x41414141
,这导致libssh2因分段错误而崩溃。
libssh2中整数溢出的变体分析
当Kevin Backhouse向供应商报告安全漏洞时,通常会尝试在报告中包括两点:
- 一个错误的概念证明利用。
- 一个QL查询,它标识了我认为应该固定的所有代码位置。
PoC包括QL查询和有几个好处:
- 如果代码中包含几个非常相似的错误,那么我可以编写一个列举所有错误的查询。
- 该查询使我能够轻松地检查错误是否已修复(在90天的最后期限即将到期时,这非常方便)。
- 我可以将QL查询及其结果列表作为单个URL包括在内,这对我来说很方便,希望对接收者也很方便。
创建PoC通常需要大量工作,因此,如果目标存在多个非常相似的错误,那么Kevin Backhouse通常只为其中一个编写PoC。Kevin Backhouse的感觉是,一个PoC足以证明安全影响是真实的,前提是其他变体显然非常相似。下面的查询针对此用例进行了调整。该查询的目的不是在libssh2中找到所有整数溢出漏洞,而且由于查询中编码了某些libssh2特定的细节,它也不会扩展到其他代码库。(跨多个代码库扩展通常是变体分析的主要目标之一。)相反,其目标是找到PoC触发的错误以及其他紧密变体。
/**
* @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) {
source.asExpr().(FunctionCall).getTarget().getName().matches("_libssh2_ntoh%")
}
override predicate isSink(DataFlow::Node sink) {
convertedExprMightOverflowNegatively(sink.asExpr()) and
exists(RelationalOperation cmp | cmp.getAnOperand() = sink.asExpr())
}
override predicate isAdditionalFlowStep(DataFlow::Node source,
DataFlow::Node target) {
exists(Field f |
source.asExpr() = f.getAnAssignedValue() and
target.asExpr() = f.getAnAccess())
or
target.asExpr().(AddExpr).getAnOperand() = source.asExpr()
or
target.asExpr().(SubExpr).getAnOperand() = source.asExpr()
}
}
from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink,
"possible integer overflow of tainted expression in bounds check"
该查询与Kevin Backhouse的同事FermínSerna针对U-Boot NFS漏洞编写的查询非常相似。该isSource
谓词找出将呼叫_libssh2_ntohu32
和_libssh2_ntohu64
,这是用于网络主机字节顺序转换。这些功能通常是“攻击者控制的数据”的出色代理。但是我的isSink
断言与费米的断言完全不同。它查找包含可能会溢出的子表达式的比较操作。例如,message_len < datalen-13
是一个比较表达式,其中子表达式datalen-13
可能会溢出。我的查询还覆盖了可选isAdditionalFlowStep
谓词,以自定义数据流边缘集。我对该谓词进行了调整,以生成精确的近似变体列表。
LGTM的一个不错的功能是它永久保存查询结果。 此链接 显示了在2019年7月1日运行查询的结果。您可以在此处查看 2019年10月10日运行相同查询的结果。在2019年7月1日报告该错误时向libssh2团队发送了第一个链接。此后,大部分结果已修复,但是您可以从第二个链接中看到一些新结果也已出现。尚未调查任何新结果是否可利用。
时间线
- 2019年7月1日:我向libssh2团队秘密披露了该漏洞。
- 2019年7月2日:libssh2团队发送了初步答复。
- 2019年8月30日:libssh2团队修复了其开发分支中的错误。
- 2019年10月10日:我通知libssh2团队我们90天的披露截止日期已到期,并询问他们何时计划发布新版本。
- 2019年10月11日:分配了CVE-2019-17498。
- 2019年10月15日:我通过在GitHub上发布我的PoC公开披露CVE-2019-17498 。
*编译整理:Domino
*参考来源:semmle