Created
March 18, 2024 19:18
-
-
Save HackingLZ/e8fa55053181ec1e56e7f01e2891a94a to your computer and use it in GitHub Desktop.
Basic Azure Enum
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import random | |
import re | |
import requests | |
import string | |
import socket | |
import xml.etree.ElementTree as ET | |
def generate_random_username(min_length=7, max_length=16): | |
length = random.randint(min_length, max_length) | |
letters = string.ascii_letters + string.digits | |
return ''.join(random.choice(letters) for _ in range(length)) | |
def get_federation_tenants(search_text): | |
search_term = re.sub("[^A-Za-z0-9@\. -]", '', search_text) | |
headers = { | |
'Content-Type': 'text/xml; charset=utf-8', | |
'SOAPAction': 'http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation', | |
'User-Agent': 'AutodiscoverClient', | |
'Accept-Encoding': 'identity', | |
} | |
url = 'https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc' | |
domain = search_term | |
xml = f'''<?xml version="1.0" encoding="utf-8"?> | |
<soap:Envelope xmlns:exm="http://schemas.microsoft.com/exchange/services/2006/messages" | |
xmlns:ext="http://schemas.microsoft.com/exchange/services/2006/types" | |
xmlns:a="http://www.w3.org/2005/08/addressing" | |
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | |
<soap:Header> | |
<a:Action soap:mustUnderstand="1">http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetFederationInformation</a:Action> | |
<a:To soap:mustUnderstand="1">https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc</a:To> | |
<a:ReplyTo> | |
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> | |
</a:ReplyTo> | |
</soap:Header> | |
<soap:Body> | |
<GetFederationInformationRequestMessage xmlns="http://schemas.microsoft.com/exchange/2010/Autodiscover"> | |
<Request> | |
<Domain>{domain}</Domain> | |
</Request> | |
</GetFederationInformationRequestMessage> | |
</soap:Body> | |
</soap:Envelope>''' | |
try: | |
response = requests.post(url, data=xml, headers=headers) | |
response.raise_for_status() | |
result = response.text | |
except requests.exceptions.RequestException as e: | |
print(f"Error getting federation tenants: {e}") | |
return [] | |
pattern = "<Domain>(.*?)</Domain>" | |
matches = re.findall(pattern, result) | |
return matches | |
def query_user_realm(tenant): | |
fakeuser = generate_random_username() | |
url = f"https://login.microsoftonline.com/getuserrealm.srf?login={fakeuser}@{tenant}&xml=1" | |
try: | |
response = requests.get(url, timeout=20) | |
if response.status_code == 400: | |
return 'Error', 'Domain does not exist' | |
response.raise_for_status() | |
root = ET.fromstring(response.text) | |
namespace_type = root.find('.//NameSpaceType').text if root.find('.//NameSpaceType') is not None else 'Unknown' | |
if namespace_type.lower() == 'federated': | |
sts_auth_url = root.find('.//STSAuthURL').text if root.find('.//STSAuthURL') is not None else 'No STSAuthURL found' | |
return namespace_type, sts_auth_url | |
elif namespace_type.lower() == 'managed': | |
return namespace_type, 'Managed' | |
else: | |
return namespace_type, 'Unknown namespace type' | |
except requests.exceptions.RequestException as e: | |
print(f"Error querying user realm: {e}") | |
return 'Error', 'Could not query user realm' | |
def get_tenant_guid(tenant_domain): | |
url = f"https://login.microsoftonline.com/{tenant_domain}/v2.0/.well-known/openid-configuration" | |
try: | |
response = requests.get(url, timeout=20) | |
if response.status_code == 400: | |
return 'Error querying tenant guid: Domain does not exist' | |
response.raise_for_status() | |
data = response.json() | |
token_endpoint = data.get('token_endpoint', '') | |
match = re.search(r'/([0-9a-fA-F-]+)(?:/oauth2/v2.0/token)?$', token_endpoint) | |
guid = match.group(1) if match else 'GUID not found' | |
return guid | |
except requests.exceptions.RequestException as e: | |
print(f"Error querying tenant guid: {e}") | |
return 'Error querying tenant guid' | |
def extract_tenant(domain): | |
pattern = r'(\w+)\.onmicrosoft\.com' | |
match = re.match(pattern, domain) | |
return match.group(1) if match else None | |
def get_http_code(url): | |
try: | |
response = requests.get(url, timeout=20) | |
return response.status_code | |
except requests.exceptions.RequestException as e: | |
print(f"Error getting status code: {e}") | |
return None | |
def check_service_availability(tenant, service): | |
domain = f"{tenant}.{service}" | |
try: | |
socket.gethostbyname(domain) | |
return domain, "Available" | |
except socket.gaierror: | |
return domain, "Not Available" | |
def check_modern_auth(domain): | |
url = f"https://{domain}" | |
status_code = get_http_code(url) | |
if status_code == 401: | |
return "Enabled" | |
elif status_code == 403: | |
return "Not Enabled" | |
else: | |
return "Status Unknown" | |
def domain_hit(domain): | |
print(f"\nChecking domain: {domain}") | |
namespace_type, sts_auth_url_or_message = query_user_realm(domain) | |
if sts_auth_url_or_message == 'Domain does not exist': | |
print("Domain does not exist.") | |
return | |
print(f"Namespace Type: {namespace_type}, STS Auth URL/Message: {sts_auth_url_or_message}") | |
tenant_guid = get_tenant_guid(domain) | |
if 'Domain does not exist' in tenant_guid: | |
print("Domain does not exist.") | |
return | |
print(f"Tenant GUID: {tenant_guid}") | |
tenants = get_federation_tenants(domain) | |
print("Federation tenants:", tenants) | |
results = { | |
'OriginalDomain': { | |
'NamespaceType': namespace_type, | |
'STSAuthURL/Message': sts_auth_url_or_message, | |
'TenantGUID': tenant_guid, | |
}, | |
'Tenants': {} | |
} | |
for tenant_domain in tenants: | |
tenant = extract_tenant(tenant_domain) | |
if tenant: | |
print(f"\nExtracted tenant: {tenant}") | |
onedrive_status = check_service_availability(tenant, "my.sharepoint.com") | |
sharepoint_status = check_service_availability(tenant, "sharepoint.com") | |
atp_status = check_service_availability(tenant, "atp.azure.com") | |
modern_auth_status = check_modern_auth(sharepoint_status[0]) | |
results['Tenants'][tenant] = { | |
'OneDrive': onedrive_status, | |
'SharePoint': sharepoint_status, | |
'ATP': atp_status, | |
'ModernAuth': modern_auth_status | |
} | |
print("\nResults:") | |
for key, value in results.items(): | |
if key == 'OriginalDomain': | |
print("Original Domain Checks:") | |
for subkey, subvalue in value.items(): | |
print(f" {subkey}: {subvalue}") | |
else: | |
print("Tenant Checks:") | |
for tenant, services in value.items(): | |
print(f"\n Tenant: {tenant}") | |
for service, status in services.items(): | |
print(f" {service}: {status}") | |
parser = argparse.ArgumentParser(description='Check domain information.') | |
parser.add_argument('domain', help='Domain to check, e.g., domain.com') | |
args = parser.parse_args() | |
if __name__ == "__main__": | |
domain_hit(args.domain) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment