很多人第一次进内网综合靶场前三关都过得挺顺扫端口、找Web入口、打个命令执行屏幕上回显一串uidxxx就觉得大功告成。可真到了这个以“域环境横向移动”为设计核心的靶场第五关卡住的往往不是技术而是思路——你已经站在一台机器上了接下来该干什么、凭什么能去下一台、拿到什么才算数脑子里一片空白。这篇文章就把这条链路从头捋一遍Web 层那个 PHP 框架 5.0 分支的远程命令执行漏洞成因到底是什么为什么打到命令执行之后几乎没人用正向连接而都要绕一圈做反向会话进了内网之后域环境里的“横向”究竟在搬动什么东西以及练完之后怎么把一次演练沉淀成能复用的方法论。写给谁看刚接触内网渗透、想搞明白“原理而不是只抄命令”的朋友以及做防守、想知道自己这边该在哪一环下手的同行。1. 靶场第五关的设计意图它为什么非要你离开 Web 服务器1.1 从“单点突破”到“多主机信任链”的难度跃迁单机靶场和内网靶场完全是两种游戏。单机靶场的评分标准很直白拿到某个文件、读到某个 flag一次成功的利用就结束了。而内网靶场从设计上就刻意把“终点”藏在你第一台机器看不到的地方逼着你承认一个事实——第一台机器只是跳板不是目标。这背后的逻辑其实很贴近真实场景。现实中的业务系统暴露在外的往往只是边缘的一层 Web 服务它本身价值有限但它和内网其他主机之间存在信任关系同一套账号体系、同一个域、可互访的网段、共享的凭据缓存。攻击面从来不是“一台机器”而是“一组彼此信任的机器”。所以靶场这一关的评分点通常落在“你是否拿到了域内更高权限的凭据”、“你是否能访问到域控所在网段的主机”这类指标上而不是“你有没有 shell”。我个人的体会是进这类靶场之前先在纸上写三行字——我在哪、我要去哪、我凭什么过去。第一行是当前主机的网络位置和权限第二行是目标通常是域内某台高价值主机第三行是可能被复用的身份凭据或信任关系。这三行写不出来后面所有的命令都是瞎敲。很多人一进内网就开始无脑扫全网段扫了两小时发现自己在浪费时间就是因为第一行都没搞清楚。1.2 环境里那些“看起来没用”的信息其实全是伏笔靶场作者放东西是有讲究的。桌面上的一个文本文件、浏览器历史里的一条内网地址、某个配置文件里的连接字符串、甚至hosts文件里的一行映射单看都是垃圾信息串起来就是地图。我习惯在拿到第一台机器的操作权限后先做一轮“低噪声信息收集”顺序大致是这样收集目标典型位置你能得到什么网络位置网卡配置、路由表、DNS 配置当前机器在几个网段、DNS 指向哪里往往就是域控身份上下文当前用户、用户列表、本地管理员组你以什么身份在跑、有没有高权限账号信任线索hosts 文件、浏览器记录、配置文件内网主机名、域名、可能的业务系统地址凭据痕迹命令行历史、脚本文件、配置文件明文最直接的横向入场券已装工具目录列表、服务列表判断这台机器平时被谁用来干什么这张表不是让你照抄而是提醒一件事信息收集的价值不在于“收集了多少”而在于“哪个信息和哪个信息能对上”。比如你在 A 机器上看到 hosts 里有个域名映射在 B 机器的配置文件里又看到同一个域名加一组账号密码那这两条信息一交叉横向路径基本就出来了。单独看两条都是无关紧要的碎片。提示信息收集阶段最大的浪费是“重复收集已知信息”。每拿到一条新信息先问自己它和我已有的哪条信息能组合组合不出来就先记下来别急着深挖。1.3 重新定义练习目标不是“拿到会话”而是“拿到可持续访问的路径”这是我认为这一关最值得反复练的地方。初学者拿到第一个 shell 就兴奋然后把它当成终点老手拿到 shell 之后想的是“这个会话断了怎么办、我下次从哪进”。举个很实际的例子你通过 Web 漏洞拿到了一个低权限的会话这个会话可能随时因为服务重启、进程被清理而消失。如果你没有在拿到它的第一时间做权限维持的合规练习在靶场里就是想办法留一个自己能重新进来的入口那这个会话本质上是一次性的。靶场里无所谓真实项目里这叫“返工”。所以在这一关我给自己定的验收标准是三条能不能重新进入会话失效后可恢复、能不能横向到第二台凭据可用、能不能拿到域内高价值目标的可读证据。三条缺一条这次演练就算没做完。这个标准比“拿到 shell 了”苛刻得多但更接近真实工作的验收方式。2. 入口环节拆解那个 PHP 框架 5.0 分支的远程命令执行错在哪儿2.1 路由分发把“用户输入”变成了“程序调用”要理解这类漏洞得先理解现代 PHP 框架的基本工作方式。请求进来框架先解析 URL把它拆成“模块/控制器/方法”三段然后去实例化对应的类、调用对应的方法参数从请求里取。这套机制的设计初衷是让开发者写代码更省事——加个控制器文件URL 就自动能访问了。问题就出在这个“自动”上。当框架允许通过请求参数去影响“调用哪个类、调用哪个方法、传什么参数”时用户输入就不再是数据而变成了程序控制流的一部分。一旦某个环节没有对“类名”和“方法名”做白名单约束理论上就可以让框架去实例化任意一个已加载的类并调用它任意一个可访问的方法。这类漏洞的可怕之处在于它不需要你找到一个业务代码里的注入点它打的是框架本身。也就是说只要这个版本部署在公网上漏洞面和业务代码写得好不好完全无关。这也是为什么版本号本身就是最重要的情报之一——它直接决定了“这台机器值不值得花时间”。2.2 版本号为什么比端口更值钱我做信息收集时有个习惯先看“是什么”再看“开了什么”。端口扫描告诉你 80 端口开着但 80 端口开着的机器有几千万台而一个能确定到具体版本的特征直接把这台机器的可利用性从“未知”压缩到“已知”。版本识别的常见来源包括报错页面的固定文案、静态资源的路径特征、响应头里的框架标识、默认页面的版权信息、特定路径的响应码差异。在练这个靶场的时候我就是靠一个默认路由的响应特征把版本锁定在了 5.0 这个分支上。这里有个经验值得说不要迷信工具的输出。指纹识别工具给的是“可能性”你得手工验证。我见过太多次工具报了一个版本实际是另一回事结果按错误的版本去试白白耗掉一小时。验证方法很简单——找一个该版本特有、而其他版本没有的路径或响应特征能对上才算数。2.3 验证思路与最小化验证原则这里我不打算给具体的请求构造原因有两个一是这类公开漏洞的分析资料已经非常充分编号和细节随手可查二是真正值得练的不是“抄一个请求”而是“怎么用最小的动作确认漏洞存在”。最小化验证原则指的是第一次验证只做“无副作用的信息读取”绝不直接上高危动作。比如先确认能不能读到当前进程的用户身份或者能不能读到一个固定路径下的系统文件。读到说明命令执行成立读不到可能是权限限制、可能是函数被禁用、也可能是路径不对。为什么要这么谨慎因为靶场里你怎么打都行但真实场景中一个不谨慎的操作可能直接把服务打挂、把日志刷爆、把对方的监控触发。养成“先读后写、先轻后重”的习惯是这一行最基本的职业素养。注意命令执行成立之后第一件事不是执行一堆命令而是确认三件事——当前用户是谁、能不能写文件、能不能出网。这三个答案决定了你后面所有方案的选型。3. 一次性命令执行不够用反向会话的工程学3.1 正向连不上不是你配错了是拓扑决定的这是新手最容易卡的地方。拿到命令执行后第一反应往往是“在这台机器上开个监听端口然后我连过去”。结果发现连不上就开始怀疑人生。问题不在你在网络。绝大多数真实环境和这个靶场一样Web 服务器处在 DMZ 或某个受限网段它的入站规则只放行了 80/443 这类业务端口其他端口一律拒绝或者它根本没有公网可达的地址中间隔着 NAT。这种情况下你从外面主动连它方向上是“由外向内”被防火墙拦掉是必然的。反向会话解决的正是这个问题连接的发起方反过来。让目标机器主动往外连一个你能接收的地址这样流量方向就变成了“由内向外”而绝大多数环境的出站策略比入站策略宽松得多。这不是什么技巧就是网络拓扑决定的必然选择。3.2 出网方向、端口与传输方式的选型逻辑知道了要反向连接下来的问题是怎么选。我一般按这几个维度决策决策维度需要确认的事影响出网能力目标能不能访问外网能不能只访问特定网段决定接收端放在哪端口策略出站哪些端口是放行的决定监听端口选 80/443 还是高位端口协议限制是否只允许 HTTP/HTTPS 出站决定用原始 TCP 还是走应用层封装执行环境有没有常见的运行时可用决定用什么方式建立会话持久性需求是一次性验证还是要反复操作决定是否需要可重连的机制这张表的读取顺序是从上到下因为上层的答案会直接否掉下层的选项。比如目标只能 HTTPS 出站那你用原始 TCP 反向连基本没戏得考虑把通信封装在 HTTP 里。在靶场里练的时候我建议每种方式都亲手试一遍包括失败的那几种。失败的尝试带来的信息量往往比成功的更大——它会告诉你这个环境的具体限制在哪。3.3 会话的交互体验比“能不能连上”更影响效率连上了只是开始。真正折磨人的是交互体验命令没有回显、按退格键乱码、跑不了需要终端的程序、一执行长命令就超时断开。这些问题的根源是最初的会话往往不是一个真正的终端而只是一个“把命令字符串丢给系统执行、再把输出抓回来”的通道。它没有伪终端PTY所以所有依赖终端的能力都没有。解决的思路是分级提升第一级能执行单条命令、能看到输出。够用来做信息收集。第二级能进入交互式环境有基本提示符。够用来做文件操作。第三级有完整的伪终端支持作业控制、支持交互式程序。够用来做需要“进入某个程序内部”的操作。从第一级到第三级每一步都需要额外的技巧而这些技巧的实现方式取决于目标机器上有什么可用的运行时。我踩过最典型的坑是费半天劲拿到了第三级会话结果因为一次误操作比如在前台跑了一个不会退出的程序把它挂死了只能从头再来。教训就是在任何交互式会话里长任务一律放后台别赌它不会卡。3.4 从“能执行命令”到“能搬东西”命令执行和文件传输是两件事。很多时候你有命令执行但目标机器上没有你需要的工具你得把工具传上去。传输方式的选择取决于通道能力能出网就走下载不能出网就用命令执行本身做编码传输把文件内容编码后分段写进去实在不行就用系统自带的工具做中转。这里的关键判断是“目标上有没有现成的下载能力”——有些运行时的标准库就能发 HTTP 请求有些就得靠系统命令。我在这一步的固定动作是先测试目标能不能访问一个我自己可控的地址用一个无害的请求验证能访问就走下载不能访问就老老实实用编码方式分段写。前者快后者稳但慢别在后者上耗太久优先想别的路子。4. 域内横向真正被搬动的是“身份”不是工具4.1 先搞清楚自己站在哪张地图上进了内网第一件事不是扫是画图。我通常按三个层次来画网络层当前主机能直达哪些网段哪些网段被隔离DNS 指向哪里主机层网段内有哪些存活主机它们开放了什么哪些是服务器、哪些是工作站身份层这些主机属于哪个域当前凭据在这个域里是什么角色三层的顺序不能乱。跳过网络层直接扫全部网段是最常见的低效操作——你以为在“全面收集”其实大部分流量根本到不了只是在自己骗自己。画完图之后你才会发现一个真相内网里真正值得打的主机通常只有那么几台其余的都是噪音。减少无效目标比加快扫描速度重要一百倍。4.2 凭据的三层形态明文、哈希、票据这是我认为这一关最核心的知识点也是很多人练完还没真正搞懂的地方。在域环境里“身份”不是抽象概念它有具体的存在形式而且是分层的形态存在于哪里能用来做什么局限明文口令配置文件、脚本、内存中的凭据缓存直接登录、直接认证拿到即用但大多数场景下拿不到口令哈希本地安全数据库、内存中的凭据在支持哈希认证的协议上直接使用依赖目标协议是否接受哈希票据内存中的认证缓存直接以该身份访问服务有有效期且绑定特定服务理解这三层的意义在于你不需要“破解”什么你只需要“搬运”身份。这是内网横向和 Web 渗透最大的思维差异。Web 渗透的思路是“找漏洞、打进去”内网横向的思路是“找到一份可用的身份然后以这个身份去访问下一台”。所以横向的本质是凭据获取 → 凭据复用 → 权限提升 → 再获取凭据一个循环。每转一圈你手里的身份就高一级。这个循环转不动了横向就停了。我在练习时犯过一个典型错误拿到一份哈希之后第一反应是想办法去破解它。花了很多时间最后发现根本不需要——直接用它去做认证就通了。能用就用不能用再想别的别把时间浪费在“我要搞明白这个密码是什么”上。4.3 远程执行通道的几种载体与选择依据域内横向的“移动”两个字落到操作上就是“用某个身份在另一台机器上执行点什么”。可用的载体通常有这么几类系统管理协议设计上就是用来远程管理的认证方式成熟日志记录完整。远程服务创建通过管理接口在目标上创建一个服务来执行命令权限要求高动静大。远程计划任务利用任务调度机制执行同样需要较高权限。远程桌面拿到交互式桌面最直观但最慢且容易被察觉。远程过程调用利用系统 RPC 接口隐蔽性好但依赖具体的接口实现。选择依据不是“哪个最厉害”而是三个很朴素的判断当前身份有没有权限、目标上有没有对应的服务、这次操作会不会留下太明显的痕迹。举个具体例子如果当前身份是普通域用户那远程服务创建这类需要管理员权限的路径基本走不通得先想办法提权或者找到有权限的凭据。反过来如果当前身份已经是域管理员那用哪种载体都行此时应该优先选“日志最少、动作最轻”的那一种。4.4 横向过程中的失败信号与排查顺序横向失败很常见而且报错信息往往很模糊——“拒绝访问”三个字可能是十种不同的原因。我的排查顺序固定是这一套认证是否通过先把“身份对不对”和“目标通不通”分开验证。用同样的凭据去访问一个已知开放的服务能通说明凭据没问题问题在目标不能通说明凭据或网络有问题。网络是否可达从当前主机测目标的关键端口别用 ping很多环境禁 ICMP会误导你。权限是否足够确认你的身份在目标上是否属于管理员组。注意本地的管理员组和域的管理员组是两回事。服务是否可用目标上对应的管理服务是否开启、是否被策略限制。时间是否同步域环境里认证对时间偏差非常敏感偏差过大直接认证失败而且报错不会告诉你原因。第五条是我踩过的最深的坑。当时所有条件都对就是认证不过折腾了很久才发现目标主机和当前主机的时间差超出允许范围。这个坑的教训是进域环境第一件事看时间。5. 演练记录怎么写才有复用价值5.1 时间线、证据链与“可复现”练完就忘是这类靶场练习最大的浪费。我坚持写三类记录时间线几点几分做了什么、得到什么结果。不用详细一行一条。证据链每条结论对应的原始输出。比如“目标存在命令执行”这条结论后面必须附上当时的验证输出。可复现说明如果让我从零再做一遍步骤是什么哪些步骤是可以省略的第三条是最容易被忽略、也最有价值的。它逼着你去掉那些“当时试了很多次才碰巧成功”的操作把路径压缩成最短链路。写不出来说明你其实没真正掌握只是运气好。5.2 把“运气”从结论里剔除演练中总有那么几个瞬间是“我也不知道为什么就成了”。这种情况必须在记录里单独标记出来然后回头补课。我的做法是凡是标了“不确定”的步骤事后必须重新做一遍并且换一个环境变量比如换一种会话方式、换一个时间点、换一台机器再验证一次。两次都能成才算真的会了。这个习惯带来的收益很直接下次遇到类似环境你不用再从头摸索因为你知道哪一步是关键、哪一步可以跳过。6. 反过来看这条链路上每一环的防守抓手练攻击的意义一半在于攻一半在于知道怎么防。沿着这条链路倒推每一环都有明确的防守切入点。6.1 Web 层版本管理与输入约束框架层面最有效的措施其实很朴素盯住版本。这类公开漏洞一旦披露厂商会很快给出修复版本问题在于有多少部署没升级。做资产管理的同行应该都有体会最大的风险从来不是“未知漏洞”而是“已知漏洞没修”。代码层面核心原则是永远不要让用户输入决定程序控制流。凡是涉及“动态调用类、动态调用方法、动态拼接执行”的地方一律白名单约束。这一条写进代码规范能挡掉一大类问题。运行环境层面禁用不必要的危险函数、限制 Web 进程的系统权限、把 Web 目录设置为不可执行这三条是成本最低、收益最高的加固。6.2 主机层出站策略比入站策略更值得关注这一条是很多人忽略的。大家习惯性盯着“谁能进来”却很少管“谁能出去”。而本文讲的整条链路里最难防的一环恰恰是反向会话——因为它是从内往外发起的。有效的做法是默认拒绝所有出站只放行明确需要的目标。一台 Web 服务器需要主动连外网的情况其实很少把出站收紧到最小集合能直接掐死反向会话这条路。配套的措施还包括限制 Web 进程能调用的系统命令、监控异常的子进程创建比如 Web 服务进程突然起了个命令行解释器这本身就是一个强信号。6.3 身份层与网络层凭据管理和分段域环境里最值钱的资产不是主机是凭据。防守重点应该放在三处减少凭据在高风险主机上的留存能不用域管理员登录的机器就别用。高权限账号分级使用域管理员账号只用于域控日常运维用委派权限。监控异常的认证行为同一份凭据短时间内出现在多台不相关主机上这本身就是异常。网络层则是老生常谈但极其有效的分段。把 Web 区、办公区、服务器区、域控区隔开只放行必要的访问路径。分段做得好即使 Web 服务器被拿下横向的每一步都要重新突破一道边界成本和被发现的风险都会急剧上升。我个人在实际操作中的体会是这条链路上真正难补的不是技术是“默认宽松”的习惯。默认放行所有出站、默认所有机器用同一个高权限账号、默认内网互相信任——这三个“默认”才是让一条从 Web 到域控的完整链路得以成立的真正原因。练靶场的价值就在于你亲手走通一遍之后回头看自己负责的环境会突然发现那些“一直这样也没出事”的配置其实一直在等着出事。