权限提升与逃逸攻击debug记录

0.前提工作

搭建一台ubuntu虚拟机,并配置好SSH,安装好Anaconda环境。使用Windows宿主机上的VSCode远程连接上Ubuntu虚拟机,并且在VSCode里调试这个脚本。为远程SSH端的VSCode安装插件:
Remote – SSH (Microsoft 出品)Python (Microsoft 出品)

这样你可以获得如同在本地一样的代码编辑和调试体验,同时代码实际运行在 Ubuntu 虚拟机中。如下图所示:

权限提升与逃逸攻击debug记录

1.debug过程

权限提升与逃逸攻击debug记录

进入主程序入口后,首先初始化海洋科学计算处理器OceanDataProcessor(),真实场景下这个api接口是平台提供好的,初始化OceanDataProcessor()即执行OceanDataProcessor()的__init__方法。

权限提升与逃逸攻击debug记录

第二步,初始化恶意攻击者对象ContainerEscalator(),这是你(攻击者)自己写的代码。

权限提升与逃逸攻击debug记录

权限提升与逃逸攻击debug记录

在ContainerEscalator类的__init__()里面,有一个self._setup_logging()的操作,这是在初始化日志记录器,如下所示:

权限提升与逃逸攻击debug记录

这里使用的是Python内置的标准日志模块logging模块,来打日志的。

有关python怎么打日志的文档请见:

https://blog.csdn.net/m0_59777389/article/details/155931579?spm=1011.2415.3001.5331

权限提升与逃逸攻击debug记录

如上图,接下来进入真正的计算+攻击的行为了,首先调用processor.load_data()方法,实现加载数据集,

权限提升与逃逸攻击debug记录

step1:在第一批次计算时,尝试侦察(检查Docker Socket挂载点)

执行挂载点检查(检查是否有Docker Socket挂载)

其实此处通过睡眠1s来模拟这个过程,并没有加载到任何数据集。

权限提升与逃逸攻击debug记录

如上图,这是第一次恶意行为:计算 + 挂载点检查。

权限提升与逃逸攻击debug记录

先通过调用processor.process_batch(1)方法,通过海洋科学计算处理器进行科学计算任务,如上图,此处我们通过random.random() + time.sleep()分别来模拟计算过程和延迟。

【插入知识点】


random.random()
是 Python
random
模块中的一个函数,它返回一个介于 0.0(包含)和 1.0(不包含)之间的伪随机浮点数,即在半开放区间
[0.0, 1.0)
内均匀分布的随机数。它是其他许多随机函数(如
random.randint()
,
random.choice()
)的底层基础,使用 Mersenne Twister 算法生成。 

均匀分布的随机数是指在特定区间内,每个数值出现的概率相等,最常见的是生成0到1之间的随机数,可以通过编程语言(如C++的
rand()
配合
srand(time(0))
、Python的
numpy.random.uniform()
)或工具(如Excel的
=RAND()
)来实现,并通过数学公式调整到任意范围(例如
rand() * (b-a) + a
)或生成整数(如
(int)(rand() * 100)
)。


random.uniform()
(来自 Python 内置的
random
模块) 和 numpy.random.uniform() (来自 NumPy 库) 都是生成指定范围内均匀分布随机浮点数(小数)的函数,区别在于一个是标准库,另一个是科学计算库;

都用于在给定的开区间
[low, high)
(包含
low
,不包含
high
)或闭区间
[low, high]
(取决于具体实现和版本,但通常是半开区间)中随机选择一个数。 

权限提升与逃逸攻击debug记录

再通过调用attacker.check_dangerous_mounts()方法,执行挂载点检查。如上图,在check_dangerous_mounts()方法中,通过调用self._run_command(cmd, “List all mounts”)方法,执行bash命令,并获取挂载信息。

该方法返回一个subprocess.CompletedProcess 对象,并赋值给result,result里面有一个stdout属性,该属性的值为执行mount命令后生成的虚拟机挂载信息。如下图所示:

权限提升与逃逸攻击debug记录

权限提升与逃逸攻击debug记录

接着再通过执行if self.target_socket in result.stdout:代码,检查是否有Docker Socket挂载信息。(这行代码属于字符串匹配的效果了)

如果有,就往logger里面写入一条critical级别的日志信息,并返回True。

`mount` 命令的作用详解请见附录1

另外咱们再单拉出来一下这行代码:result = self._run_command(cmd, “List all mounts”)  # 执行bash命令,获取挂载信息

步入方法内部,具体代码如下:

权限提升与逃逸攻击debug记录

result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)


subprocess.run
(重难点)
这是 Python 执行系统命令的官方推荐方式。


cmd
: 你要执行的命令(如
ls
,
ping
等)。


shell=True
: 表示通过系统的 Shell(如 Linux 的 bash 或 Windows 的 cmd)来解释执行。


capture_output=True
: 捕获输出。它会把命令运行的结果存入内存,而不是直接显示在屏幕上。如果为True,则会捕获stdout和stderr,并将其赋给返回CompletedProcess对象的stdout和stderr属性。


text=True
: 将结果以**文本(字符串)**形式返回。如果不加这个,返回的是字节码(bytes)。


timeout=10
: 超时保护。如果命令执行超过 10 秒还没结束,程序会强制关掉它并抛出异常,防止程序卡死。

这是执行cmd命令的关键切入口,最后返回result(一个CompletedProcess对象),

返回的CompletedProcess对象具有以下属性:

args:传入的参数。returncode:命令的返回码,0通常表示成功。stdout:捕获的标准输出(如果capture_output=True或stdout=subprocess.PIPE)。stderr:捕获的标准错误(如果capture_output=True或stderr=subprocess.PIPE)。

step2:在第二批次计算时,尝试逃逸(执行Docker Socket逃逸)

权限提升与逃逸攻击debug记录

接着进行第二次恶意行为:计算 + Docker Socket逃逸 (当然,如果第一步没有检测到docker.sock挂载,第二步的 Docker Socket逃逸也不会成功的)

权限提升与逃逸攻击debug记录

如上图所示,其实我们的代码并未检测到'/var/run/docker.sock'路径,所以该方法直接return false,后面代码不再执行。


os.path.exists()
函数

检查文件或路径是否存在

但是我们任然分析一下,Docker Socket逃逸是如何执行执行的?

权限提升与逃逸攻击debug记录


socket.AF_UNIX
:Unix域套接字,用于同一台机器上的进程间通信
socket.SOCK_STREAM
:流式套接字(TCP类型)
socket.connect()
:连接到指定的socket地址

重要提示:这用于攻击是因为Docker API通过
/var/run/docker.sock
提供。

通常情况下,Socket客户端通过如下形式连接到Socket服务器:



s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 创建一个套接字对象
s.connect((HOST, PORT))  # 使用 .connect() 连接到服务器

但是这里的代码却是:



sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)  # # 创建一个套接字对象
sock.connect('/var/run/docker.sock')  # 使用 .connect() 连接到服务器

原因是:.connect()的值取决于套接字地址族。


socket.AF_UNIX

socket.AF_INET
的区别:

socket.AF_INET
:用于IPv4网络通信,即通过IP地址和端口进行通信。它使用一个两元组
(host, port)
来指定地址,其中
host
是字符串形式的IP地址(如 '192.168.1.1')或域名(如 '),
port
是整数端口号。
socket.AF_UNIX
:用于同一台机器上的进程间通信(IPC),它使用文件系统路径来标识套接字。例如,一个进程可以创建一个套接字并绑定到一个文件路径(如 '/tmp/my_socket'),其他进程通过这个路径来连接。

这是因为当使用
socket.AF_UNIX
时,套接字地址是一个文件系统路径(字符串)。所以
connect
方法期望一个字符串路径,而不是两元组。

特性 AF_INET AF_UNIX
通信范围 跨网络 单机进程间
地址格式
(host, port)

"/path/to/socket"
速度 较慢(网络协议栈) 很快(内存拷贝)
安全性 网络防火墙 文件系统权限
典型应用 Web服务器、客户端 Docker API、数据库、系统服务

【注意】Docker Socket逃逸原理

Docker守护进程默认监听在Unix套接字
/var/run/docker.sock
上,提供HTTP API。

在Docker容器中,如果挂载了宿主机的Docker守护进程的Unix套接字,那么容器内的进程就可以通过这个套接字与Docker守护进程通信(),如果容器内可以访问这个套接字,就可以发送Docker API请求到这个套接字。

例如创建新的容器并挂载宿主机目录,从而读取宿主机文件或获得宿主机shell。从而控制Docker守护进程,实现逃逸。

Unix Domain Socket:一种进程间通信方式,允许在同一台机器上运行的进程之间进行数据交换。Docker守护进程默认监听/var/run/docker.sock。

docker.sock是啥请见附录3!

socket.socket()详细学习请见:

https://blog.csdn.net/m0_59777389/article/details/156294025?sharetype=blogdetail&sharerId=156294025&sharerefer=PC&sharesource=m0_59777389&spm=1011.2480.3001.8118

权限提升与逃逸攻击debug记录

HTTP协议格式

HTTP请求格式:
方法 URL HTTP版本
头部字段:
字段名: 字段值
重要细节

是HTTP协议要求的换行符,最后需要一个空行

表示头部结束学习建议:用Wireshark或浏览器开发者工具查看真实HTTP请求

【注意】虽然Unix域套接字不是网络套接字,但Docker守护进程的API是HTTP协议,所以需要按照HTTP格式构造请求。在HTTP请求中,Host头部在Unix域套接字中并不是必须的,但Docker API可能要求,所以这里还是加上了。

字节编码和网络通信


.encode()
:将字符串转换为bytes(字节)
sendall()
:确保发送所有数据,不像
send()
可能只发送部分常见错误:忘记编码或发送不完全

权限提升与逃逸攻击debug记录

网络数据接收


recv(4096)
:每次最多接收4096字节
while True
循环:持续接收直到满足条件重要细节1:
b""
创建空的bytes对象重要细节2:
b"0

"是HTTP chunked传输编码的结束标记实际技巧:在生产代码中应该使用更严谨的HTTP解析

关闭连接

必须关闭socket连接释放资源最佳实践:使用
with
语句自动管理

权限提升与逃逸攻击debug记录

HTTP响应解析


find()
:在bytes中查找子串位置HTTP头和body之间用

分隔
+4
:跳过分隔符本身调试技巧:可以打印
response
查看完整格式

权限提升与逃逸攻击debug记录

step3:如果没逃逸成功,尝试暴力手段 (GCC编译)

权限提升与逃逸攻击debug记录

我们步入simulate_dirty_cow_exploit()函数的内部,查看step3是如何尝试内核漏洞,编译并执行C语言代码的,如下图:

权限提升与逃逸攻击debug记录

这里我们仅做行为模拟,实际攻击代码省略(真实的 Dirty COW 代码会尝试写入只读文件 /etc/passwd),真实Dirty COW漏洞利用要复杂得多,涉及内核内存竞争,我们在这里不过多讨论真实的gcc攻击代码的具体内容(这是黑客干的活!!!)

多行字符串

使用三个双引号
"""
定义多行字符串注意字符串中的转义字符
\n
会变成

C语言基础回顾

#include <stdio.h>
:标准输入输出头文件
#include <stdlib.h>
:标准库头文件
printf()
:格式化输出函数
system("id")
:执行shell命令
id
(显示用户身份)
return 0
:程序正常退出

/etc/passwd文件是什么?Dirty COW漏洞是什么?

相关内容请见:附录4

权限提升与逃逸攻击debug记录

临时文件路径选择

使用
/tmp/
目录存储临时文件
/tmp
目录通常所有用户都有写权限攻击者常使用
/tmp

/dev/shm
等可写目录安全防护:监控
/tmp
目录的可疑文件创建

执行完这一步后,我打开我的Ubuntu虚拟机查看了一下果然,在/tmp目录下生成了一个dcow_sim.c的C语言代码文件,如下图所示:

权限提升与逃逸攻击debug记录

权限提升与逃逸攻击debug记录

GCC编译命令详解


gcc
:GNU编译器集合
{src_file}
:要编译的源文件(C文件)
-o {exe_file}
:指定输出可执行文件名

安全检测要点

生产容器通常不安装gcc等开发工具编译行为是高风险的异常活动容器安全产品会检测
gcc

make
等命令的执行

gcc /tmp/dcow_sim.c -o /tmp/dcow_sim

# 这个命令做了以下几件事:
1. 预处理:处理 #include、#define 等
2. 编译:将C代码翻译成汇编代码
3. 汇编:将汇编代码翻译成机器码(目标文件)
4. 链接:连接库函数,生成最终可执行文件

有关GCC编译相关命令请见附录5

执行完编译代码后,我的Ubuntu虚拟机的/tmp目录下生成了一个dcow_sim*的文件,这是一个可执行二进制文件(因为有
x
权限)

权限提升与逃逸攻击debug记录

图中星号
*
的意义

这是
ll

ls -F
命令的标记,表示可执行文件类似的标记:

*
:可执行文件
/
:目录
@
:符号链接
|
:管道文件

权限提升与逃逸攻击debug记录

条件判断与文件检查


os.path.exists()
:检查文件是否存在在执行前检查可以避免
FileNotFoundError

logger.critical()
:记录关键/危险事件

可执行文件执行

直接使用文件路径执行:
./path/to/executable
注意文件需要有可执行权限

接下来执行这个编译好的gcc可执行文件(bash命令:/tmp/dcow_sim),这相当于在Ubuntu虚拟机里面执行bash命令,如下图所示:

权限提升与逃逸攻击debug记录

文件清理与痕迹隐藏(功成身退,不留痕迹!!)

攻击者常用手法:用完即删,消灭证据
os.remove()
:删除文件(不可恢复)安全取证思路:监控文件删除行为,特别是临时文件的快速创建和删除

附录

1.Ubuntu虚拟机中执行mount命令详解

有关mount命令的使用请见本人的另一篇文档:

https://blog.csdn.net/m0_59777389/article/details/156241824?sharetype=blogdetail&sharerId=156241824&sharerefer=PC&sharesource=m0_59777389&spm=1011.2480.3001.8118

权限提升与逃逸攻击debug记录

从你提供的截图来看,这不是一个典型的Docker容器内部的视图,而是一个**完整的Linux操作系统(宿主机或完整的虚拟机)**的视角。

一、 图中信息深度解析

你的截图显示了 mount 命令的输出结果。每一行都代表一个“挂载”,格式通常为:

' 设备/源  on  挂载点  type  文件系统类型   (挂载选项) '

我们可以将图中的信息分为三类来解读:

1. 物理磁盘与系统分区(核心数据所在)


/dev/mapper/ubuntu--vg-ubuntu--lv on / type ext4 (rw,relatime)

含义:这是你的根文件系统(Root Filesystem)。它位于一个LVM(逻辑卷管理)卷上,格式是 ext4。

状态
rw
表示读写模式(Read-Write)。这是系统运行的核心。


/dev/sda2 on /boot type ext4

含义:这是引导分区,存放内核文件。


/dev/sda1 on /boot/efi type vfat

含义:这是EFI系统分区,用于UEFI启动。

2. 虚拟文件系统(内核与进程交互的接口)

这部分是之前的脚本
malicious_escape_simulation.py
重点关注的对象。


proc on /proc type proc

含义:这是一个伪文件系统,包含运行中进程的信息(如内存、CPU状态)。容器逃逸的高危点


sysfs on /sys type sysfs

含义:导出内核对象(如硬件设备驱动信息)的接口。


cgroup2 on /sys/fs/cgroup

含义:控制组(Cgroups),用于限制资源(CPU/内存)。这是容器技术(Docker)的基石。

3. 临时文件系统(内存中的存储)


tmpfs on /run


udev on /dev

含义:这些数据存储在内存中,重启即丢失。


二、 Mount(挂载)的作用是什么?

在 Linux 中,”一切皆文件”。Mount(挂载) 就是将物理设备(如硬盘、U盘)或虚拟资源(如内核接口)“拼接”到系统的目录树(Directory Tree)上的过程。

对于云平台运维来说,Mount 决定了:

数据存在哪:是写在本地硬盘,还是写在远程存储(NFS/Ceph),还是写在内存里。

能不能写:是只读(
ro
)还是读写(
rw
)。

隔离性:容器看到了哪些目录?它能不能看到宿主机的
/etc


三、 安全风险:为什么恶意脚本要检查 Mount?

在你的脚本
malicious_escape_simulation.py
中,
check_dangerous_mounts
函数之所以执行
mount
,是为了寻找配置错误导致的逃逸路径

根据你提供的文档,权限提升与逃逸通常利用“配置错误利用:利用 privileged mode、挂载错误等配置问题” 。

以下是运维中必须警惕的危险挂载(Dangerous Mounts)

1. 宿主机根目录挂载 (
/
)

现象:容器内
mount
看到宿主机的分区挂载在容器内某个目录(如
/mnt/host
)。

风险:如果容器能看到宿主机的根目录,攻击者可以直接修改宿主机的
/etc/crontab
(计划任务)或
/root/.ssh/authorized_keys
,从而直接控制宿主机。这是最彻底的逃逸。

2. Docker Socket 挂载 (
/var/run/docker.sock
)

现象
mount
输出中包含
docker.sock

风险:这是最常见且最致命的配置错误。

原理
docker.sock
是 Docker 守护进程的遥控器。如果容器内有这个文件,容器里的进程就可以发指令给宿主机的 Docker Daemon。

攻击后果:攻击者可以说:“嗨,宿主机Docker,帮我启动一个新的容器,把宿主机的根目录
/
挂载进去,并且给我特权模式!” —— 然后攻击者进入新容器,就等于获得了宿主机的 Root 权限 。

你的截图你的截图中没有出现
docker.sock
,说明你的当前环境在这个维度是安全的(或者你是在宿主机上运行,而不是在容器里)。

3.
/proc

/sys
的读写权限

现象
proc

sysfs
被挂载为
rw
(读写),且没有掩盖敏感路径。

风险

攻击者可以通过写入
/proc/sys/kernel/core_pattern
等文件,指示宿主机在程序崩溃时运行恶意脚本,从而实现逃逸。

攻击者可能通过
/proc
读取到宿主机上其他容器的内存信息。


四、 运维建议:如何加固?

作为海洋科学计算平台的运维人员,针对上述风险,建议采取以下措施:

最小权限原则

严禁将
/var/run/docker.sock
挂载到普通计算任务容器中。如果任务需要操作容器,应使用受限的 API 或 Sidecar 模式。

文档建议使用“AppArmor/SELinux配置文件”来限制文件访问 3。

挂载选项加固

对于
/proc

/sys
等系统目录,在容器中应强制为 只读(Read-Only, ro)

使用
nodev
(禁止字符/块设备)、
nosuid
(禁止 Set-User-ID 程序)、
noexec
(禁止执行二进制文件)等挂载选项来限制非必要分区(如
/tmp
或数据卷)。

监控与审计

使用脚本定期扫描集群中所有容器的挂载信息(如你练习的这个脚本逻辑)。如果发现含有
docker.sock
或宿主机敏感路径的挂载,立即报警。

总结:你图中的
mount
输出是一个标准的 Linux 宿主机/虚拟机状态。如果这是在一个提供给用户的“计算容器”里看到的(能看到
/dev/sda
和完整的
/
),那就说明隔离性完全失效了,这是一个极高危的信号。但在你自己的 Ubuntu 虚拟机(宿主机)上看到这个,是完全正常的。

2.subprocess.run()

subprocess.run()是Python中用于运行外部命令的高级函数,它返回一个CompletedProcess对象,包含命令执行的结果。它是在Python 3.5中引入的,旨在替代一些旧的函数,如os.system()、os.spawn()、os.popen()等。

基本语法:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False,shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None,env=None, universal_newlines=None, **other_popen_kwargs)

常用参数说明:

args:要执行的命令,可以是一个字符串(当shell=True时)或一个字符串列表(推荐使用列表,可以避免注入攻击)。capture_output:如果为True,则会捕获stdout和stderr,并将其赋给返回CompletedProcess对象的stdout和stderr属性。注意:不能同时使用capture_output和stdout/stderr参数,否则会引发异常。shell:如果为True,则通过系统的shell执行命令。这允许使用shell的特性,如通配符、管道等,但可能带来安全风险。cwd:设置命令执行的工作目录。timeout:设置命令超时时间(秒),如果命令执行时间超过该时间,会抛出TimeoutExpired异常。check:如果为True,且命令的返回码非0,则会抛出CalledProcessError异常。encoding:如果指定,则stdin、stdout和stderr将使用该编码进行文本转换。也可以使用text参数来指定是否以文本模式处理。text:如果为True,则stdin、stdout和stderr将以文本模式处理,否则为字节模式。

返回的CompletedProcess对象具有以下属性:

args:传入的参数。returncode:命令的返回码,0通常表示成功。stdout:捕获的标准输出(如果capture_output=True或stdout=subprocess.PIPE)。stderr:捕获的标准错误(如果capture_output=True或stderr=subprocess.PIPE)。

系统交互类高危操作还包括:


os.system()

描述:直接执行系统命令。

高危点:用户可以通过
os.system("rm -rf /")
等命令直接破坏系统,或通过命令窃取敏感信息(如
os.system("cat /etc/passwd")
)。


subprocess.call()

描述:调用外部进程并执行系统命令。

高危点:与
os.system()
类似,但功能更强大,支持参数传递和更复杂的命令组合,攻击者可能利用它执行复杂的恶意命令。


subprocess.Popen()

描述:创建子进程并执行系统命令,支持更底层的,非阻塞的进程交互。

高危点:用户可以通过
Popen
执行任意系统命令,并通过管道、文件描述符等与系统交互,增加攻击的隐蔽性和复杂性。


subprocess.run()

描述:Python 3.5+ 提供的更高级的命令执行接口。

高危点:与
subprocess.call()
类似,允许用户执行任意系统命令,且支持更现代化的参数配置,可能被用于恶意目的。

Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。run 方法调用方式返回 CompletedProcess 实例,和直接 Popen 差不多,实现是一样的,实际也是调用 Popen,与 Popen 构造函数大致相同。通常情况下,如果你只想运行命令并捕获其输出,应该使用 `run`;如果你需要更多对进程的控制,例如与其输入和输出流进行交互,则应使用 Popen。`Popen` 类与 `run()` 接受相同的参数,包括指定要运行的命令的 `args` 以及其他可选参数,如 `stdin`、`stdout`、`stderr`、`shell`、`cwd` 和 `env`。

Python subprocess 模块的作用

1. 执行外部命令:subprocess 允许你在 Python 脚本中执行系统命令或其他可执行文件。
2. 进程管理:你可以启动新的进程并与其交互,监视它们的状态以及等待它们的完成。
3. 输入/输出重定向:通过 subprocess,你能够重定向进程的标准输入、输出和错误流,实现和外部进程的交互。

有关Python subprocess 模块的深入学习请见:

https://www.geeksforgeeks.org/python/python-subprocess-module/

https://www.cnblogs.com/wangguishe/p/18307131

https://www.runoob.com/python3/python-subprocess.html

https://www.runoob.com/w3cnote/python3-subprocess.html

https://docs.python.org/zh-cn/3/library/subprocess.html#subprocess.run

3.docker.sock是啥?用户如何利用docker.sock实现逃逸呢?

这是一个非常硬核且核心的问题。作为平台运维人员,理解
docker.sock
的本质及其带来的“上帝模式”风险,是理解容器逃逸攻击的基石。

我将为你拆解
docker.sock
的本质,并演示如何利用它实现逃逸。

一、
docker.sock
到底是啥?

简单来说,
docker.sock
是 Docker 守护进程(Docker Daemon)的“遥控接收器”

权限提升与逃逸攻击debug记录

本质:它是一个 Unix Domain Socket(一种进程间通信机制 IPC)。这就好比是计算机内部的一条“电话线”,允许同一个操作系统上的不同进程进行通信。

角色:Docker 采用的是 C/S(客户端/服务端)架构。

Server (Docker Daemon):后台一直在运行的那个进程,负责干脏活累活(创建容器、管理镜像、配置网络)。它一直在监听
docker.sock
这条“电话线”。

Client (Docker CLI):你在终端敲的
docker run

docker ps
命令。

通信过程:当你敲下
docker ps
时,Docker 客户端会将这个指令通过
docker.sock
发送给 Docker Daemon。Daemon 执行完后,再通过这个 Socket 把结果发回给你。

权限:默认情况下,只有 Root 用户 和 Docker 用户组 才能通过这个 Socket 发号施令。拥有了这个 Socket 的读写权限,就等于拥有了宿主机的 Root 权限。


二、 为什么挂载它就能逃逸?(上帝模式原理)

假设管理员为了方便(比如要在容器里通过 Jenkins 构建 Docker 镜像),把宿主机的
/var/run/docker.sock
挂载到了你的容器内部。

这就相当于管理员把宿主机 Docker 守护进程的“遥控器”递到了你手里。

虽然你人(进程)被关在容器 A 里,但你可以拿起这个遥控器,对着宿主机的 Docker Daemon 喊话:“嘿!帮我启动一个新的容器 B,但是要把宿主机的根目录挂载到容器 B 里!

宿主机的 Daemon 收到指令后,会乖乖照做。因为指令是合法的,它无法区分发令的是管理员还是恶意的容器进程。


三、 逃逸实操演示(用户如何操作)

假设你已经通过 Webshell 或代码执行进入了一个容器,并且发现
/var/run/docker.sock
存在。以下是标准的逃逸步骤:

第一步:在容器内安装/使用 Docker 客户端

你需要一个能跟 Socket 说话的工具。

方法 A(简单版):如果容器里正好装了 docker 命令行工具(很多 CI/CD 容器都有)。

方法 B(硬核版):如果没有 docker 命令,你可以用
curl
发送 HTTP 请求(正如debug过程中 
_exploit_docker_socket
函数所做的那样)。

第二步:发动逃逸指令 (The Escape Command)

攻击者在容器内执行以下命令:



# 这是一个经典的逃逸命令
docker -H unix:///var/run/docker.sock run -v /:/host -it ubuntu bash

命令解析


-H unix:///var/run/docker.sock
:告诉 Docker 客户端,不要连接默认位置,而是连接我当前挂载的这个 Socket(拿起手边的遥控器)。
run
:启动一个新容器。
-v /:/host
(核心杀招)
将宿主机的根目录
/
挂载到新容器的
/host
目录下。

-it ubuntu bash
:启动并进入这个新容器的交互式 Shell。

第三步:进入新容器,控制宿主机

命令执行成功后,你其实已经“穿越”到了一个新的容器里。在这个新容器里,你执行:


ls /host

你会惊讶地发现,你看到了宿主机完整的文件系统!


/host/etc/passwd
是宿主机的用户表。
/host/root/.ssh/
是宿主机的 SSH 密钥。

第四步:彻底接管 (Chroot)

为了让操作更方便,攻击者通常会执行:


chroot /host

这条命令会将当前 Shell 的根目录切换到 /host。

此时,你打开的 Shell,看起来、用起来,就完全等同于你在宿主机上直接登录了一个 Root Shell。

你可以修改宿主机的 Root 密码、添加 SSH 公钥、植入挖矿木马,随心所欲。


四、 结合你的代码与日志

你的debug示例代码正是在模拟这个过程,只不过它用的是 Python 代码而非命令行:

代码行为:它定义了 _exploit_docker_socket 函数。

连接 Socket:


curl -s --unix-socket /var/run/docker.sock ...
。这相当于拿起了遥控器。

发送指令:

代码中构造了一个 JSON 数据包,其中的 “Cmd”: [“/bin/sh”, “-c”, “{host_cmd}”] 和挂载逻辑,就是在告诉 Daemon 在宿主机层面执行命令。

日志体现:

日志中出现的 DOCKER API ACCESSIBLE – CONTAINER ESCAPE SUCCESSFUL就是攻击成功的标志。

总结

docker.sock = 宿主机 Docker 守护进程的通信接口(皇宫的钥匙)。挂载进容器 = 把钥匙扔进了监狱。逃逸原理 = 囚犯捡起钥匙,打开牢门(创建新容器),并把整个皇宫(宿主机根目录)搬进了自己的新房间。

4./etc/passwd文件是什么?

/etc/passwd是Linux系统中存储用户账户信息的重要文件。它包含了每个用户的基本信息,每行代表一个用户,每行由7个字段组成,用冒号分隔。

每行的格式如下:
用户名:密码占位符:用户ID(UID):组ID(GID):描述信息:家目录:登录shell

 username:password:UID:GID:GECOS:homedir:shell
 └──┬─┘  └──┬─┘└┬┘└┬┘└─┬─┘└─┬─┘  └─┬┘
    用户名       密码    UID  GID   描述     家目录   登录shell

举例:



# 1. root用户(系统管理员)
root:x:0:0:root:/root:/bin/bash
#  用户名: root
#  密码: x (表示密码在/etc/shadow中)
#  UID: 0 (系统最高权限)
#  GID: 0 (root组)
#  描述: root
#  家目录: /root
#  登录shell: /bin/bash
 
# 2. 普通用户(你的用户)
user:x:1000:1000:wangminghao:/home/user:/bin/bash
#  用户名: user
#  密码: x (在/etc/shadow中)
#  UID: 1000 (普通用户通常从1000开始)
#  GID: 1000 (主组ID)
#  描述: wangminghao (你的全名)
#  家目录: /home/user
#  登录shell: /bin/bash
 
# 3. 系统服务账户
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
#  用户名: daemon
#  不能登录: /usr/sbin/nologin
#  用于运行系统守护进程,没有登录权限
 
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
#  SSH服务账户,不能直接登录

【注意】在早期的Unix系统中,密码哈希值就存放在/etc/passwd的第二个字段,但现在为了安全,通常放在/etc/shadow中,而这里用x表示。

【每个字段的深度解释】

/etc/passwd里面,每行代表一个用户,每行由7个字段组成,用冒号分隔。

1. 用户名 (username)

user:x:1000:1000:...
^用户名最长为32字符,不能包含冒号

2. 密码占位符 (password field)


x
:密码存储在
/etc/shadow
中(现代Linux默认)
*

!
:账户被锁定,不能登录空:无需密码即可登录(极其危险!)加密字符串:老式系统直接存储加密密码

3. 用户ID (UID – User ID)


0
:root用户,最高权限
1-99
:系统预定义用户
100-999
:系统服务和守护进程
1000+
:普通用户
65534
:特殊用户
nobody

4. 组ID (GID – Group ID)

用户的主组ID

对应
/etc/group
文件

5. 描述信息 (GECOS/comment field)

通常存储用户全名、电话、办公室等

可包含逗号分隔的多个字段

6. 家目录 (home directory)

用户登录后的初始目录

root:
/root

普通用户:
/home/用户名

7. 登录shell (login shell)


/bin/bash
:正常的shell


/usr/sbin/nologin
:禁止登录


/bin/false
:禁止登录且不执行任何操作


/bin/sync
:特殊用途

为什么/etc/passwd文件重要?

因为它包含了系统中所有用户的信息,尤其是UID。UID为0的用户是root,拥有最高权限。如果攻击者能够修改这个文件,他们可以创建一个UID为0的用户,从而获得root权限。


/etc/passwd
是 Linux/Unix 系统中最重要的系统文件之一
,相当于Windows中的注册表或SAM数据库。

用户认证的核心:系统登录时,首先检查这个文件权限管理的基础:所有文件和进程的权限都基于这里的UID/GID系统稳定性的关键:损坏会导致所有用户无法登录

Dirty COW漏洞是什么?
Dirty COW(脏牛)是Linux内核中的一个漏洞,全称是Copy-on-Write(COW)漏洞。它允许攻击者利用竞态条件(race condition)来修改只读文件,从而提升权限。

漏洞原理(简化):
Linux内核的内存子系统在处理写时拷贝(Copy-on-Write)时存在竞争条件,导致攻击者可以修改只读文件。例如,攻击者可以修改/etc/passwd文件,添加一个具有root权限的用户。

注意:这个漏洞在2016年被发现,并且已经修复。现在的大多数系统都已经打了补丁。

5.GCC编译命令

GCC编译命令是用于将C/C++源代码编译为可执行程序的核心工具,基本流程是:预处理 -> 编译 -> 汇编 -> 链接

常用命令如
gcc main.c -o myprog
(编译并链接);关键选项有
-c
(仅编译/汇编成.o文件)、
-o
(指定输出文件名)、
-g
(生成调试信息)、
-Wall
(显示所有警告)、
-O2
(优化)、
-S
(生成汇编文件)、
-E
(预处理),它们控制了编译流程和生成结果。 

1. 基本用法

编译并生成可执行文件

gcc source.c -o executable_name

例:
gcc hello.c -o hello
 

2. 常用编译流程步骤及选项

预处理 (Preprocessing)
gcc -E source.c -o source.i
编译 (Compilation)
gcc -S source.i -o source.s
汇编 (Assembly)
gcc -c source.s -o source.o
链接 (Linking)
gcc source.o -o executable_name
(链接多个.o文件)一步完成 (常用)
gcc source.c -o executable_name
 

3. 重要选项 (Options)


-o <name>
:指定输出文件名称。
-c
:仅编译和汇编,生成目标文件(
.o
),不链接。
-g
:包含调试信息,供 GDB 等调试器使用。
-Wall
:开启所有警告信息,有助于发现潜在问题。
-O<level>
:代码优化等级,如
-O2
(中等优化)。
-std=<standard>
:指定 C/C++ 标准,如
-std=c99

-shared
:生成共享库(动态库 .so),常配合
-fPIC

-fPIC
:生成位置无关代码,用于共享库。
-L<path>
:指定库文件搜索路径。
-l<libname>
:链接指定库(例如
-lm
链接数学库)。 

4. C++ 编译

C++ 源文件通常用
.cpp
后缀。使用
g++
命令编译更方便,它能自动链接 C++ 标准库。

g++ main.cpp -o myprog
 

© 版权声明

相关文章

暂无评论

none
暂无评论...