在本文中,我们将为读者详细介绍安全人员最近在Web代理Squid中发现的一个缓冲区溢出漏洞。需要注意的是,远程的、未经身份验证的攻击者可以通过向目标服务器发送精心设计的请求来利用这个溢出漏洞,进而可以在Squid进程的上下文中执行代码。
[Squid是一款非常流行的开源Internet代理和Web缓存应用程序。它可用于降低Web服务器的带宽用量,过滤网络流量,并通过本地缓存常用资源来加速Web访问。此外,Squid还支持各种网络协议,其中包括HTTP、FTP和Gopher协议。同时,Squid不仅支持前向代理,使得内部网络上的客户端可以通过Squid连接到外部网络上的服务器;而且也支持反向代理,使得外部网络上的客户端可以通过Squid连接到内部网络上的服务器,此外还支持拦截或透明代理,以便为客户端和服务器提供透明的连接。
在Squid中,它为用户提供了缓存管理器接口cachemgr.cgi
,可用于展示Web上Squid代理进程方面的统计信息。除了显示统计信息之外,缓存管理器还可用于管理缓存,而无需登录Squid服务器。
HTTP是由RFC 7230-7237及其他RFC中描述的一种请求/响应型通信协议。其中,请求将从客户端发送到服务器,之后,服务器又会将响应发送回客户端。HTTP请求由请求行、各种头部,空行和可选的消息正文组成:
其中,CRLF
表示回车换行,而SP则表示空格字符。另外,各个参数可以放在Request-URI或消息正文中,以名称-值对的形式从客户端传递给服务器;具体放到哪里,要视采用的Method和Content-Type头部而定。例如,如果要使用HTTP请求传递一个名为param
且值为1
的参数,则可以使用GET方法,具体如下所示:
HTTP支持多种身份验证方案,并且大多方案都会用到“Authorization”头部,其格式如下所示:
其中,<type>常见的取值包括“Basic”、“Digest”、“OAuth”和“Negotiate”。</type>
安全研究人员发现,Squid中存在一个缓冲区溢出漏洞。当Squid收到针对cachemgr
的传入请求时,将调用CacheManager::ParseHeaders()
函数来解析请求的头部。这方面的一个例子是对“REQUEST-URI”的HTTP请求,该请求以“cache_object
”开头。如果请求头部中包含以“Basic”值开头的“Authorization”头部,则会调用易受攻击的函数HttpHeader::getAuth()
。
类似地,当Squid用作FTP代理并且发送以"ftp"开头的REQUEST-URI
请求时,将调用HttpHeader::getAuth()
函数。
而HttpHeader::getAuth()
函数会用到一个长8192字节的缓冲区decodeAuthToken,准确来说,使用base64_decode_update()函数对<credentials>
进行base64解码后的数据就存放在其中。如果解码的数据的长度超过8192字节,则会发生缓冲区溢出。
远程攻击者可以通过向目标服务器发送精心设计的HTTP请求来利用这个漏洞。攻击者一旦得手,就能够以服务器进程的权限来执行任意代码;如果失败,攻击活动将导致服务器进程异常终止。
下面展示的是取自Squid 4.7版的源代码片段,其中已经加入了相应的代码注释。
下面代码来自src/cache_manager.cc
文件:
下面的代码来自src/clients/FtpGateway.cc
文件:
下面的代码来自src/HttpHeader.cc
文件:
最后,下面的代码来自 lib/base64.c
文件:
以下数据包解码结果展示了从客户端发送给目标Squid代理的攻击请求的相关片段:
如前所述,标准的Authorization头部的格式为:
在“Authorization: Basic”后面,有一个字符串,其中只有一部分显示在了这个片段中。当对整个字符串进行base64解码时,解码结果会溢出8192字节的缓冲区。请注意,base64解码过程产生的输出数据的长度,是输入长度的3/4,因此,传输的字符串实际上必须远大于8192字节才能成功发起攻击。
Squid维护者已经通过最新的commit修复了这个漏洞。根据相关说明,我们发现他们是通过用SBuf替换用来存放解码后的base64令牌的具有固定大小的缓冲区来修复这个安全问题的。他们还更新了对SBuf应用编程接口操作的调用方式,以提高内存管理的效率。