Closed angry-bender closed 3 months ago
For context, i am using something like this just to try get some items unattended.
Connect-MgGraph -Scopes AuditLog.Read.All, AuditLogsQuery.Read.All, Directory.Read.All, Directory.AccessAsUser.All, Policy.Read.All, IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All, IdentityRiskyUser.Read.All, User.Read.All, UserAuthenticationMethod.Read.All, User.ReadBasic.All > $null
....
try{
Get-OAuthPermissions -OutputDir .\test
## Here is where the browser window comes up again
Get-UALGraph -OutputDir .\test -searchName test
## And again here
Get-ADSignInLogsGraph -OutputDir .\test
}
catch{
Write-Host "ERROR: Log Acquisition failed"
}
Hmmm,
Simplest solution could be to add a alreadyconsented
argument to each of the growth modules, which bypasses the consent π, as it might be an edge case (without a Get-all script) to go this route
Hi, I was just mentioning yesterday that it's been way too quiet since the 2.0 release. ;)
I've been considering removing the Connect-MgGraph part, but it might lead to more GitHub issues from people forgetting to connect before running the script. So, I'm not sure if that's what we want, haha.
Just to clarify, when using delegated permissions (not application permissions) and running commands like Get-MFA
followed by Get-ADSignInLogsGraph
, does your browser open each time, requiring you to close it again? I haven't encountered this issue, possibly because I have full admin consent for the environment. Do you have admin consent for your environment?
We could implement a check using Get-MgContext | Select-Object -ExpandProperty Scopes
to verify if the necessary scopes are present. If they aren't, the script would run Connect-MgGraph
with the appropriate scopes.
@angry-bender would like to hear your thoughts on the following:
I updated the Get-GraphAuthType
function to not only return the type of authentication used but also the scopes of the current connection, if any.
function Get-GraphAuthType {
$context = Get-MgContext
$authType = $context | Select-Object -ExpandProperty AuthType
$scopes = $context | Select-Object -ExpandProperty Scopes
switch ($authType) {
"AppOnly" { return @{ AuthType = "application"; Scopes = $scopes } }
"Delegated" { return @{ AuthType = "delegated"; Scopes = $scopes } }
}
}
In the scripts that uses Microsoft Graph, such as when calling Get-ConditionalAccessPolicies
, I will check if the required scopes in this case, Policy.Read.All
are included in the scopes returned by the Get-GraphAuthType
function. If the required scope is present, the script will skip the reconnection step. If the scope is missing, the script will attempt to connect with the appropriate scope. If no connection is detected at all it will also try to connect with the appropiate scopes.
$graphAuth = Get-GraphAuthType
$requiredScopes = @("Policy.Read.All")
$joinedScopes = $requiredScopes -join ","
if ($graphAuth.AuthType -eq "delegated") {
$missingScopes = @()
foreach ($requiredScope in $requiredScopes) {
if (-not ($graphAuth.Scopes -contains $requiredScope)) {
$missingScopes += $requiredScope
break
}
}
if ($missingScopes.Count -gt 0) {
foreach ($missingScope in $missingScopes) {
Write-LogFile -Message "[INFO] Missing Graph scope detected: $missingScope" -Color "Yellow"
}
Write-LogFile -Message "[INFO] Attempting to re-authenticate with the appropriate scope(s): $joinedScopes" -Color "Green"
Connect-MgGraph -NoWelcome -Scopes $joinedScopes > $null
}
}
elseif ($graphAuth.AuthType -eq "application") {
Continue
}
else {
Write-LogFile -Message "[INFO] No active Connect-MgGraph session found. Attempting to connect with the appropriate scope(s): Connect-MgGraph -Scopes $joinedScopes" -Color "Red"
Connect-MgGraph -Scopes $joinedScopes
}
Currently, I have added this code to the Get-ConditionalAccessPolicies
function to experiment with the idea. If you find it useful, I will try to add this approach to all functions that use Graph. I kinda want to rework this code a little bit so that most of the logic is handled within the Get-GraphAuthType
function, reducing the number of lines needed in each function that useswith Microsoft Graph. In addition, I should also verify if the required scope is included in the application's authentication and provide a warning if it is not.
Did not test, but by adding the application part you will get something like this:
$graphAuth = Get-GraphAuthType
$requiredScopes = @("Policy.Read.All")
$joinedScopes = $requiredScopes -join ","
$missingScopes = @()
foreach ($requiredScope in $requiredScopes) {
if (-not ($graphAuth.Scopes -contains $requiredScope)) {
$missingScopes += $requiredScope
}
}
if ($graphAuth.AuthType -eq "delegated") {
if ($missingScopes.Count -gt 0) {
foreach ($missingScope in $missingScopes) {
Write-LogFile -Message "[INFO] Missing Graph scope detected: $missingScope" -Color "Yellow"
}
Write-LogFile -Message "[INFO] Attempting to re-authenticate with the appropriate scope(s): $joinedScopes" -Color "Green"
Connect-MgGraph -NoWelcome -Scopes $joinedScopes > $null
}
} elseif ($graphAuth.AuthType -eq "application") {
if ($missingScopes.Count -gt 0) {
foreach ($missingScope in $missingScopes) {
Write-LogFile -Message "[INFO] The connected application is missing Graph scope detected: $missingScope" -Color "Red"
}
}
} else {
Write-LogFile -Message "[INFO] No active Connect-MgGraph session found. Attempting to connect with the appropriate scope(s): $joinedScopes" -Color "Red"
Connect-MgGraph -NoWelcome -Scopes $joinedScopes
}
That looks awesome, thanks for that π
Hi, I was just mentioning yesterday that it's been way too quiet since the 2.0 release. ;)
I've been considering removing the Connect-MgGraph part, but it might lead to more GitHub issues from people forgetting to connect before running the script. So, I'm not sure if that's what we want, haha.
Just to clarify, when using delegated permissions (not application permissions) and running commands like
Get-MFA
followed byGet-ADSignInLogsGraph
, does your browser open each time, requiring you to close it again? I haven't encountered this issue, possibly because I have full admin consent for the environment. Do you have admin consent for your environment?We could implement a check using
Get-MgContext | Select-Object -ExpandProperty Scopes
to verify if the necessary scopes are present. If they aren't, the script would runConnect-MgGraph
with the appropriate scopes.
I think for the get all usecase making people consent first up, is going to be a good idea. But for the individual modules just a check and then sign in should work IMO
The proposed changes you have above, look like they would go awesome in a get all function π.
Haha, the get all function sounds like so much pain to createπ
Haha, the get all function sounds like so much pain to createπ
I coded one internally in an hour or so, albeit it literally gets everything including the kitchen sink, no filters like uses or anything like that. Wasnt too painful π
For the legacy commandlets it's working well, but this is why I raised the issues with the graph side, as when I tried it using that method it all broke in a horrific way π€£
Haha, maybe I'm just trying to find an excuse to avoid spending my free time creating a get-all function π€£. How do you handle the "run all" process internally? Do you execute tasks one by one, or do you have a sequence where certain scripts run behind each other? Considering that the UAL takes the longest, do you save it for last, or do you run it in parallel with the other collections?
We utilize Azure Functions internally, with each function dedicated to collecting a specific log source. These functions are triggered simultaneously using an Azure Logic App workflow, and the output is written to a Blob Storage Container. Azure Data Factory automatically processes the data and loads the results into our SIEM, where automatically custom detection rules are ran against the data.
Haha, maybe I'm just trying to find an excuse to avoid spending my free time creating a get-all function π€£. How do you handle the "run all" process internally? Do you execute tasks one by one, or do you have a sequence where certain scripts run behind each other? Considering that the UAL takes the longest, do you save it for last, or do you run it in parallel with the other collections?
We utilize Azure Functions internally, with each function dedicated to collecting a specific log source. These functions are triggered simultaneously using an Azure Logic App workflow, and the output is written to a Blob Storage Container. Azure Data Factory automatically processes the data and loads the results into our SIEM, where automatically custom detection rules are ran against the data.
Ha ha, so was I until I needed it to avoid hovering over the acquisition process late at night π€£.
Basically I go
Check dependent packages Request auth Collect all logs (UAL Last) Collect all environmental details
If I have a known user, I just change the get-ual function down to a single user, as your right the whole UAL does take quite some time (1/2 day or more).
I also try catch the dependent packages and auth as a exit on fail. Else for logs I also have a try catch, but it just errors and continues.
I want to use a Azure function so badly, but I haven't been able to figure out how to get authentication cross tenancies / non-interactivley. Hence the get all from a disposable VM.
Nice, that sounds like a solid workflow! We experimenting using a multi-tenant application created in our tenant that we register in the client environment. We request the appropriate Graph scopes and, if necessary, add some roles using an ARM template. This allows us to use the application in the client environment to execute regular PowerShell cmdlets and Graph API calls like we do in the Extractor Suite.
Nice, that sounds like a solid workflow! We experimenting using a multi-tenant application created in our tenant that we register in the client environment. We request the appropriate Graph scopes and, if necessary, add some roles using an ARM template. This allows us to use the application in the client environment to execute regular PowerShell cmdlets and Graph API calls like we do in the Extractor Suite.
That's exactly how I was thinking of doing a more mature version too, just been a case of not having the time to figure out the nuts and bolts of the how, but it's good to know it should work π
Hi, I fixed this in the new update. Feel free to open a new issue if anything is not working. The fix should prevent you from closing the browser if you already constend.
How it works:
For example, in the Get-RiskyUsers
function, we use the following two lines:
$requiredScopes = @("IdentityRiskEvent.Read.All","IdentityRiskyServicePrincipal.Read.All","IdentityRiskyUser.Read.All")
$graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes
The variable $requiredScopes
is an array that contains the necessary permissions needed to access information via Microsoft Graph. In this case, the scopes are:
IdentityRiskEvent.Read.All
IdentityRiskyServicePrincipal.Read.All
IdentityRiskyUser.Read.All
The function Get-GraphAuthType
is then called with the required scopes.
It retrieves the current Graph context using Get-MgContext
. If no context is found, it sets the authentication type to "none". It checks the current context to see if the required scopes are already present. If any required scopes are missing, they are added to the $missingScopes
array.
Depending on the authentication type:
Full function code:
function Get-GraphAuthType {
param (
[string[]]$RequiredScopes
)
$context = Get-MgContext
if (-not $context) {
$authType = "none"
$scopes = @()
} else {
$authType = $context | Select-Object -ExpandProperty AuthType
$scopes = $context | Select-Object -ExpandProperty Scopes
}
$missingScopes = @()
foreach ($requiredScope in $RequiredScopes) {
if (-not ($scopes -contains $requiredScope)) {
$missingScopes += $requiredScope
}
}
$joinedScopes = $RequiredScopes -join ","
switch ($authType) {
"delegated" {
if ($missingScopes.Count -gt 0) {
foreach ($missingScope in $missingScopes) {
Write-LogFile -Message "[INFO] Missing Graph scope detected: $missingScope" -Color "Yellow"
}
Write-LogFile -Message "[INFO] Attempting to re-authenticate with the appropriate scope(s): $joinedScopes" -Color "Green"
Connect-MgGraph -NoWelcome -Scopes $joinedScopes > $null
}
}
"application" {
if ($missingScopes.Count -gt 0) {
foreach ($missingScope in $missingScopes) {
Write-LogFile -Message "[INFO] The connected application is missing Graph scope detected: $missingScope" -Color "Red"
}
}
}
"none" {
Write-LogFile -Message "[INFO] No active Connect-MgGraph session found. Attempting to connect with the appropriate scope(s): $joinedScopes" -Color "Green"
Connect-MgGraph -NoWelcome -Scopes $joinedScopes
}
}
return @{
AuthType = $authType
Scopes = $scopes
MissingScopes = $missingScopes
}
}
Originally posted by @JoeyInvictus in https://github.com/invictus-ir/Microsoft-Extractor-Suite/issues/57#issuecomment-2191101719
Hey @JoeyInvictus loving the new changes, just noticed that the new graph authentication changes required you to close the browser if already granted each time. This one will inhibit an unattended 'get-all' function in the future, if you get the auth right from the get go then run each module.
As much as I don't like the are you already logged in tests we had, and love the simplicity of V2, for these modules, I'm wondering if there is a way to check if consent has already been granted without it opening a new browser window? Might be a case of grabbing a single record to null in a try catch π