声明
出品|博客(ID:moon_flower)
======================
以下内容,来自moon_flower作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
Origin Potato(MS08-068)
原理就是上文提到的ntlm反射,
重点看一下修复,微软在kb957097补丁中通过修复SMB身份验证答复的验证方式来防止凭据重播。当主机A向主机B进行SMB认证的时候,将 pszTargetName设置为cifs/B,然后在 type2拿到B发送的Challenge之后,在lsass里面缓存 (Challenge,cifs/B),接着B拿到A的ype3,这时会去检查lsass缓存里是否有 (Challenge,cifs/B),如果有就说明这是同一台主机,那么认证失败。
Hot Potato(MS16-075)
影响范围Windows 7,8,10,Server 2008以及Server 2012,经典的ntlm relay 攻击链,依靠windows update触发。
工具地址:
https://github.com/foxglovesec/Potato
实现流程:
1.本地 NBNS Spoofer :冒充名称解析,强制系统下载恶意 WAPD 配置2.伪造 WPAD 代理服务器:部署 malicios WAPD 配置,强制系统进行 NTLM 认证3.HTTP -> SMB NTLM 中继:将 WAPD NTLM 令牌中继到 SMB 服务以创建提升的进程
流程分3步,首先进行本地NBNS欺骗,windows通常通过这一协议进行域名
解析,当windows在hosts文件和dns查询都搜索失败后,会再进行NBNS
查询,在本地广播域中向所有的主机发出UDP广播询问。
但是直接嗅探到网络流量信息需要本地管理员权限,这里曲线救国。
如果我们能提前知道NBNS请求所对应的目标主机的主机名(目标主机 127.0.0.1),就可以创建一个虚假的应答信息,并快速地使用NBNS应答信息来对目标主机进行泛洪攻击。
因为NBNS数据包中有一个长度为2子节的数据-TXID,要求与请求和应答信息相匹配,所以要通过泛洪暴力枚举65536 个可能性。
这是没有匹配到dns的情况,如果之前就保存了主机的DNS记录,那么可以使用UDP端口枯竭(使每一个UDP端口失效),迫使目标系统中所有的DNS查询失败。
下一步是伪造WPAD代理服务器,在windows操作系统中,IE浏览器在默认情况下会通过http://wpad/wpad.dat来自动尝试检查网络代理,同时也有其它一些windows中的服务会采用这一机制(比如利用中提到的Windows Update)。
但是并不是所有网络中都可以正常访问这个url(不是所有的dns域名服务器都存在主机wpad),那么我们就可以伪造一个WPAD代理服务器,结合前文的本地NBNS欺骗,就可以声称WPAD主机的ip地址是目标地址(127.0.0.1)。
这时127.0.0.1本地运行一个HTTP服务器,当收到http://wpad/wpad.dat请求时,做以下答复:
FindProxyForURL(url,host){ if (dnsDomainIs(host, "localhost")) return "DIRECT"; return "PROXY 127.0.0.1:80";
这样目标上所有的 http 流量都通过127.0.01重定向。之前对于NTLM 反射的补丁只限于SMB->SMB,但NTLM支持跨协议,也就是说像HTTP->SMB仍可正常工作。
现在HTTP流量都会途径我们控制的HTTP服务器,那么就可以将其重定向到
URL: http://localhost/GETHASHESxxxxx,以NTLM身份验证的401请求响应(其中xxxxx是某个唯一标识符)。然后将NTLM凭据中继到本地SMB监听器以创建运行用户定义命令的新系统服务。
当这个请求由高权限发起的时候(比如windows update,system权限),就完成了提权。再看一下漏洞的触发,依赖于发送http://wpad/wpad.dat请求,而当windows已经由WPAD的缓存条目或因为没有找到WPAD而允许直接上网时,需要30-60min才会刷新。
Rotten Potato(MS16-075的变种)
通过DCOM call来使服务向攻击者监听的端口发起连接并进行NTLM认证,
需要SelmpersonatePrivilege权限。
可以立即触发,不需要等待windows更新。
影响范围:< win10 1809和windows server 2019
实现流程:
1.通过 NT AUTHORITY/SYSTEM 运行的 RPC 将尝试通过 CoGetInstanceFromIStorage API 调用向我们的本地代理进行身份验证2.135 端口的 RPC 将用于回复第一个 RPC 正在执行的所有请求充当模板3.AcceptSecurityContextAPI 调用以在本地模拟 NT AUTHORITY/SYSTEM
首先是用CoGetInstanceFromIStorage尝试从调用者(system)指定的位置获取指定对象的实例,下面代码试图从127.0.0.1的6666端口上获取一个BITS对象。(实际上是从IStorage中获取对象)
其中,CLSID是标识COM类对象的全局唯一标识符,类似uuid。BITS(后台只能传输服务)实现从HTTP web服务器和SMB服务实现文件共享,BITS实现了 IMarshal接口并允许代理声明强制NTLM身份验证。
public static void BootstrapComMarshal(){IStorage stg = ComUtils.CreateStorage();//使用已知的本地系统服务 COM 服务器,在此强制执行 BITSv1Guid clsid = new Guid("4991d34b-80a1-4291-83b6-3328366b9097");TestClass c = new TestClass(stg, String.Format("{0}[{1}]", "127.0.0.1", 6666)); // ip and portMULTI_QI[] qis = new MULTI_QI[1];qis[0].pIID = ComUtils.IID_IUnknownPtr;qis[0].pItf = null;qis[0].hr = 0;CoGetInstanceFromIStorage(null, ref clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, c, 1, qis);}
现在有一个COM试图连接127.0.0.1:6666(通过RPC协议),那么我们在6666
端口上建立一个本地TCP监听器,如果这时我们以正确的方式回复,
那么这个COM(system 权限运行)就会尝试与我们进行NTLM身份验证。在这里我们做的是将6666接收到的数据包中继到本地的135端口的RPC监听上,并将135端口返回的数据包作为回复COM的模板。
如果从调用函数的层面理解NTLM的认证过程,有下图:
重点看服务端的调用,首先调用acquirecdentialshandle获取相应的句柄,然后用AcceptSecurityContext处理type1,这个函数的输出就是type2的消息,该消息将被发送回试图进行身份验证的客户端,这里就是DCOM。
当客户端回复type3后,服务端将其传递给AcceptSecurityContext,以完成身份验证并获得令牌。在我们的攻击中,type1被转发到了RPC的135端口上,RPC回复一个Type2,但不是直接转发回去,需要在中转的时候进行一些处理,这里做的使用AcceptSecurityContext 调用的结果替换发送到COM数据包中的NTLM blob(?)。
但为什么要这样?因为我们需要的是用system账户运行的COM来完成NTLM Challenge和Reserved(我们使用这两个部分来协商本地令牌),所以如果不替换,后续再次调用AcceptSecurityContext就会失败。
到现在为止,我们能确定的是,客户端以system权限执行的COM需要对服务端返回的NTLM type2数据包中的NTLM Server Challenge和Reserved部分进行一些操作(magic?)
而只有对AcceptSecurityContext生成的结果执行这些操作的时候,才能获得令牌。
这里的Reserved字段实际上是对SecHandle的引用,当system账户接收到 NTLM type2的消息时,会在内存中进行Reserved验证(如果没有替换,将被认证为RPC而不是我们。
完成上述操作后,system权限运行的COM将向我们发送type3(是空的?),
但会用它来调用AcceptSecurityContext。最后使用其调用结果用
ImpersonateSecurityContext获得一个模拟令牌。
有了模拟令牌,根据 SeImpersonate 权限的特性,可以以此令牌创建进程(基本直接翻译原文,其中有很多地方还不是很理解,有错误希望各位师傅指出)
原版的Rotten Potato的实现基于meterpreter shell,后来有人写了webshell的版本(也就是Lonely Potato)。
复现:新建一个IIS服务器,传上shell,连上马
尝试获取不同权限,直接cd到public中可以获取IUSR权限
用烂土豆提权:
PrintSpoofer
看这个洞之前先回顾一下getsystem的原理那篇文章,提权的原理就是诱使 system权限的服务访问我们指定的命名管道,getsystem提供各种模式欺骗 system连接管道:
其中第5个就是PrintSpoofer,利用了打印机组件路径检查的bug。
项目地址:
https://github.com/leechristensen/SpoolSample
Windows的MS-RPRN协议用于打印客户机和打印服务器之间的通信,默认情况下启用。
协议定义的RpcRemoteFindFirstPrinter-ChangeNotificationEx()
调用创建一个远程更改通知对象,该对象监视对打印机对象的更改,并将更改通知发送到打印客户端。
DWORD RpcRemoteFindFirstPrinterChangeNotificationEx( /* [in] */ PRINTER_HANDLE hPrinter, /* [in] */ DWORD fdwFlags, /* [in] */ DWORD fdwOptions, /* [unique][string][in] */ wchar_t *pszLocalMachine, /* [in] */ DWORD dwPrinterLocal, /* [unique][in] */ RPC_V2_NOTIFY_OPTIONS *pOptions)
同时,Print Spooler服务的RPC接口暴露在命名管道:\\.\pipe\spoolss 中,
该服务默认开启。其中pszLocalMachine是指向表示客户端计算机名称的
字符的指针,需要传递一个UNC路径,传递\\127.0.0.1时,服务器会访问\\127.0.0.1\pipe\spoolss,但这个管道已经被系统注册了,并由NT AUTHORITY\SYSTEM控制。那么下一步就是要想办法把这个请求让
我们准备好的恶意管道接收。
考虑到UNC路径的性质,如果主机名包含/,它将通过路径检查,但真正连接的时候会转化为\ 。那么,如果传递一个\\127.0.0.1/pipe/foo,检查时会认为127.0.0.1/pipe/foo是一个主机名,随后在连接named pipe时会对参数做标准化,于是就会连接\\127.0.0.1\pipe\foo\pipe\spoolss,那么攻击者就可以把主机名改为\\127.0.0.1/pipe/foo并注册这个named pipe从而窃取 client的token。
工具地址:
https://github.com/itm4n/PrintSpoofer
还有crisprss修改的免杀版:
https://github.com/crisprss/PrintSpoofer
参考
-
https://www.anquanke.com/post/id/92908
-
https://forum.butian.net/share/860
-
https://tttang.com/archive/1560/
-
https://www.anquanke.com/post/id/217397
-
https://www.anquanke.com/post/id/83322
-
https://www.anquanke.com/post/id/194514
欢迎关注长白山攻防实验室微信公众号
定期更新优质文章分享