上回我们说到,通过ruler可以给已知用户名、口令的用户增加规则,从而在使用Outlook连接Exchange邮箱服务器的主机上做到任意代码执行。那么问题来了,如果不知道该用户的口令,能否控制他们的主机呢?
数据包分析
首先需要明白ruler的具体实现过程和原理,到底是如何给其他用户增加规则、修改主页、发送form的。但是大致上我们可以猜测是给Exchange服务器对应的接口发送了几个数据包做到的。
这些数据包发送到了哪个接口,需要从接口处获得什么作为返回,以便进行下一次的请求。
因此需要对ruler的源码进行阅读同时结合对它发数据包的分析弄清楚整个的流程。
采用Fiddler对ruler的数据包进行解密(试过BurpSuite,但是它对NTLM认证的支持不好,会造成认证失败)
在Fiddler中进行如下操作,最后勾选 Decrypt HTTPS traffic
就能够对发送的流量进行解密。
Tools
Options
HTTPS
Capture HTTPS CONNECTs
Decrypt HTTPS traffic
ruler自带了proxy选项,只需设置proxy为Fiddler监听的8888端口就能成功抓取到对应流量
--proxy value If you need to use an upstream proxy. Works with https://user:pass@ip:port or https://ip:port
当通过ruler命令 homepage display
查看某用户的收件箱主页时,收发数据包的具体动作如下,共有6个数据包,并且认证的方式是 HTTP Basic
身份认证。当用户访问受限资源时,服务器会返回401状态码要求进行身份认证,身份认证成功之后会进行6个请求,首先请求了 autodiscover/autodiscover.xml
页面,接着在获取了对应邮箱的 MailboxId
之后向对应的mapi接口发送了mapi请求,获得请求结果。
CVE-2018-8581
一个非常经典的漏洞,网上分析该漏洞的文章也较多。通过该漏洞能允许任意用户伪装成Exchange服务器上的其他任何人。通过向SOAP请求向Exchange服务器发送请求能让Exchange服务器向制定的IP地址发送NTLM hash,该hash值可以被我们用来重新与Exchange服务器进行交互,并且该hash值是在 NT\AuthoritySytem
上运行的 ,权限很大。
这说白了就是一种中间人攻击,使用管理员权限的令牌去对其他的用户账户进行相应的设置了。
最初的攻击方式其一是通过NTLM中继读到登录域控的任意用户的NTLMhash,这样就能够登录到该用户主机执行任意命令。
python wmiexec.py -hash LMhash:NThash username@ipaddress
python wmiexec.py -hashes :NThash username@ipaddress
其二是通过NTLM中继以Exchange Server的权限向Exchange Server发送SOAP请求,将某一用户的收件箱委托到当前已经获取用户名及口令的账户上。这样就能以当前已经获取用户名及口令的账户读取其他用户的邮件了。网上大多数的攻击脚本都采用的上面提到的两种攻击方法。
另辟蹊径
很多的漏洞随着时间会渐渐淡出人们的生活,这个周期通常需要一两年,当厂商提出某种新的漏洞利用缓解方式之后,有些漏洞便无法再使用了。如何抓住厂商完善产品“空窗期”,利用漏洞或者某种特性,做更多的事情,是渗透测试人员在每一次红队行动中需要思考的问题。
下载其他用户的邮件
通过高权限的用户token以及SOAP请求,也能够下载其他用户的邮件。SOAP请求的内容需参考微软的文档。
最后做到的效果就是通过python编写的脚本中继pubsubscription的请求,继而修改其他用户收件箱文件夹的权限为 Editor
。修改文件夹权限的关键在于
通过已经获取的账户用户名及口令登录,能够读取其他用户收件箱中的邮件。读取邮件的代码需要用 C#
进行编写,使用 EWS Managed API
开发
给其他用户添加Rule规则
通过之前的抓包我们发现ruler与exchange进行交互的方式仍然是HTTP Basic Auth的方法。这样利用 CVE-2018-8581
的漏洞就能以管理员的权限成功通过认证。希望能够结合中继的请求认证方式,给其他的用户增加rule规则。因为ruler的作者自己实现了一套MAPI的规则,代码全部由golang编写,并且代码量很大。权衡了一下工作量,还是用go语言重写relayNTLM更容易一些,通过NTLM认证之后给其他用户添加规则。
首先要弄清楚的是这6个请求之间的逻辑关系,即在发送后一个请求之前需要从前一个请求当中获取什么信息。
执行ruler并加上 --verbose
参数
$ ./ruler --nocache --insecure --verbose --username user3 --email [email protected] homepage display
Password:
[+] Retrieving MAPI/HTTP info
[*] Autodiscover step 2 - URL: https://internet.local/autodiscover/autodiscover.xml
[*] MAPI URL found: https://exchange2016.internet.local/mapi/emsmdb/[email protected]
[*] MAPI AddressBook URL found:
[*] User DN: /o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=4a22881a84c9483794236d11d38e9098-user3
[*] Got Context, Doing ROPLogin
[*] And we are authenticated
[*] Openning the Inbox
[+] Getting existing endpoint
[+] No endpoint set
[*] And disconnecting from server
除了获取 MailboxId
的请求之外一共有五个请求
1.最开始的请求是获取Context
2.第二个请求是做ROPLogin
3.第三个请求是打开Inbox
4.第四个请求是获得(设置)Rules
5.最后一个请求是关闭连接
前两个请求是发送用户名密码与Exchange进行连接,关键的函数有这样几个,最开始的包发送到 autodiscover/autodiscover.xml
进行认证,获取userDN。获取了userDN之后才能进行认证登录。
connect
MAPIDiscvoer
autodiscover
get userDN, userDN是用来认证登录的
调用 mapi.Authenticate
与Exchange进行登录交互认证,这个过程发送了两个数据包,分别在 sendMapiConnectRequestHTTP
函数以及 AuthenticateFetchMailbox
当中实现。
mapi.Authenticate
AuthenticateHTTP
sendMapiConnectRequestHTTP
AuthenticateFetchMailbox
调用 mapi.getFolder
获取Exchange服务器上当前账户的文件夹信息,通常不同的文件夹有不同的FolderID,这个过程中发送了一个数据包。
mapi.getFolder
GetFolder
GetFolderFromID
接下来根据要实现的功能进行调用研究,对于getrules的功能,构造好getrules的请求,这个过程只发送了一个数据包。
displayRules
printRules
mapi.FetchRules
发送数据包断开连接,经过实验,这个数据包最后也可以舍弃掉,断不断开无所谓,入了就是入了。
mapi.Disconnect
长连接的模拟
在修改程序攻击脚本的过程中遇到了长连接的问题,因为HTTP Basic认证需要在同一个TCP连接中发送和接受数据,所以两次请求的过程中必须保证使用的是同一个TCP连接。用python进行长连接请求用的是 http.client
包当中的 HTTPSConnection
和 HTTPConnection
类。通过这两个类生成长连接发送POST和GET请求。
在golang当中需要类似的方法保持连接并发送POST和GET数据包,搜索了一段时间发现调用 http.Client
类当中的 Do
方法能够发送请求。并且如果将 Client
类变成全局变量就能实现每次发送数据都通过同一个TCP连接。
var tr *http.Transport
var client *http.Client
Cookie设置
go语言当中也能对Cookie进行设置,它也用一个 http.Client
类,初始化的时候提供一个 cookiejar
变量作为参数,长连接中会自动根据返回头中的 Set-Cookie
值设置 cookiejar
中的Cookie
并在下一次请求中携带 Cookie
。
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
jar, _ := cookiejar.New(nil)
client = &http.Client{Transport: tr, Jar: jar}
是否有办法通过Cookie的设置减少需要中继的请求数量?在测试的过程中打印出 cookiejar
中的键值对,查看每次请求过程中发送到服务器上的cookie值。
u, _ := url.Parse(MAPI_URL)
for _, cookie := range client.Jar.Cookies(u){
fmt.Printf("%v = %v\n", cookie.Name, cookie.Value)
}
经过实验发现每次请求和返回会加上一个 MapiSequence
的Cookie, -
前面的数字表示请求的数据包顺序值, -
后面的内容是随机生成的4个字节进行编码后的结果,这个内容不是和用户帐号绑定的,每次生成的字节内容都不一样。前面的数字表示整个Mapi请求进行到了第几阶段,一整套的Mapi请求共有5次,这个数字会从0增长到4。
MapiSequence = 1-VQjiZg==
减少或者跳过其中的步骤会导致认证失败,增加rule规则的话必须得完成前4次交互(最后一次交互断开连接可以不要)。但在有了Cookie的值之后,只要在客户端设置好了Cookie就不用在请求的过程中带上 Authorization
,也就是说不用中继5次NTLM认证了。一次中继之后获得cookie,就能够用这次获得的cookie进行下面的4次请求。
修改其他用户的HomePage
接收到的请求包含了UserAgent,不包含Cookie。UserAgent当中有使用的Outlook版本以及.NET版本、Windows版本、解析html的ie库版本。其中Trident标签在IE8之后都存在,并且是使用的IE浏览器版本号减4。
$ ncat -lvv -p 8000
Ncat: Version 7.80 ( https://nmap.org/ncat )
[...]
GET / HTTP/1.1
Accept: */*
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Microsoft Outlook 16.0.4266)
Host: 192.168.110.100:8000
Connection: Keep-Alive
也就是说修改了用户HomePage之后可以获得对方使用的 Outlook
软件版本信息。修改homepage后在主页代码插入漏洞利用代码,通过IE中存在的漏洞获取用户权限。
攻击效果展示
攻击的过程是通过python脚本让Exchange服务器发送push_subscription请求,同时用golang脚本在本机启动中继服务器,监听端口获得HTTP请求的headers,中继服务器会将请求重新转发到Exchange服务器上,实现任意账户规则的添加。
https://www.bilibili.com/video/BV1kT4y1E7Jj
如果需要中继添加规则的go语言代码可以私信我。
防御办法
将Office更新到最新版本
文件
Office账户
Office更新
更新了之后删除了执行应用程序以及脚本的功能,同时在form表单中无法执行相应的“宏”代码了。
*本文作者:Kriston,转载请注明来自FreeBuf.COM
来源:freebuf.com 2020-06-21 08:00:51 by: Kriston
请登录后发表评论
注册