
接手这类告警的时候我第一反应其实是完了平台上是不是被人种了东西。对方发来的安全扫描结果很整齐所有以eos8.3.1为基座的应用只要一启动就会向100.100.100.200这个IP的80端口发起请求。多个应用、同一IP、同一端口、启动即触发这种规律性在恶意回连里反而不常见。但扫到就是扫到该走排查流程一步都不能省。这篇文章把这整套排查思路整理出来包括怎么确认IP身份、怎么定位到具体进程和代码、在EOS这类国产中间件平台上最可能的原因是什么、以及最终怎么让请求消失。适合中间件运维、安全工程师以及所有部署过类似平台、被启动后外联告警吓到过的同学参考。1. 先看这个IP的身份100.100.100.200不是黑客C2的典型画像1.1 100.64.0.0/10地址段到底是什么接到告警不要急着下结论第一件事是确认目标IP的归属。100.100.100.200这个地址落在100.64.0.0/10网段内这个网段在RFC 6598里被定义为运营商级NAT共享地址空间常被简称为CGN段。它是给运营商在用户侧和公网之间做地址转换用的既不算是严格意义上的公网IP也不是常规企业内网里会主动分配的私网段。对安全扫描器来说它看到的是一台内网服务器向一个外部地址发起了连接所以报警很正常。但实际部署过中间件平台的人都知道很多软件的默认配置文件里就躺着这类看起来像外网的保留地址。原因不复杂厂商在做默认配置时需要一个看起来不会和内网网段冲突的占位地址而100.64.0.0/10这个段在企业内网中几乎不会使用非常适合作占位。1.2 恶意回连行为和平台启动请求的行为差异恶意回连的特征通常是加密流量、高频心跳、目标地址频繁变换或者域名解析的IP经常更新。而被扫描到的这个请求是每次应用启动时固定发生一次目标IP和端口完全不变频率和启动动作强相关行为上更接近初始化阶段某个组件在向指定地址获取东西。当然光靠行为相似度高不能排除风险但至少可以把平台普通启动动作和被植入后门这两个假设的优先级调整一下——先按配置或功能行为去查查无实据再往恶意方向走。1.3 多实例同时请求本身就是一条重要线索最值得注意的不是有一个请求而是以eos8.3.1为基座的所有应用都这样。这基本排除了单个应用被单独植入后门的可能把问题指向了平台公共层。公共层就三块平台分发的公共jar包、平台级别的配置文件、启动脚本或公共环境变量。后续排查就往这个方向走效率会高很多。2. 进程、线程、抓包把谁发起请求钉死在证据上2.1 先看连接状态锁定发起请求的进程在Linux服务器上最快的方式是直接查当前活动连接。我习惯先用ss因为netstat在高并发场景下容易卡顿ss快得多ss -tnp | grep 100.100.100.200如果没有输出说明请求是启动瞬间发起、结束后连接就断掉了那就得用抓包或者事后日志来定位。如果有输出能看到类似这样的内容ESTAB 0 0 192.168.1.10:54321 100.100.100.200:80 users:((java,pid12345,fd108))这里最关键的是PID。拿到PID之后确认它属于哪个应用实例然后进到该实例的部署目录找到对应的启动脚本和配置文件。注意一点EOS平台可能同时跑多个服务进程比如应用服务、调度服务、消息组件它们都属于同一个PID也可能分散在不同进程里。抓到的PID到底对应哪个应用用ps -fp 看下命令行参数和启动路径就能确认。2.2 Java进程里的线程级定位既然EOS是Java技术栈拿到PID后下一步就是把请求定位到具体代码位置。这一步的核心是抓线程栈jstack 12345 thread_dump_1.txt在dump文件里搜socketRead、SocketInputStream、HttpClient、URLConnection这些字样。如果每次都找不到建议在应用重启前连续抓2到3个dump间隔5秒左右提高命中率。进程线程栈比较深的时候还可以用Arthas直接trace。先启动Arthas并attach到目标进程然后重放启动动作或者等下一次请求触发在控制台里观察调用栈trace java.net.SocketOutputStream writetrace到的方法名和类名能直接告诉你是哪个组件在写数据比如类名是license、register、report、health之类的基本就能猜个七七八八了。2.3 tcpdump抓包让请求内容自己说话进程和线程锁定了方向抓包则是为了看请求内容和返回结果。启动应用前先开抓包把启动阶段几十秒的流量完整记录下来tcpdump -ni eth0 host 100.100.100.200 and port 80 -w eos_startup.pcap抓完用Wireshark打开或者直接在命令行里用tshark看关键字段tshark -r eos_startup.pcap -Y ip.addr100.100.100.200 -T fields -e http.request.uri -e http.host -e http.user_agent请求的URI是关键。常见的几种情况请求/license/validate、/activate之类路径多半是许可在线校验请求/regist、/heartbeat之类路径多半是某个组件的注册或心跳上报请求一个不存在的路径并且返回404可能是配置里写死了某个默认服务地址如果应用在启动后的几秒内就把请求发完并断开连接那大概率是查询型操作查一下许可有效性或组件注册信息就结束了这类请求被防火墙拦掉一般不影响功能如果连接一直保持不释放就要小心说明这个组件可能依赖这个远程服务做持续通信。3. 三个高概率元凶许可校验、组件默认配置、业务代码残留3.1 许可校验与在线激活机制这是我在各种国产中间件平台里见到最多的一类。商业中间件产品在启动时经常需要做许可校验校验方式分为在线和离线两种。部署在内网环境时厂商提供的默认配置里经常写着一个占位的校验服务器地址用于在线激活场景。如果实施过程中没有改成离线许可模式客户端启动后就会反复向默认地址发起验证请求。判断是不是这一类看请求路径和重试行为。请求路径里带license、auth、verify之类关键词的基本锁定了方向。重试行为也有特征如果目标不可达客户端通常会重试3到5次然后超时跳过表现为启动时间变长但最终应用还是能起来。遇到这类情况正确做法是向平台厂商申请离线许可文件把许可验证方式切换为离线模式而不是在防火墙里单方面把地址封掉。只封地址应用确实不请求了但启动时还是会等超时体验很差。3.2 平台内置组件和示例工程的默认配置第二个高概率来源是平台自带的示例工程或内置组件配置残留。很多中间件在安装包里会附带示例应用、示例注册中心、示例配置中心地址。实施人员部署时如果没清理干净应用启动时会连带启动这些示例组件组件里的注册中心地址就是厂商预设的默认值。这类问题有个很明显的特征安装包解压后在原始未修改的配置目录里就能搜到目标IP。如果你拿到的安装包是从官网或者原厂渠道下载的原版包直接解压一份在干净的目录里搜100.100.100.200搜到了就证明是产品自带行为不是现场被篡改。3.3 应用侧业务代码的硬编码如果平台公共层全部排查干净、没有搜到IP那就要怀疑是基座之上跑的业务代码在启动时发起了请求。常见场景包括项目组封装的基础工具类里写死了某个内部服务的地址引入的第三方SDK报表、短信、推送、监控探活类自带默认服务器地址Spring Bean初始化时做了远程资源配置拉取。这类请求通常只在特定几个应用里出现而不是所有应用都有。如果安全扫描报告显示所有EOS应用都有这个行为那业务代码硬编码的可能性相对较小但排查时仍然要把这一项列进去防止出现所有应用都集成了同一个公共依赖的情况。4. 坐实根因全库检索配置文件与启动日志的实操套路4.1 在文件系统里全量搜索IP地址判断到底是谁引用了这个IP最直接的办法就是把IP在部署目录里搜一遍。先搜明文配置grep -r 100.100.100.200 /app/eos_platform/ --include*.properties --include*.yml --include*.yaml --include*.xml --include*.conf --include*.txt 2/dev/null注意要把隐藏目录也带上很多配置放在/opt、/etc下以点开头的目录里。搜完文本文件再搜jar包这一步很多人会漏掉。jar包是zip格式直接用zipgrep搜find /app/eos_platform/ -name *.jar | xargs -I {} zipgrep -l 100.100.100.200 {} 2/dev/null搜出来的jar包列表可能有一长串不要被吓到重点看class文件的路径前缀把它对应到具体组件比如是org/apache/xx还是com/xxxx/platform这个路径就是组件的身份标识。有一种情况是IP被做了拆分或者编码明文搜不到。比如配置写成100.100.100.200其实是字符串拼接出来的或者用Base64/Hex编码存了。这种场景下用strings或者直接看class文件字节码更实际。如果确认是编码存储建议先看下是不是平台自带的加解密工具在起作用——商业中间件经常会有配置加密功能加密后的配置在文件里就是乱码需要平台管理端解密查看。4.2 从启动日志里找请求上下文有些平台把网络请求记录在debug日志里默认不打印需要显式打开。EOS这类基于Java的应用可以在启动脚本的JAVA_OPTS里加一个参数来打开HTTP和Socket的调试输出-Djavax.net.debugall这个参数对JSSE相关的HTTP调用有效能看到SSL握手细节和连接建立日志。如果用HttpClient发的请求那就在日志配置里把org.apache.http这个包名的日志级别调到DEBUG可以看到请求行、请求头、目标Host。日志里定位到连接发起时间点后再反查那个时间点前后其他日志通常能串出一条完整链路比如XX模块开始初始化-连接远程服务器-获取配置-失败-使用本地缓存。这条链路本身就是给安全团队的结论素材。4.3 一个请求是否符合预期的判断检查表给手头案例做裁决的时候我习惯按下面这个清单逐项核对请求发起时间是否为应用启动初始化阶段除启动外运行期间是否还有周期性请求请求被拦截后应用功能是否有实际的异常或报错该地址在平台官方文档、厂商知识库中是否有说明请求内容是否包含本机主机名、IP、应用标识等业务特征是否所有基座相同、版本相同的实例都有一致行为如果前五项的答案大多为是、第六项为是基本可以认为是产品设计行为或配置残留风险等级可以从高危降到观察。如果请求发生在业务高峰期、内容里带敏感字段、且只在某一台特定机器上出现那才是真正需要按应急响应流程去处理的情况。5. 处理与收尾让启动请求消失同时给安全团队一份可归档的结论5.1 案例一许可在线校验类问题排查确认是许可在线校验之后处理路径就是联系厂商做离线授权。正常情况下平台管理端会提供许可导入界面申请到离线许可文件后直接导入然后重启应用验证。验证方式很简单重启后观察是否还有到100.100.100.200的连接同时确认平台管理端显示许可状态为有效。如果平台确实只支持在线校验那就需要和厂商确认这个在线校验服务器是否可以部署在内网或者是否有IP白名单机制。把校验服务器地址换成内网部署的地址请求自然就从外联变成内联安全扫描也不会再告警。5.2 案例二配置文件残留类问题平台自带组件或者示例工程导致的IP残留处理方法是找到对应配置项把地址改成实际使用的内部服务地址或者直接停用相关组件。改完后用前面同样的grep命令重新搜索确认部署目录里已经搜不到这个IP。这里有个小坑修改配置文件后没有重启生效导致安全团队复测时请求仍在。所以改配置后的验证动作一定不能省先重启再用ss观察才能算闭环。5.3 兜底方案防火墙策略和监控白名单同步有些环境短期内联系不上厂商或者确认为无害请求但短时间内无法通过配置消除这时可以先在出方向上做策略收敛。比如只允许该服务器访问业务必须的网段对100.100.100.200这条单独加一条拒绝规则并记录规则编号。更稳妥的做法是在网络安全设备或主机入侵检测系统里把100.100.100.200:80加入观察清单持续记录一段时间。如果后续观察到请求频率出现异常增长或者内容发生变化再升级处理。这类兜底方案重点在于记录在案让安全团队知道这是已知行为、已有处理策略而不是放任不管。5.4 输出结论三句话讲清楚给安全团队所有处理完成后还需要给安全团队一个能归档的结论。我的习惯是控制在三句话该请求是平台哪个组件、在什么阶段发起的比如XX版本许可校验模块在应用启动时向默认许可服务器地址发起验证为什么目标地址是100.100.100.200比如厂商默认配置中保留了该占位地址未随部署环境调整已做的处置和验证结果比如已切换离线许可模式重启后确认不再发起该请求这三句话写清楚安全团队那边就可以关单后续如果再被扫描到也有据可查。6. 这次排查踩过的坑和给你的建议6.1 拿到告警先别急着封IP这是我反复提醒自己的第一件事。看到外联安全扫描的组合人的本能是立刻封掉目标IP。但很多中间件启动时要先做远程配置拉取或者许可校验你在防火墙把IP一封应用启动直接卡在超时严重的连服务都起不来。先花半小时做定位比事后救火省太多精力。6.2 多个应用同时告警优先查公共层单个应用出现异常连接怀疑面可以广一点所有基于同一个平台的实例同时出现同样行为公共层是绝对的高优先级排查方向。公共jar包、公共配置、启动脚本按这个顺序走完大概率能命中。我在这个案例里最后定位到的问题就出在平台公共目录的一个配置文件中所有实例启动时读取的是同一份默认配置。6.3 排查过程中保留完整时间线从收到告警、拿到扫描报告、到定位完成每一步做了哪些操作、抓到了什么证据建议记录时间和操作内容。这类问题往往要跨团队协作安全团队、应用团队、平台厂商各执一词的时候时间线就是最有说服力的东西。而且事后归档时时间线可以直接用来写复盘报告不用重新回忆。6.4 扫描器的告警不等于安全事件最后想多说一句。安全扫描器的机制是发现了可疑行为就上报这是它职责所在。但作为处理告警的人我们要按证据链一步步收敛而不是被告警的严重等级带着跑。此次案例中100.100.100.200只是厂商默认配置里的占位地址请求行为在应用生命周期里既没有数据回传、也没有后续命令下发最终结论是无害的产品设计行为而非攻击事件。但如果没有完整的排查链路支撑这句话是不能轻易说出口的。