Audit Azure with Nessus

Auditfiles:

Tenable currently provides 3 Nessus auditfiles to audit Microsoft Azure:

  • TNS_Microsoft_Azure_Best_Practices-Databases.audit
  • TNS_Microsoft_Azure_Best_Practices-Infrastructure.audit
  • TNS_Microsoft_Azure_Best_Practices-Websites.audit

Compliance Checks in Database.audit

‘Review SQL Servers’
‘Review SQL Server Administrator Logins’
‘No External Administrator Logins’
‘Review SQL Server Status’
‘All SQL Server are in ‘Ready’ Status’
‘Review SQL Server Databases’
‘Review SQL Server Database Status’
‘All Databases are Online’
‘Databases created since last scan’
‘Auditing is enabled on all databases’
‘Audit Retention is 90 days or more on all databases’
‘Event Category ‘Plain SQL – Success’ is enabled on all databases’
‘Event Category ‘Plain SQL – Failure’ is enabled on all databases’
‘Event Category ‘Parameterized SQL – Success’ is enabled on all databases’
‘Event Category ‘Parameterized SQL – Failure’ is enabled on all databases’
‘Event Category ‘Stored Procedure – Success’ is enabled on all databases’
‘Event Category ‘Stored Procedure – Failure’ is enabled on all databases’
‘Event Category ‘Login – Success’ is enabled on all databases’
‘Event Category ‘Login – Failure’ is enabled on all databases’
‘Event Category ‘Transaction Management – Success’ is enabled on all databases’
‘Event Category ‘Transaction Management – Failure’ is enabled on all databases’
‘Review the existing SQL Server Firewall rules’
‘Review the existing Recoverable Databases’

Compliance Checks in Infrastucture.audit

Subscriptions – ‘Review Enabled Subscriptions’
Subscriptions – ‘No Disabled Subscriptions’
Principals – ‘Account Administrator Accounts’
Principals – ‘Service Administrator Accounts’
Principals – ‘CoAdministrator Accounts’
Virtual Networks – ‘Virtual Network Entries exist’
Virtual Networks – ‘Review Defined Virtual Networks’
Virtual Networks – ‘In-use Virtual Networks exist’
Virtual Networks – ‘Review In-use Virtual Networks’
Virtual Networks – ‘No In-use Virtual Networks exist’
Virtual Networks – ‘Unused Virtual Networks exist’
Virtual Networks – ‘Review Unused Virtual Networks’
Virtual Networks – ‘No Unused Virtual Networks exist’
Virtual Networks – ‘Subnets exist’
Virtual Networks – ‘Review Subnets’
Virtual Networks – ‘No Subnets exist’
Virtual Networks – ‘No Virtual Network Entries exist’
Certificates – ‘Certificate Entries exist’
Certificates – ‘Review Certificates’
Certificates – ‘Hostname Certificates’
Certificates – ‘Certificates recently issued’
Certificates – ‘Certificates expiring before next scan’
Certificates – ‘No Certificate Entries exist’
Virtual Machines – ‘Virtual Machines exist’
Virtual Machines – ‘Review Virtual Machines’
Virtual Machines – ‘Stopped Virtual Machines’
Virtual Machines – ‘No Deallocated Virtual Machines’
Virtual Machines – ‘No Virtual Machines without EndPoints’
Virtual Machines – ‘Review Virtual Machine EndPoints’
Virtual Machines – ‘Review Virtual Machine Extensions’
Virtual Machines – ‘No Disabled Virtual Machine Extensions’
Virtual Machines – ‘Virtual Machine Public IP Addresses’
Virtual Machines – ‘No Virtual Machine Public IP Addresses’
Virtual Machines – ‘No Virtual Machines exist’
Deployment Snapshot

Compliance Checks in Website.audit

‘Review Website Names in use’
‘Review Website Status’
‘No Stopped Websites Exist’
‘Review Enabled Websites’
‘No Disabled Websites Exist’
‘Review Admin Enabled Sites’
‘No Admin Disabled Sites Exist’
‘Review Sites and Hostnames’
‘Review Sites and Hostnames that are enabled’
‘Review Sites and SSL Status’
‘No Sites with sslState = 0’
‘Review Sites modified since the last scan’

Compliance plugin name

/opt/nessus/lib/nessus/plugins/azure_compliance_check.nbin

When running this plugin with nasl the following information must be provided:

microsoft_azure Compliance Checks, version 1.27
 
Which file contains your security policy? 
Username to connect with :
Password : 
Client ID :
Verify SSL? [yes or no. Default: no] : 
Subscription IDs [Leave blank for all] :

Audit file details

There is currently no public documentation that provides information on how to create custom audit files for Microsoft Azure. So we have to analyze the Tenable auditfiles to determine the structure and the syntax and the functions.

Auditfile opening and closure tags of the azure auditfile:

<check_type : "microsoft_azure">
 
</check_type>

Basic structure of a check:

<custom_item>
 description : "Microsoft Azure - <check description>"
 request : "getresourcesubs"
 json_transform : '<query>'
</custom_item>

There are 2 types of request in the sample auditfiles:

request : "getresourcesubs"
and
request : "getsubscriptionuseraccounts"

Principle checks use the request : “getsubscriptionuseraccounts” :

json_transform : '.[] as $base | $base | .principals[] | 
select(.Role | test(".*AccountAdministrator")) |  
"Subscription : " + $base.subscriptionId + " Account Administrator - " + .Email'

json_transform : '.[] as $base | $base | .principals[] | 
select(.Role | test(".*ServiceAdministrator")) | 
"Subscription : " + $base.subscriptionId + " Service Administrator - " + .Email'

json_transform : '.[] as $base | $base | .principals[] | 
select(.Role | test(".*CoAdministrator")) |
"Subscription : " + $base.subscriptionId + " CoAdministrator - " + .Email'

All other checks use the request : “getresourcesubs”

Subscription Checks:

json_transform : '.[]|select(.state == "Enabled")| 
.displayName +" (" + .subscriptionId + ") Status: " + .state'

Virtual Network Checks:

json_transform : '"Virtual Networks: \([.[].resourceGroups[].virtualNetworks[].properties?.addressSpace?.addressPrefixes[]?] | length)"' 

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[] | "Subscription : " + $subID + " Virtual Network Name: " + .name + " In-Use: " + (.properties.inUse | tostring)'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[].properties | "Virtual Networks in use: " + (.inUse | tostring)'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[] | select(.properties.inUse == true) | "Subscription : " + $subID + " - Virtual Network Name: " + .name + " - Address Range(s): " + (.properties.addressSpace.addressPrefixes | join(", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[].properties | "Virtual Networks in use: " + (.inUse | tostring)'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[] | select(.properties.inUse== false) | "Subscription : " + $subID + " - Unused Virtual Network : " + .name + " - Address Range(s): " + (.properties.addressSpace.addressPrefixes | join(", "))'

json_transform : '"Virtual Subnets: \([.[].resourceGroups[].virtualNetworks[]?.properties?.subnets[]?.addressPrefix?] | length)"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualNetworks[] | .name as $name | .properties.subnets[] as $subnets |
 "Subscription: " + $subID + " Virtual Network: " + $name + " Subnet: " + $subnets.name + " Prefix: " + $subnets.addressPrefix'

Certificate Checks:

json_transform : '"Certificates: \([.[].resourceGroups[].certificates[].id] | length)"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].certificates[] |
 "Subscription: " + $subID + " - Certificate Name: \(.properties.subjectName)" + " (" + .name + ")"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].certificates[] |
 "Subscription: " + $subID + " - Hostname Certificate: \(.properties.subjectName)" + " - \([.properties.hostNames[]] | join (", "))"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].certificates[] | select ((.properties.issueDate | iso_8601_days_ago) < @[email protected]) |
 "Subscription: " + $subID + " Certificate Name: " + .subjectName + " Issued by: " + .properties.issuer + " Issued on: " + .properties.issueDate'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].certificates[] | select ((.properties.expirationDate | iso_8601_days_ago) > [email protected][email protected]) |
 "Subscription: " + $subID + " Certificate Name: " + .name + " Issued by: " + .properties.issuer + " Expires on: " + .properties.expirationDate'

Virtual Machine Checks:

json_transform : '"Virtual Machines: \([.[].resourceGroups[].virtualMachines[].properties?.instanceView?.fullyQualifiedDomainName?] | length)"' 

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[] | "Subscription: " + $subID + " - Virtual Machine: " + ([.properties.instanceView.fullyQualifiedDomainName] | join (", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties.instanceView | select (.powerState == "Stopped") |
"Subscription: " + $subID + " - Stopped Virtual Machine: " + ([.fullyQualifiedDomainName] | join (", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties.instanceView | select (.powerState == "Stopped") |
"Subscription: " + $subID + " - Stopped Virtual Machine: " + ([.fullyQualifiedDomainName] | join (", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties.instanceView | select (.status == "StoppedDeallocated") |
"Subscription: " + $subID + " - Deallocated Virtual Machine: " + ([.fullyQualifiedDomainName] | join (", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties | select ((.networkProfile.inputEndpoints | tostring) == "null") |
 "Subscription: " + $subID + " - Virtual Machine without EndPoint: " + ([.instanceView.fullyQualifiedDomainName] | join (", "))'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties as $props | $props.networkProfile.inputEndpoints[] |
 "Subscription: \($subID) - Virtual Machine: \($props.instanceView.fullyQualifiedDomainName) Endpoint Name: \(.endpointName) Public Port: \(.publicPort) Private Port: \(.privatePort) Protocol: \(.protocol)"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties as $props | $props.extensions[] |
 "Subscription: \($subID) - Virtual Machine: \($props.instanceView.fullyQualifiedDomainName) Extension Name: \(.referenceName) Version: \(.version) State: \(.state)"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties as $props | $props.extensions[] as $extension | select ($extension.state != "Enable") | "Subscription: \($subID) - Virtual Machine: \($props.instanceView.fullyQualifiedDomainName) - Disabled Extension = \($extension.extension)"'

json_transform : '"Public IP Addresses: \([.[].resourceGroups[].virtualMachines[].properties?.instanceView?.publicIpAddresses[]?] | length)"'

json_transform : '.[] | .subscriptionId as $subID | .resourceGroups[].virtualMachines[].properties.instanceView | select ([.publicIpAddresses[]] | length >= 1) |
 "Subscription: \($subID) - Virtual Machine: \(.fullyQualifiedDomainName) - Public IP addresses = \([.publicIpAddresses[]?] | join (", "))"'

 

 

References for auditing Azure with Nessus: