Skip to content

Commit b94cfc0

Browse files
committed
Grant-AppAdminConsent added
1 parent bac93ae commit b94cfc0

2 files changed

Lines changed: 83 additions & 15 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,12 @@ Please refer to the [Wiki](https://github.com/mlcsec/Graphpython/wiki) for the f
192192
* **Find-DynamicGroups** - Find groups with dynamic membership rules
193193
* **Update-UserPassword** - Update the passwordProfile of the target user (NewUserS3cret@Pass!)
194194
* **Update-UserProperties** - Update a specific user property of the target user
195+
* **Add-UserTAP** - Add new Temporary Access Password (TAP) to target user
196+
* **Add-GroupMember** - Add member to target group
195197
* **Add-ApplicationPassword** - Add client secret to target application
196198
* **Add-ApplicationCertificate** - Add client certificate to target application
197199
* **Add-ApplicationPermission** - Add permission to target application e.g. Mail.Send and attempt to grant admin consent
198-
* **Add-UserTAP** - Add new Temporary Access Password (TAP) to target user
199-
* **Add-GroupMember** - Add member to target group
200+
* **Grant-AppAdminConsent** - Grant admin consent for Graph API permission already assigned to enterprise application
200201
* **Create-Application** - Create new enterprise application with default settings
201202
* **Create-NewUser** - Create new Entra ID user
202203
* **Invite-GuestUser** - Invite guest user to Entra ID
@@ -600,7 +601,7 @@ Graph permission IDs applied to objects can be easily located with detailed expl
600601
- [x] `Get-DeviceConfigurationPolicies` - tidy up the templateReference and assignmentTarget output
601602
- [x] `Add-ApplicationPermission` - updated logic and added ability to grant admin consent for admin permissions assigned from the same command - update `Grant-AppAdminConsent` to handle any failures so users don't have to repeat this whole command again
602603
- New:
603-
- [ ] `Grant-AppAdminConsent` - grant admin consent for requested/applied admin app permissions (if `Add-ApplicationPermission` fails)
604+
- [x] `Grant-AppAdminConsent` - grant admin consent for requested/applied admin app permissions (if `Add-ApplicationPermission` fails)
604605
- [x] `Backdoor-Script` - first user downloads target script content then adds their malicious code, supply updated script as args, encodes then [patch](https://learn.microsoft.com/en-us/graph/api/intune-shared-devicemanagementscript-update?view=graph-rest-beta)
605606
- [ ] `Deploy-MaliciousWin32App` - use IntuneWinAppUtil.exe to package the EXE/MSI and deploy to devices
606607
- check also [here](https://learn.microsoft.com/en-us/graph/api/resources/intune-app-conceptual?view=graph-rest-1.0) for managing iOS, Android, LOB apps etc. via graph

graphpython.py

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,12 @@ def list_commands():
123123
["Find-DynamicGroups", "Find groups with dynamic membership rules"],
124124
["Update-UserPassword", "Update the passwordProfile of the target user (NewUserS3cret@Pass!)"],
125125
["Update-UserProperties", "Update the user properties of the target user"],
126+
["Add-UserTAP", "Add new Temporary Access Password (TAP) to target user"],
127+
["Add-GroupMember", "Add member to target group"],
126128
["Add-ApplicationPassword", "Add client secret to target application"],
127129
["Add-ApplicationCertificate", "Add client certificate to target application"],
128130
["Add-ApplicationPermission", "Add permission to target application e.g. Mail.Send and attempt to grant admin consent"],
129-
["Add-UserTAP", "Add new Temporary Access Password (TAP) to target user"],
130-
["Add-GroupMember", "Add member to target group"],
131+
["Grant-AppAdminConsent", "Grant admin consent for Graph API permission already assigned to enterprise application"],
131132
["Create-Application", "Create new enterprise application with default settings"],
132133
["Create-NewUser", "Create new Entra ID user"],
133134
["Invite-GuestUser", "Invite guest user to Entra ID"],
@@ -654,7 +655,7 @@ def main():
654655
"get-roledefinitions", "get-roleassignments", "display-avpolicyrules", "display-asrpolicyrules", "display-diskencryptionpolicyrules",
655656
"display-firewallrulepolicyrules", "display-lapsaccountprotectionpolicyrules", "display-usergroupaccountprotectionpolicyrules",
656657
"display-edrpolicyrules","add-exclusiongrouptopolicy", "deploy-maliciousscript", "reboot-device", "shutdown-device", "lock-device", "backdoor-script",
657-
"add-applicationpermission", "new-signedjwt", "add-applicationcertificate", "get-application", "locate-permissionid", "get-serviceprincipal"
658+
"add-applicationpermission", "new-signedjwt", "add-applicationcertificate", "get-application", "locate-permissionid", "get-serviceprincipal", "grant-appadminconsent"
658659
]
659660

660661

@@ -733,7 +734,7 @@ def main():
733734
"delete-user", "delete-group", "remove-groupmember", "delete-application", "delete-device", "wipe-device", "retire-device",
734735
"get-caps", "get-devicecategories", "display-devicecompliancepolicies", "get-devicecompliancesummary",
735736
"get-deviceconfigurations", "get-deviceconfigurationpolicies", "get-deviceconfigurationpolicysettings",
736-
"get-deviceenrollmentconfigurations", "get-devicegrouppolicyconfigurations",
737+
"get-deviceenrollmentconfigurations", "get-devicegrouppolicyconfigurations", "grant-appadminconsent",
737738
"get-devicegrouppolicydefinition", "dump-devicemanagementscripts", "update-userproperties",
738739
"get-scriptcontent", "get-roledefinitions", "get-roleassignments", "display-avpolicyrules",
739740
"display-asrpolicyrules", "display-diskencryptionpolicyrules", "display-firewallrulepolicyrules", "backdoor-script",
@@ -3699,7 +3700,7 @@ def read_and_encode_cert(cert_path):
36993700
print_yellow("\n[*] Add-ApplicationPermission")
37003701
print("=" * 80)
37013702

3702-
# 1. CHECK existing permissions
3703+
# 1. check existing permissions
37033704
api_url = f"https://graph.microsoft.com/beta/myorganization/applications(appId='{args.id}')" # app id
37043705
#api_url = f"https://graph.microsoft.com/v1.0/applications/{args.id}" # object id
37053706

@@ -3714,7 +3715,7 @@ def read_and_encode_cert(cert_path):
37143715
response_json = response.json()
37153716
existingperms = response_json.get('requiredResourceAccess', [])
37163717

3717-
# 2. patch
3718+
# 2. patch perms
37183719
api_url = f"https://graph.microsoft.com/beta/myorganization/applications(appId='{args.id}')" # app id
37193720
#api_url = f"https://graph.microsoft.com/v1.0/myorganization/applications/{args.id}" # object id
37203721

@@ -3866,12 +3867,69 @@ def parse_permissionid(content):
38663867
print("=" * 80)
38673868

38683869
# grant-appadminconsent
3869-
# - cover for if the grant fails in above likely due to token privs
3870-
# - instead of repeating whole request this can be used if permissions are updated successfully but the admin consent grant fails
3871-
# need:
3872-
# - admin_data payload and granturl/req
3873-
# - client app id
3874-
# - permission name to be granted e.g. 'Mail.Send' - prompt for input?
3870+
elif args.command and args.command.lower() == "grant-appadminconsent":
3871+
if not args.id:
3872+
print_red("[-] Error: --id required for Grant-AppAdminConsent command")
3873+
return
3874+
3875+
print_yellow("\n[*] Grant-AppAdminConsent")
3876+
print("=" * 80)
3877+
clientAppId = args.id
3878+
user_agent = get_user_agent(args)
3879+
headers = {
3880+
'Authorization': f'Bearer {access_token}',
3881+
'User-Agent': user_agent,
3882+
'Content-Type': 'application/json',
3883+
}
3884+
3885+
script_dir = os.path.dirname(os.path.abspath(__file__))
3886+
file_path = os.path.join(script_dir, '.github', 'graphpermissions.txt')
3887+
try:
3888+
with open(file_path, 'r') as file:
3889+
content = file.read()
3890+
except FileNotFoundError:
3891+
print_red(f"\n[-] The file {file_path} does not exist.")
3892+
sys.exit(1)
3893+
except Exception as e:
3894+
print_red(f"\n[-] An error occurred: {e}")
3895+
sys.exit(1)
3896+
3897+
try:
3898+
permission_names = input("\nEnter Permission Names (comma-separated): ").strip().split(',')
3899+
permission_names = [name.strip() for name in permission_names]
3900+
except KeyboardInterrupt:
3901+
sys.exit()
3902+
3903+
invalid_permissions = [name for name in permission_names if name not in content]
3904+
if invalid_permissions:
3905+
print_red(f"\n[-] Invalid Graph permissions: {', '.join(invalid_permissions)}")
3906+
print("=" * 80)
3907+
sys.exit()
3908+
3909+
admin_data = {
3910+
"clientAppId": clientAppId,
3911+
"onBehalfOfAll": True,
3912+
"checkOnly": False,
3913+
"tags": [],
3914+
"constrainToRra": True,
3915+
"dynamicPermissions": [
3916+
{
3917+
"appIdentifier": "00000003-0000-0000-c000-000000000000",
3918+
"appRoles": permission_names,
3919+
"scopes": []
3920+
}
3921+
]
3922+
}
3923+
3924+
url = "https://graph.microsoft.com/beta/directory/consentToApp"
3925+
request = requests.post(url, headers=headers, json=admin_data)
3926+
3927+
if request.ok:
3928+
print_green(f"\n[+] Admin consent granted for: '{', '.join(permission_names)}'")
3929+
else:
3930+
print_red(f"\n[-] Failed to grant admin consent: {request.status_code}")
3931+
print_red(request.text)
3932+
print("=" * 80)
38753933

38763934
# add-userTAP
38773935
elif args.command and args.command.lower() == "add-usertap":
@@ -6050,8 +6108,10 @@ def parse_permissionid(content):
60506108
if not args.id:
60516109
print_red("[-] Error: --id argument is required for Locate-PermissionID command")
60526110
return
6111+
60536112
print_yellow("\n[*] Locate-PermissionID")
60546113
print("=" * 80)
6114+
60556115
def parse_html(content):
60566116
soup = BeautifulSoup(content, 'html.parser')
60576117
permissions = {}
@@ -6075,10 +6135,12 @@ def parse_html(content):
60756135
permissions[title] = permission_data
60766136

60776137
return permissions
6138+
60786139
def highlight(text, should_highlight):
60796140
if should_highlight:
60806141
return f"\033[92m{text}\033[0m"
60816142
return text
6143+
60826144
def print_permission(permission, data, app_ids, delegated_ids):
60836145
print_green(f"{permission}")
60846146
for category, values in data.items():
@@ -6088,9 +6150,11 @@ def print_permission(permission, data, app_ids, delegated_ids):
60886150
print(f" Application: {highlight(values['Application'], app_highlight)}")
60896151
print(f" Delegated: {highlight(values['Delegated'], delegated_highlight)}")
60906152
print()
6153+
60916154
identifiers = args.id.split(',')
60926155
script_dir = os.path.dirname(os.path.abspath(__file__))
60936156
file_path = os.path.join(script_dir, '.github', 'graphpermissions.txt')
6157+
60946158
try:
60956159
with open(file_path, 'r') as file:
60966160
content = file.read()
@@ -6100,16 +6164,19 @@ def print_permission(permission, data, app_ids, delegated_ids):
61006164
except Exception as e:
61016165
print_red(f"[-] An error occurred: {e}")
61026166
return
6167+
61036168
permissions = parse_html(content)
61046169
app_ids = []
61056170
delegated_ids = []
6171+
61066172
for permission, data in permissions.items():
61076173
if data['Identifier']['Application'] in identifiers:
61086174
app_ids.append(data['Identifier']['Application'])
61096175
if data['Identifier']['Delegated'] in identifiers:
61106176
delegated_ids.append(data['Identifier']['Delegated'])
61116177

61126178
found_permissions = False
6179+
61136180
for permission, data in permissions.items():
61146181
if data['Identifier']['Application'] in app_ids or data['Identifier']['Delegated'] in delegated_ids:
61156182
print_permission(permission, data, app_ids, delegated_ids)

0 commit comments

Comments
 (0)