Skip to content

AAD report error due to invalid JSON result #1607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Strawberryww opened this issue Mar 3, 2025 · 25 comments · May be fixed by #1682
Open

AAD report error due to invalid JSON result #1607

Strawberryww opened this issue Mar 3, 2025 · 25 comments · May be fixed by #1682
Assignees
Labels
bug This issue or pull request addresses broken functionality public-reported This issue is reported by the public users of the tool.

Comments

@Strawberryww
Copy link

🐛 Summary

When using ScubaGear 1.5.0 to scan AAD, it reports the following error:

unable to parse input: yaml: line 12: found unknown escape character
Fatal Error involving the Report Creation.                                                                                           Ending ScubaGear execution. Error: Invalid JSON primitive: .

at New-Report, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\CreateReport\CreateReport.psm1: line 66
at Invoke-ReportCreation<Process>, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line 1277
at Invoke-SCuBA<Process>, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line 440
at <ScriptBlock>, <No file>: line 1
At C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\Orchestrator.psm1:1371 char:13
+             throw $InvokeReportErrorMessage
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Fatal Error inv...o file>: line 1:String) [], RuntimeException
    + FullyQualifiedErrorId : Fatal Error involving the Report Creation.
            Ending ScubaGear execution. Error: Invalid JSON primitive: .

at New-Report, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\CreateReport\CreateReport.psm1: line 66
at Invoke-ReportCreation<Process>, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line 1277
at Invoke-SCuBA<Process>, C:\ScubaGear-1.5.0\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line 440
at <ScriptBlock>, <No file>: line 1

To reproduce

My Azure tenant can always reproduces the error, but another tenant is OK.

The invoke command is as below:

Invoke-SCuBA -ProductNames aad -CertificateThumbprint "<Thumbprint>" -AppID "<AppId>" -Organization <Org> -Outpath <outputPath>

Env:
ScubaGear: 1.5.0
Powershell: 5.1.19041.5369
Windows 10 Pro: 10.0.19045 N/A Build 19045

Expected behavior

There should be no such error.

Any helpful log output or screenshots

The generated JSON result is as below:

    ...
    "risky_third_party_service_principals": ,
    "aad_successful_commands": [
    "Get-MgBetaIdentityConditionalAccessPolicy",
    "Get-MgBetaSubscribedSku",
    "Get-PrivilegedUser",
    "Get-PrivilegedRole",
    "Get-MgBetaUserCount",
    ...

The risky_third_party_service_principals field is missing its value.

Image

Image

Possibly the error is related to the following code in PowerShell/ScubaGear/Modules/Providers/ExportAADProvider.psm1:

Image

Error in PowerShell:

Image

@mitchelbaker-cisa mitchelbaker-cisa self-assigned this Mar 3, 2025
@mitchelbaker-cisa mitchelbaker-cisa added this to the Marlin milestone Mar 3, 2025
@mitchelbaker-cisa mitchelbaker-cisa added bug This issue or pull request addresses broken functionality public-reported This issue is reported by the public users of the tool. labels Mar 3, 2025
@mitchelbaker-cisa
Copy link
Collaborator

mitchelbaker-cisa commented Mar 3, 2025

Hey @Strawberryww, thanks for reporting this issue. We have replicated the bug and are looking into a fix. Are you seeing any results for the risky_applications object above risky_third_party_service_principals?

@Strawberryww
Copy link
Author

@mitchelbaker-cisa Thanks. Yes, there is result for the risky_applications object above risky_third_party_service_principals:

Image

@mitchelbaker-cisa
Copy link
Collaborator

I created a new branch that adds additional error handling to the Format-RiskyThirdPartyServicePrincipals function. The issue seems to stem from the function returning @($null), which PowerShell internally converts to $null. The null value is handled as an invalid JSON primitive when we generate the ScubaResults.json file, resulting in the error message you have attached above.

Do you mind downloading a zip of the new branch to test out the fix? It can be found here:
https://github.com/cisagov/ScubaGear/archive/refs/heads/1607-risky-sps-invalid-json.zip

After importing it in a new PowerShell terminal, run Import-Module .\PowerShell\ScubaGear, then run ScubaGear in either interactive/noninteractive mode, the Entra ID report should generate correctly.

@Strawberryww
Copy link
Author

@mitchelbaker-cisa Thanks for your analysis. I've tried the fix with https://github.com/cisagov/ScubaGear/archive/refs/heads/1607-risky-sps-invalid-json.zip, but still get the invalid JSON error:

unable to parse input: yaml: line 12: found unknown escape character
Fatal Error involving the Report Creation.
            Ending ScubaGear execution. Error: Invalid JSON primitive: .

at New-Report, C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\CreateReport\CreateReport.psm1:
line 66
at Invoke-ReportCreation<Process>,
C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line 1277
at Invoke-SCuBA<Process>, C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\Orchestrator.psm1: line
440
at <ScriptBlock>, <No file>: line 1
At C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\Orchestrator.psm1:1371 char:13
+             throw $InvokeReportErrorMessage
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Fatal Error inv...o file>: line 1:String) [], RuntimeException
    + FullyQualifiedErrorId : Fatal Error involving the Report Creation.
            Ending ScubaGear execution. Error: Invalid JSON primitive: .

    at New-Report, C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\CreateReport\CreateReport.psm1
   : line 66
    at Invoke-ReportCreation<Process>, C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\Orchestrat
   or.psm1: line 1277
    at Invoke-SCuBA<Process>, C:\ScubaGear-1607-risky-sps-invalid-json\PowerShell\ScubaGear\Modules\Orchestrator.psm1:
   line 440
at <ScriptBlock>, <No file>: line 1
Image

There is value for risky_applications object, but not for risky_third_party_service_principals:

Image

I've double checked that the code PowerShell/ScubaGear/Modules/Providers/ExportAADProvider.psm1 is new:

Image

Is there any way to save the output to a file before converting to JSON? Then, it's better for analysis. That is:

...
### save the output before the following converting
$RiskyThirdPartySPs = ConvertTo-Json -Depth 3 $Tracker.TryCommand("Format-RiskyThirdPartyServicePrincipals", @{"RiskySPs"=$RiskySPs})
...

Thanks.

@mitchelbaker-cisa
Copy link
Collaborator

Hey @Strawberryww, thanks for testing out the new branch and providing these additional details. We've determined there are four cases that occur regarding risky application/service principal data:

  • Both risky apps and risky 3rd party SPs exist
  • Neither risky apps or risky 3rd party SPs exist
  • No risky apps exist but risky 3rd party SPs exist
  • Risky apps exist but no risky 3rd party SPs exist

The issue occurs in the last case, "risky apps exist but no risky 3rd party SPs exist." We're still working on a fix but will message here when it's ready.

Regarding your question on saving the output to a file before converting to JSON, you can add $ServicePrincipals > riskythirdpartysp-output.txt in the Format-RiskyThirdPartyServicePrincipals function before the the return $ServicePrincipals line.

@Strawberryww
Copy link
Author

@mitchelbaker-cisa Thanks for you detailed explanation. I'll try to save the JSON result and check whether there is any clue.

@QuarantinedUser
Copy link

Following. I'm running into the same bug :(. Tested the new branch just to see what would happen and still failed.

@cr-MichaelWaski
Copy link

Is there a way to disable the third-party apps to get around this error until it is fixed?

@TheArchimedez
Copy link

TheArchimedez commented Mar 26, 2025

Is there a way to disable the third-party apps to get around this error until it is fixed?

You can override the faulty bit if you edit the file ScubaGear/Modules/Providers/ExportAADProvider.psm1

around line 215-216 you'll see the variables
$AggregateRiskyApps = "{}"
and
$RiskyThridPartySPs = "{}"

If you override those (I added new lines at 218 and 219 after the closing } of the if/else block), adding in
$AggregateRiskyApps = "0"
and
$RiskyThridPartySPs = "0"

and saving the file, then re-running ScubaGear, it will complete and you'll get the results for everything other than the RiskyApps.

@SAL-3000
Copy link

SAL-3000 commented Mar 26, 2025

Following here as well. Same bug, same issue, verbiage is the same as posted by @Strawberryww

tried editing exportaadprovider.psm1 per @TheArchimedez still no joy. if i remove aad from the -productname everything runs like a champ

@SAL-3000
Copy link

SAL-3000 commented Mar 26, 2025

another workaround, but not pretty and only serves to test out v 1.5 is to remark out and remove the following out of exportaadprovider.psm1

Remark out

Line 201:     ##### This block gathers information on risky API permissions related to application/service principal objects
Line 202:     #Import-Module $PSScriptRoot/ProviderHelpers/AADRiskyPermissionsHelper.psm1
Line 204:     #$RiskyApps = $Tracker.TryCommand("Get-ApplicationsWithRiskyPermissions")
Line 205:     #$RiskySPs = $Tracker.TryCommand("Get-ServicePrincipalsWithRiskyPermissions", @{"M365Environment"=$M365Environment})
Line 207:     #$RiskyApps = if ($null -eq $RiskyApps -or $RiskyApps.Count -eq 0) { $null } else { $RiskyApps }
Line 208:     #$RiskySPs = if ($null -eq $RiskySPs -or $RiskySPs.Count -eq 0) { $null } else { $RiskySPs }
Line 210:     #if ($RiskyApps -and $RiskySPs) {
Line 211:     #    $AggregateRiskyApps = ConvertTo-Json -Depth 3 $Tracker.TryCommand("Format-RiskyApplications", @{"RiskyApps"=$RiskyApps; "RiskySPs"=$RiskySPs})
Line 212:     #    $RiskyThirdPartySPs = ConvertTo-Json -Depth 3 $Tracker.TryCommand("Format-RiskyThirdPartyServicePrincipals", @{"RiskyApps"=$RiskyApps; "RiskySPs"=$RiskySPs})
Line 215:      #   $AggregateRiskyApps = "0"
Line 216:       #  $RiskyThirdPartySPs = "0"
    Line 217:     #}

Remove

Line 237:     "risky_applications": $AggregateRiskyApps,
Line 238:     "risky_third_party_service_principals": $RiskyThirdPartySPs,

if you do that, tool will run aad report

@TheArchimedez
Copy link

another workaround, but not pretty and only serves to test out v 1.5 is to remark out and remove the following out of exportaadprovider.psm1

Remark out

Line 201:     ##### This block gathers information on risky API permissions related to application/service principal objects
Line 202:     #Import-Module $PSScriptRoot/ProviderHelpers/AADRiskyPermissionsHelper.psm1
Line 204:     #$RiskyApps = $Tracker.TryCommand("Get-ApplicationsWithRiskyPermissions")
Line 205:     #$RiskySPs = $Tracker.TryCommand("Get-ServicePrincipalsWithRiskyPermissions", @{"M365Environment"=$M365Environment})
Line 207:     #$RiskyApps = if ($null -eq $RiskyApps -or $RiskyApps.Count -eq 0) { $null } else { $RiskyApps }
Line 208:     #$RiskySPs = if ($null -eq $RiskySPs -or $RiskySPs.Count -eq 0) { $null } else { $RiskySPs }
Line 210:     #if ($RiskyApps -and $RiskySPs) {
Line 211:     #    $AggregateRiskyApps = ConvertTo-Json -Depth 3 $Tracker.TryCommand("Format-RiskyApplications", @{"RiskyApps"=$RiskyApps; "RiskySPs"=$RiskySPs})
Line 212:     #    $RiskyThirdPartySPs = ConvertTo-Json -Depth 3 $Tracker.TryCommand("Format-RiskyThirdPartyServicePrincipals", @{"RiskyApps"=$RiskyApps; "RiskySPs"=$RiskySPs})
Line 215:      #   $AggregateRiskyApps = "0"
Line 216:       #  $RiskyThirdPartySPs = "0"
    Line 217:     #}

Remove

Line 237:     "risky_applications": $AggregateRiskyApps,
Line 238:     "risky_third_party_service_principals": $RiskyThirdPartySPs,

if you do that, tool will run aad report

Ahh I see the issue with your implementation of my workaround, the

Line 215: # $AggregateRiskyApps = "0"
Line 216: # $RiskyThirdPartySPs = "0"

Should have been below the last } of the if else blocks, apologies for not making that clear.

But yes, your solution is doing essentially the same thing. More than one way to skin a cat ;)

@SAL-3000
Copy link

Thanks @TheArchimedez for the confirmation

@mitchelbaker-cisa i'm currently working with an organization that BOD 25-01 applies to. Since remediation is required by 20-June do we have a timeframe on when this will be resolved ? we were running 1.4 but since 1.5 is current release i thought i would upgrade and go from there. any guidance there ?

@schrolla
Copy link
Contributor

schrolla commented Apr 1, 2025

Related to identified bug #1658 which breaks out this specific issue with the risky service principals field being null.

@mitchelbaker-cisa
Copy link
Collaborator

Thanks @TheArchimedez for the confirmation

@mitchelbaker-cisa i'm currently working with an organization that BOD 25-01 applies to. Since remediation is required by 20-June do we have a timeframe on when this will be resolved ? we were running 1.4 but since 1.5 is current release i thought i would upgrade and go from there. any guidance there ?

I'm working on getting a fix ASAP, we will most likely release a patch fix to address the issue.

@mitchelbaker-cisa
Copy link
Collaborator

When you have a chance, please test out the latest changes to this feature branch and let me know if you are able to generate the AAD report successfully. https://github.com/cisagov/ScubaGear/tree/1607-risky-sps-invalid-json

@kickinitlegit
Copy link

When you have a chance, please test out the latest changes to this feature branch and let me know if you are able to generate the AAD report successfully. https://github.com/cisagov/ScubaGear/tree/1607-risky-sps-invalid-json

Can't speak for others, but I seem to still be getting the same error with the patch. Merged the ZIP into a 1.5 installation, imported the Scuba module, and ran all products.

Image

@mitchelbaker-cisa
Copy link
Collaborator

Thanks for testing, @kickinitlegit. To confirm you reimported ScubaGear with Import-Module .\PowerShell\ScubaGear? If you open the ProviderSettingsExport.json generated, is risky_applications also empty or is it still risky_third_party_service_principals?

@kickinitlegit
Copy link

Thanks for testing, @kickinitlegit. To confirm you reimported ScubaGear with Import-Module .\PowerShell\ScubaGear? If you open the ProviderSettingsExport.json generated, is risky_applications also empty or is it still risky_third_party_service_principals?

Didn't get a chance to check the json. I can confirm that I re-imported, though. I'll try testing in my lab this weekend if someone doesn't get to it first.

@kickinitlegit
Copy link

Thanks for testing, @kickinitlegit. To confirm you reimported ScubaGear with Import-Module .\PowerShell\ScubaGear? If you open the ProviderSettingsExport.json generated, is risky_applications also empty or is it still risky_third_party_service_principals?

Looks like"risky_applications" populated with objects. I believe "risky_third_party_service_principals" is empty, however.

Image

Image

@mitchelbaker-cisa
Copy link
Collaborator

mitchelbaker-cisa commented Apr 14, 2025

@kickinitlegit Do you mind checking if the RiskyPermissions.json file you have locally matches the file in the remote branch? There should be the following number of permission scopes for each node:

  • Microsoft Graph: 37 total
  • Office 365 Management APIs: 2 total
  • Office 365 Exchange Online: 13 total
  • Office 365 SharePoint Online: 5 total

@kickinitlegit
Copy link

kickinitlegit commented Apr 16, 2025

@kickinitlegit Do you mind checking if the RiskyPermissions.json file you have locally matches the file in the remote branch? There should be the following number of permission scopes for each node:

  • Microsoft Graph: 36 total
  • Office 365 Management APIs: 2 total
  • Office 365 Exchange Online: 12 total
  • Office 365 SharePoint Online: 5 total

I'm seeing 37, 2, 13, 5. Should I try re-downloading and re-merging the branch since the numbers don't match up?

EDIT: After looking, I'm seeing the same number and same permissions in mine as in the remote branch. Ignore the above.

@mitchelbaker-cisa
Copy link
Collaborator

I must have miscounted, confirming the totals are 37, 2, 13, 5 so no issues here. I added a few improvements to the error handling, could you try redownloading/remerging the branch?

@kickinitlegit
Copy link

kickinitlegit commented Apr 16, 2025

I must have miscounted, confirming the totals are 37, 2, 13, 5 so no issues here. I added a few improvements to the error handling, could you try redownloading/remerging the branch?

Remerging with the new error handling seems to work. I no longer got the error shown in the issue highlighted. Any files you need me to check?

@mitchelbaker-cisa
Copy link
Collaborator

Ok great. If there were no errors then in the output directory do you have a ScubaResults_<guid>.json file instead of a ProviderSettingsExport.json file? If so, ScubaResults_<guid>.json should have something like the following for the risky_third_party_service_principals node:

risky_third_party_service_principals: [],

# or
risky_third_party_service_principals: [ null ],

@schrolla schrolla removed this from the Marlin milestone Apr 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue or pull request addresses broken functionality public-reported This issue is reported by the public users of the tool.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants