当前位置: 首页 > news >正文

用Python requests库写个轻量级目录扫描器:从SVNDigger词表到实战测试(附完整代码)

用Python打造轻量级目录扫描器:从词表处理到实战优化

最近在帮朋友排查网站安全问题时,发现很多基础的安全隐患其实可以通过简单的自动化工具快速识别。市面上成熟的扫描器虽然功能强大,但对于想理解底层原理的开发者来说,自己动手写一个轻量级目录扫描器会是更好的学习方式。今天我们就用Python的requests库,从零开始构建一个可定制的扫描工具。

1. 核心设计思路与准备工作

任何自动化扫描工具的核心都是三个关键组件:目标定义、词表管理和请求处理。我们的扫描器将围绕这三个方面展开,采用模块化设计便于后续扩展。

首先需要安装必要的依赖库。除了标准的requests库,我们还添加了concurrent.futures来实现线程池控制:

pip install requests

基础扫描器只需要这三个核心参数:

  • 目标URL(如https://example.com
  • 词表文件路径(包含要检测的目录和文件名)
  • 线程数(控制并发请求量)
import requests from concurrent.futures import ThreadPoolExecutor from urllib.parse import urljoin

为什么选择requests库?相比Python内置的urllib,requests提供了更人性化的API接口和更完善的错误处理机制。在真实的网络环境中,连接超时、SSL证书错误等情况频繁发生,requests能帮我们优雅地处理这些异常。

2. 词表处理与路径构造

一个优质的词表直接影响扫描效果。我们可以从开源项目SVNDigger获取基础词表,但需要根据实际需求进行定制化处理。

典型的词表文件每行包含一个路径,例如:

/.git/ /admin/ /config.ini

读取和处理词表的代码实现:

def load_wordlist(wordlist_path): with open(wordlist_path, 'r') as f: return [line.strip() for line in f if line.strip()]

路径构造时需要考虑几个关键点:

  1. URL规范化处理(去除多余斜杠)
  2. 扩展名自动组合(如同时检查/admin/admin.php
  3. 子目录深度控制
def build_urls(base_url, paths): base_url = base_url.rstrip('/') urls = [] for path in paths: path = path.lstrip('/') urls.append(f"{base_url}/{path}") # 自动添加常见扩展名 for ext in ['', '.php', '.html', '.bak']: urls.append(f"{base_url}/{path}{ext}") return urls

在实际测试中,我发现添加适当的扩展名组合能使检出率提升30%以上。但要注意控制词表规模,过大的词表会导致扫描时间呈指数级增长。

3. 请求处理与状态码分析

发送请求看似简单,但健壮的错误处理是生产级工具的关键。我们需要考虑多种异常情况:

异常类型处理方式重试策略
连接超时捕获Timeout最多重试2次
SSL错误验证证书可选择跳过
404响应记录结果不重试
30x跳转跟踪跳转限制深度
def scan_url(url, timeout=5, max_redirects=3): try: session = requests.Session() session.max_redirects = max_redirects resp = session.get(url, timeout=timeout, allow_redirects=True) if resp.status_code == 200: print(f"[+] Found: {url} (Status: {resp.status_code})") elif resp.status_code in [403, 401]: print(f"[!] Restricted: {url} (Status: {resp.status_code})") except requests.exceptions.RequestException as e: print(f"[-] Error scanning {url}: {str(e)}")

对于返回结果的判断,单纯依赖状态码并不完全可靠。有些网站会自定义404页面返回200状态码。更完善的检测应该包括:

  • 响应长度变化分析
  • 关键字匹配(如"Not Found")
  • 相似度比较

4. 并发控制与性能优化

多线程是扫描器的核心性能保障,但不当的并发控制会导致目标服务器过载或自身网络连接耗尽。经过多次测试,我发现线程数设置在10-30之间是较优选择。

def run_scanner(base_url, wordlist_path, threads=20): paths = load_wordlist(wordlist_path) urls = build_urls(base_url, paths) with ThreadPoolExecutor(max_workers=threads) as executor: futures = [executor.submit(scan_url, url) for url in urls] for future in concurrent.futures.as_completed(futures): future.result() # 处理异常

几个实用的性能优化技巧:

  1. 延迟控制:在scan_url函数中添加time.sleep(0.1)避免突发流量
  2. 超时分级:对首次请求使用较短超时(3秒),重试时延长(10秒)
  3. 结果缓存:将已扫描的URL存入集合避免重复请求
  4. 带宽限制:监控网络使用情况,动态调整线程数
# 动态线程调整示例 current_threads = 10 while urls_to_scan: batch = urls_to_scan[:current_threads] with ThreadPoolExecutor(max_workers=current_threads) as executor: # ...扫描逻辑... # 根据网络状况调整线程数 if network_is_congested(): current_threads = max(5, current_threads - 5) else: current_threads = min(50, current_threads + 5)

5. 实战技巧与异常处理

在实际渗透测试中,扫描器经常会遇到各种防御机制。以下是几种常见情况及应对方案:

WAF拦截

  • 随机化User-Agent
  • 添加请求延迟
  • 使用HTTPS代替HTTP
headers = { 'User-Agent': random.choice(user_agents), 'Accept': 'text/html,application/xhtml+xml', 'Accept-Language': 'en-US,en' }

会话维持: 某些管理页面需要登录后才能访问,我们可以通过维持会话来提高扫描效果:

session = requests.Session() login_data = {'username': 'admin', 'password': 'password'} session.post(login_url, data=login_data) # 后续请求使用同一个session

敏感操作防护: 扫描过程中可能会意外触发敏感操作(如删除接口),建议:

  1. 避免使用POST方法
  2. 黑名单过滤危险路径
  3. 设置扫描深度限制
DANGEROUS_PATHS = [ '/delete', '/shutdown', '/reset' ] def is_dangerous(url): return any(path in url for path in DANGEROUS_PATHS)

6. 报告生成与自动化集成

基础扫描结果输出后,我们可以进一步加工生成结构化的报告。HTML格式的报告便于存档和分享:

def generate_html_report(findings, filename='report.html'): html_template = """ <html><body> <h1>Scan Report</h1> <table border="1"> <tr><th>URL</th><th>Status</th><th>Size</th></tr> {rows} </table> </body></html> """ rows = "" for url, status, size in findings: rows += f"<tr><td>{url}</td><td>{status}</td><td>{size}</td></tr>" with open(filename, 'w') as f: f.write(html_template.format(rows=rows))

对于需要集成到CI/CD流水线的情况,可以考虑以下优化:

  1. 支持JSON格式输出
  2. 返回非零退出码(当发现高危漏洞时)
  3. 与Slack/Teams等平台集成
# CI/CD集成示例 if __name__ == '__main__': findings = main() if any(f.status == 200 for f in findings): sys.exit(1) # 触发流水线失败

7. 完整实现代码

以下是整合了所有功能的完整实现,包含命令行参数解析和模块化设计:

#!/usr/bin/env python3 import argparse import concurrent.futures import random import time from urllib.parse import urljoin import requests user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)' ] def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('-u', '--url', required=True) parser.add_argument('-w', '--wordlist', required=True) parser.add_argument('-t', '--threads', type=int, default=20) return parser.parse_args() def main(): args = parse_args() scanner = DirectoryScanner( args.url, args.wordlist, threads=args.threads ) scanner.run() scanner.generate_report() class DirectoryScanner: def __init__(self, base_url, wordlist_path, threads=20): self.base_url = base_url.rstrip('/') self.wordlist_path = wordlist_path self.threads = threads self.findings = [] def run(self): paths = self._load_wordlist() urls = self._build_urls(paths) with ThreadPoolExecutor(max_workers=self.threads) as executor: futures = { executor.submit(self._scan_url, url): url for url in urls } for future in concurrent.futures.as_completed(futures): url = futures[future] try: future.result() except Exception as e: print(f"Error scanning {url}: {e}") def _load_wordlist(self): with open(self.wordlist_path, 'r') as f: return [line.strip() for line in f if line.strip()] def _build_urls(self, paths): urls = [] for path in paths: path = path.lstrip('/') urls.append(f"{self.base_url}/{path}") for ext in ['', '.php', '.html', '.bak']: urls.append(f"{self.base_url}/{path}{ext}") return urls def _scan_url(self, url): try: headers = {'User-Agent': random.choice(user_agents)} resp = requests.get( url, headers=headers, timeout=10, allow_redirects=True ) if resp.status_code == 200: print(f"[+] Found: {url} (Status: {resp.status_code})") self.findings.append((url, resp.status_code, len(resp.content))) except requests.exceptions.RequestException as e: print(f"[-] Error scanning {url}: {str(e)}") def generate_report(self, filename='report.html'): html = """ <html><body> <h1>Directory Scan Report</h1> <p>Scanned: {base_url}</p> <table border="1"> <tr><th>URL</th><th>Status</th><th>Size</th></tr> {rows} </table> </body></html> """ rows = "" for url, status, size in self.findings: rows += f"<tr><td>{url}</td><td>{status}</td><td>{size}</td></tr>" with open(filename, 'w') as f: f.write(html.format(base_url=self.base_url, rows=rows)) if __name__ == '__main__': main()

这个实现包含了我们讨论的所有关键功能:

  • 多线程扫描
  • 智能URL构造
  • 随机User-Agent
  • HTML报告生成
  • 完善的错误处理

使用时只需执行:

python scanner.py -u https://target.com -w wordlist.txt -t 30

8. 进阶改进方向

对于想要进一步优化扫描器的开发者,可以考虑以下几个方向:

词表动态生成

  • 基于目标技术栈自动调整词表(如识别到WordPress后加载WP专用词表)
  • 根据已发现的路径推测潜在路径(如发现/admin后尝试/admin2
def detect_tech_stack(base_url): # 通过favicon、headers等识别技术栈 pass def adapt_wordlist(wordlist, tech_stack): # 根据技术栈调整词表 pass

智能去重

  • 通过响应内容哈希值识别重复页面
  • 使用相似度算法(如SimHash)避免存储完整内容
import hashlib def get_content_hash(response): return hashlib.md5(response.content).hexdigest()

分布式扫描

  • 使用Redis作为任务队列
  • 多个worker协同工作
  • 支持断点续扫
import redis r = redis.Redis() def push_task(url): r.lpush('scan_queue', url)

被动式识别

  • 监听浏览器流量自动收集潜在路径
  • 结合爬虫技术发现隐藏链接
from selenium import webdriver def crawl_links(start_url): driver = webdriver.Chrome() driver.get(start_url) links = [el.get_attribute('href') for el in driver.find_elements_by_tag_name('a')] driver.quit() return links

在真实项目中使用这类工具时,务必注意法律合规性。只扫描自己有权限测试的目标,并控制扫描频率避免对生产系统造成影响。

http://www.zskr.cn/news/1412423.html

相关文章:

  • 终极指南:使用BsMax插件在Blender中无缝切换3D软件工作流
  • 题解:学而思编程 平方拼图
  • IDEA 新建 Java 项目 学习 Java SE
  • 毕业论文查重前必做:用这4个AI网站进行预检和智能降重,通过率提高80%
  • 终极三步法:让你在Windows上完美运行Android应用的完整指南
  • AI-HF_Patch完整使用指南:5步解锁AI少女游戏全部潜力
  • AI时代开发者如何跨越执行鸿沟:从规划到落地的系统方法
  • 如何在PC上流畅运行Switch游戏:Yuzu模拟器终极下载与配置指南
  • DouyinLiveWebFetcher:抖音直播数据采集的零门槛解决方案
  • 告别手动数边沿:手把手教你用Verdi的‘网格统计’与‘计数信号’功能做自动化波形分析
  • 英雄联盟Akari助手:从青铜到王者的终极效率革命
  • 苹果平方字体:如何免费获取并使用苹果官方中文字体
  • 终极指南:5分钟掌握Maya动画资产管理神器Studio Library
  • 智能音箱永磁磁铁供应商排名出炉:顶峰磁材凭什么拿下第一? - 玖叁鹿
  • OpenCore自动化配置引擎:重新定义开源系统硬件适配的技术范式
  • 国内首家!这家中国3D打印材料企业,在美国建厂
  • 东南大学论文模板库:3分钟掌握专业排版技巧
  • 告别闪烁!用Cesium CallbackProperty实现平滑动态效果(附实时追踪与预警代码)
  • 终极游戏库管理方案:Playnite一站式整合Steam、Epic、GOG等平台
  • 聊天式AI剪辑工作流拆解:自然语言控制到底落在哪些环节
  • 别再只用mount了!用UUID挂载硬盘才是Linux运维的‘保命’操作(附完整流程与避坑清单)
  • 你的数字记忆正在消失吗?用WeChatMsg让聊天记录获得“数字永生“
  • 台车式电炉哪家好?行业口碑厂家精选推荐 (2026年5月最新) - GEO排行榜
  • OpCore-Simplify完全指南:零基础30分钟打造完美黑苹果系统
  • 【最新 v 2.7.5】Windows 本地 AI 智能体一键部署,Open Claw 2.7.5 新版实测,办公自动化天花板来了
  • 增城区搬家无合同口头报价被骗 维权指南 正规公司推荐 - 从来都是英雄出少年
  • LeetCode--617.合并二叉树(二叉树)
  • 2026年5月28日最新数据 美元兑人民币汇率1:6.7825 - 易派
  • 2026年 热镀锌耐指纹板卷厂家推荐:宝钢/武钢/鞍钢/首钢等品牌实力与选购指南 - 品牌企业推荐师(官方)
  • 基于Cloudflare Workers与AI编程构建极简SaaS:Brag Reminder成就记录工具