权限提升与逃逸攻击debug记录
0.前提工作
搭建一台ubuntu虚拟机,并配置好SSH,安装好Anaconda环境。使用Windows宿主机上的VSCode远程连接上Ubuntu虚拟机,并且在VSCode里调试这个脚本。为远程SSH端的VSCode安装插件:
Remote – SSH (Microsoft 出品)Python (Microsoft 出品)
这样你可以获得如同在本地一样的代码编辑和调试体验,同时代码实际运行在 Ubuntu 虚拟机中。如下图所示:

1.debug过程

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

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


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

这里使用的是Python内置的标准日志模块logging模块,来打日志的。
有关python怎么打日志的文档请见:
https://blog.csdn.net/m0_59777389/article/details/155931579?spm=1011.2415.3001.5331

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

step1:在第一批次计算时,尝试侦察(检查Docker Socket挂载点)
执行挂载点检查(检查是否有Docker Socket挂载)
其实此处通过睡眠1s来模拟这个过程,并没有加载到任何数据集。

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

先通过调用processor.process_batch(1)方法,通过海洋科学计算处理器进行科学计算任务,如上图,此处我们通过random.random() + time.sleep()分别来模拟计算过程和延迟。
【插入知识点】
是 Python
random.random()模块中的一个函数,它返回一个介于 0.0(包含)和 1.0(不包含)之间的伪随机浮点数,即在半开放区间
random内均匀分布的随机数。它是其他许多随机函数(如
[0.0, 1.0),
random.randint())的底层基础,使用 Mersenne Twister 算法生成。
random.choice()均匀分布的随机数是指在特定区间内,每个数值出现的概率相等,最常见的是生成0到1之间的随机数,可以通过编程语言(如C++的
配合
rand()、Python的
srand(time(0)))或工具(如Excel的
numpy.random.uniform())来实现,并通过数学公式调整到任意范围(例如
=RAND())或生成整数(如
rand() * (b-a) + a)。
(int)(rand() * 100)
(来自 Python 内置的
random.uniform()模块) 和 numpy.random.uniform() (来自 NumPy 库) 都是生成指定范围内均匀分布随机浮点数(小数)的函数,区别在于一个是标准库,另一个是科学计算库;
random都用于在给定的开区间
(包含
[low, high),不包含
low)或闭区间
high(取决于具体实现和版本,但通常是半开区间)中随机选择一个数。
[low, high]

再通过调用attacker.check_dangerous_mounts()方法,执行挂载点检查。如上图,在check_dangerous_mounts()方法中,通过调用self._run_command(cmd, “List all mounts”)方法,执行bash命令,并获取挂载信息。
该方法返回一个subprocess.CompletedProcess 对象,并赋值给result,result里面有一个stdout属性,该属性的值为执行mount命令后生成的虚拟机挂载信息。如下图所示:


接着再通过执行if self.target_socket in result.stdout:代码,检查是否有Docker Socket挂载信息。(这行代码属于字符串匹配的效果了)
如果有,就往logger里面写入一条critical级别的日志信息,并返回True。
`mount` 命令的作用详解请见附录1
另外咱们再单拉出来一下这行代码:result = self._run_command(cmd, “List all mounts”) # 执行bash命令,获取挂载信息
步入方法内部,具体代码如下:

result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
(重难点) 这是 Python 执行系统命令的官方推荐方式。
subprocess.run
: 你要执行的命令(如
cmd,
ls 等)。
ping
: 表示通过系统的 Shell(如 Linux 的 bash 或 Windows 的 cmd)来解释执行。
shell=True
: 捕获输出。它会把命令运行的结果存入内存,而不是直接显示在屏幕上。如果为True,则会捕获stdout和stderr,并将其赋给返回CompletedProcess对象的stdout和stderr属性。
capture_output=True
: 将结果以**文本(字符串)**形式返回。如果不加这个,返回的是字节码(bytes)。
text=True
: 超时保护。如果命令执行超过 10 秒还没结束,程序会强制关掉它并抛出异常,防止程序卡死。
timeout=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逃逸)

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

如上图所示,其实我们的代码并未检测到'/var/run/docker.sock'路径,所以该方法直接return false,后面代码不再执行。
函数
os.path.exists()
检查文件或路径是否存在
但是我们任然分析一下,Docker Socket逃逸是如何执行执行的?

:Unix域套接字,用于同一台机器上的进程间通信
socket.AF_UNIX:流式套接字(TCP类型)
socket.SOCK_STREAM:连接到指定的socket地址
socket.connect()
重要提示:这用于攻击是因为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
:用于IPv4网络通信,即通过IP地址和端口进行通信。它使用一个两元组
socket.AF_INET 来指定地址,其中
(host, port) 是字符串形式的IP地址(如 '192.168.1.1')或域名(如 '),
host 是整数端口号。
port:用于同一台机器上的进程间通信(IPC),它使用文件系统路径来标识套接字。例如,一个进程可以创建一个套接字并绑定到一个文件路径(如 '/tmp/my_socket'),其他进程通过这个路径来连接。
socket.AF_UNIX
这是因为当使用 时,套接字地址是一个文件系统路径(字符串)。所以
socket.AF_UNIX 方法期望一个字符串路径,而不是两元组。
connect
| 特性 | AF_INET | AF_UNIX |
|---|---|---|
| 通信范围 | 跨网络 | 单机进程间 |
| 地址格式 | |
|
| 速度 | 较慢(网络协议栈) | 很快(内存拷贝) |
| 安全性 | 网络防火墙 | 文件系统权限 |
| 典型应用 | Web服务器、客户端 | Docker API、数据库、系统服务 |
【注意】Docker Socket逃逸原理:
Docker守护进程默认监听在Unix套接字 上,提供HTTP API。
/var/run/docker.sock
在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

HTTP协议格式
HTTP请求格式:头部字段:
方法 URL HTTP版本
重要细节:
字段名: 字段值
是HTTP协议要求的换行符,最后需要一个空行
表示头部结束学习建议:用Wireshark或浏览器开发者工具查看真实HTTP请求
【注意】虽然Unix域套接字不是网络套接字,但Docker守护进程的API是HTTP协议,所以需要按照HTTP格式构造请求。在HTTP请求中,Host头部在Unix域套接字中并不是必须的,但Docker API可能要求,所以这里还是加上了。
字节编码和网络通信
:将字符串转换为bytes(字节)
.encode():确保发送所有数据,不像
sendall()可能只发送部分常见错误:忘记编码或发送不完全
send()

网络数据接收
:每次最多接收4096字节
recv(4096) 循环:持续接收直到满足条件重要细节1:
while True 创建空的bytes对象重要细节2:
b""
b"0
"是HTTP chunked传输编码的结束标记实际技巧:在生产代码中应该使用更严谨的HTTP解析
关闭连接
必须关闭socket连接释放资源最佳实践:使用语句自动管理
with

HTTP响应解析
:在bytes中查找子串位置HTTP头和body之间用
find()
分隔:跳过分隔符本身调试技巧:可以打印
+4查看完整格式
response

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

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

这里我们仅做行为模拟,实际攻击代码省略(真实的 Dirty COW 代码会尝试写入只读文件 /etc/passwd),真实Dirty COW漏洞利用要复杂得多,涉及内核内存竞争,我们在这里不过多讨论真实的gcc攻击代码的具体内容(这是黑客干的活!!!)
多行字符串
使用三个双引号定义多行字符串注意字符串中的转义字符
"""会变成
\n
C语言基础回顾:
:标准输入输出头文件
#include <stdio.h>:标准库头文件
#include <stdlib.h>:格式化输出函数
printf():执行shell命令
system("id")(显示用户身份)
id:程序正常退出
return 0
/etc/passwd文件是什么?Dirty COW漏洞是什么?
相关内容请见:附录4

临时文件路径选择
使用目录存储临时文件
/tmp/目录通常所有用户都有写权限攻击者常使用
/tmp、
/tmp等可写目录安全防护:监控
/dev/shm目录的可疑文件创建
/tmp
执行完这一步后,我打开我的Ubuntu虚拟机查看了一下果然,在/tmp目录下生成了一个dcow_sim.c的C语言代码文件,如下图所示:


GCC编译命令详解:
:GNU编译器集合
gcc:要编译的源文件(C文件)
{src_file}:指定输出可执行文件名
-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

图中星号的意义:
*
这是或
ll命令的标记,表示可执行文件类似的标记:
ls -F
:可执行文件
*:目录
/:符号链接
@:管道文件
|

条件判断与文件检查
:检查文件是否存在在执行前检查可以避免
os.path.exists()
FileNotFoundError:记录关键/危险事件
logger.critical()
可执行文件执行:
直接使用文件路径执行:注意文件需要有可执行权限
./path/to/executable
接下来执行这个编译好的gcc可执行文件(bash命令:/tmp/dcow_sim),这相当于在Ubuntu虚拟机里面执行bash命令,如下图所示:

文件清理与痕迹隐藏(功成身退,不留痕迹!!)
攻击者常用手法:用完即删,消灭证据:删除文件(不可恢复)安全取证思路:监控文件删除行为,特别是临时文件的快速创建和删除
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

从你提供的截图来看,这不是一个典型的Docker容器内部的视图,而是一个**完整的Linux操作系统(宿主机或完整的虚拟机)**的视角。
一、 图中信息深度解析
你的截图显示了 mount 命令的输出结果。每一行都代表一个“挂载”,格式通常为:
' 设备/源 on 挂载点 type 文件系统类型 (挂载选项) '
我们可以将图中的信息分为三类来解读:
1. 物理磁盘与系统分区(核心数据所在)
/dev/mapper/ubuntu--vg-ubuntu--lv on / type ext4 (rw,relatime)含义:这是你的根文件系统(Root Filesystem)。它位于一个LVM(逻辑卷管理)卷上,格式是 ext4。
状态:
表示读写模式(Read-Write)。这是系统运行的核心。
rw
/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_keys2. Docker Socket 挂载 (
)
/var/run/docker.sock现象:
输出中包含
mount。
docker.sock风险:这是最常见且最致命的配置错误。
原理:
是 Docker 守护进程的遥控器。如果容器内有这个文件,容器里的进程就可以发指令给宿主机的 Docker Daemon。
docker.sock攻击后果:攻击者可以说:“嗨,宿主机Docker,帮我启动一个新的容器,把宿主机的根目录
挂载进去,并且给我特权模式!” —— 然后攻击者进入新容器,就等于获得了宿主机的 Root 权限 。
/你的截图:你的截图中没有出现
,说明你的当前环境在这个维度是安全的(或者你是在宿主机上运行,而不是在容器里)。
docker.sock3.
和
/proc的读写权限
/sys现象:
或
proc被挂载为
sysfs(读写),且没有掩盖敏感路径。
rw风险:
攻击者可以通过写入
等文件,指示宿主机在程序崩溃时运行恶意脚本,从而实现逃逸。
/proc/sys/kernel/core_pattern攻击者可能通过
读取到宿主机上其他容器的内存信息。
/proc
四、 运维建议:如何加固?
作为海洋科学计算平台的运维人员,针对上述风险,建议采取以下措施:
最小权限原则:
严禁将
挂载到普通计算任务容器中。如果任务需要操作容器,应使用受限的 API 或 Sidecar 模式。
/var/run/docker.sock文档建议使用“AppArmor/SELinux配置文件”来限制文件访问 3。
挂载选项加固:
对于
、
/proc等系统目录,在容器中应强制为 只读(Read-Only, ro)。
/sys使用
(禁止字符/块设备)、
nodev(禁止 Set-User-ID 程序)、
nosuid(禁止执行二进制文件)等挂载选项来限制非必要分区(如
noexec或数据卷)。
/tmp监控与审计:
使用脚本定期扫描集群中所有容器的挂载信息(如你练习的这个脚本逻辑)。如果发现含有
或宿主机敏感路径的挂载,立即报警。
docker.sock总结:你图中的
输出是一个标准的 Linux 宿主机/虚拟机状态。如果这是在一个提供给用户的“计算容器”里看到的(能看到
mount和完整的
/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 守护进程(Docker Daemon)的“遥控接收器”。
docker.sock

本质:它是一个 Unix Domain Socket(一种进程间通信机制 IPC)。这就好比是计算机内部的一条“电话线”,允许同一个操作系统上的不同进程进行通信。
角色:Docker 采用的是 C/S(客户端/服务端)架构。
Server (Docker Daemon):后台一直在运行的那个进程,负责干脏活累活(创建容器、管理镜像、配置网络)。它一直在监听 这条“电话线”。
docker.sock
Client (Docker CLI):你在终端敲的 、
docker run 命令。
docker ps
通信过程:当你敲下 时,Docker 客户端会将这个指令通过
docker ps 发送给 Docker Daemon。Daemon 执行完后,再通过这个 Socket 把结果发回给你。
docker.sock
权限:默认情况下,只有 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 命令,你可以用 发送 HTTP 请求(正如debug过程中
curl 函数所做的那样)。
_exploit_docker_socket
第二步:发动逃逸指令 (The Escape Command)
攻击者在容器内执行以下命令:
# 这是一个经典的逃逸命令
docker -H unix:///var/run/docker.sock run -v /:/host -it ubuntu bash
命令解析:
:告诉 Docker 客户端,不要连接默认位置,而是连接我当前挂载的这个 Socket(拿起手边的遥控器)。
-H unix:///var/run/docker.sock:启动一个新容器。
run (核心杀招):将宿主机的根目录
-v /:/host 挂载到新容器的
/ 目录下。
/host:启动并进入这个新容器的交互式 Shell。
-it ubuntu bash
第三步:进入新容器,控制宿主机
命令执行成功后,你其实已经“穿越”到了一个新的容器里。在这个新容器里,你执行:
ls /host
你会惊讶地发现,你看到了宿主机完整的文件系统!
是宿主机的用户表。
/host/etc/passwd 是宿主机的 SSH 密钥。
/host/root/.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中(现代Linux默认)
/etc/shadow或
*:账户被锁定,不能登录空:无需密码即可登录(极其危险!)加密字符串:老式系统直接存储加密密码
!3. 用户ID (UID – User ID)
:root用户,最高权限
0:系统预定义用户
1-99:系统服务和守护进程
100-999:普通用户
1000+:特殊用户
65534
nobody4. 组ID (GID – Group ID)
用户的主组ID
对应
文件
/etc/group5. 描述信息 (GECOS/comment field)
通常存储用户全名、电话、办公室等
可包含逗号分隔的多个字段
6. 家目录 (home directory)
用户登录后的初始目录
root:
/root普通用户:
/home/用户名7. 登录shell (login shell)
:正常的shell
/bin/bash
:禁止登录
/usr/sbin/nologin
:禁止登录且不执行任何操作
/bin/false
:特殊用途
/bin/sync
为什么/etc/passwd文件重要?
因为它包含了系统中所有用户的信息,尤其是UID。UID为0的用户是root,拥有最高权限。如果攻击者能够修改这个文件,他们可以创建一个UID为0的用户,从而获得root权限。
是 Linux/Unix 系统中最重要的系统文件之一,相当于Windows中的注册表或SAM数据库。
/etc/passwd
用户认证的核心:系统登录时,首先检查这个文件权限管理的基础:所有文件和进程的权限都基于这里的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 (仅编译/汇编成.o文件)、
-c (指定输出文件名)、
-o (生成调试信息)、
-g (显示所有警告)、
-Wall (优化)、
-O2 (生成汇编文件)、
-S (预处理),它们控制了编译流程和生成结果。
-E
1. 基本用法
编译并生成可执行文件:
gcc source.c -o executable_name
例:
gcc hello.c -o hello
2. 常用编译流程步骤及选项
预处理 (Preprocessing):编译 (Compilation):
gcc -E source.c -o source.i汇编 (Assembly):
gcc -S source.i -o source.s链接 (Linking):
gcc -c source.s -o source.o (链接多个.o文件)一步完成 (常用):
gcc source.o -o executable_name
gcc source.c -o executable_name
3. 重要选项 (Options)
:指定输出文件名称。
-o <name>:仅编译和汇编,生成目标文件(
-c),不链接。
.o:包含调试信息,供 GDB 等调试器使用。
-g:开启所有警告信息,有助于发现潜在问题。
-Wall:代码优化等级,如
-O<level>(中等优化)。
-O2:指定 C/C++ 标准,如
-std=<standard>。
-std=c99:生成共享库(动态库 .so),常配合
-shared。
-fPIC:生成位置无关代码,用于共享库。
-fPIC:指定库文件搜索路径。
-L<path>:链接指定库(例如
-l<libname> 链接数学库)。
-lm
4. C++ 编译
C++ 源文件通常用 后缀。使用
.cpp 命令编译更方便,它能自动链接 C++ 标准库。
g++
g++ main.cpp -o myprog