
Part2 技术研究过程
枚举可用的用户名
先来一张Exchange 2019 的登录界面图,费了好大劲安装了一个Exchange2019虚拟机,非常麻烦。

liming:Abc123!@#liming@rainy.com.cn:Abc123!@#rainy\liming:Abc123!@#rainy.com.cn\liming:Abc123!@#

在枚举可用的Exchange邮件服务器用户名方面,工具换了好几个,最后还是觉得这个准确率高一些,挂上字典就可以将可用的用户名枚举出来。

爆破工具的选择
查阅了各种文章、翻遍了github,发现爆破Exchange邮件服务器账号密码的工具很多,好用的却没有几个。对比了 ExchangeUserBrute、MailSniper.ps1、ruler-win64、伊朗的Jason、EBurst-master等等工具,最终发现还是EBurst最好用,它支持的Exchange的爆破接口多,而且python代码开源,易于改造。
如下图所示,使用-C命令可以探测出可用于爆破的接口,/autodiscover、/Microsoft-Server-ActiveSync、/rpc、/api、/oab、/ews、/mapi、/powershell 这8个接口可以用来爆破。在实战过程中,如果某一接口被防守人员限制了,可以更换其他接口继续爆破。
python2 EBurst-dial.py -d 192.168.237.188 -C

工具Eburst的初步改造
1 SSL证书校验问题
接下来对 EBurst 工具进行了优化,解决了 HTTPS 证书校验导致的连接失败问题,兼容自签名证书和证书不匹配场景,同时屏蔽因 verify=False 产生的 SSL 警告,保持终端输出更加简洁。

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)check_NTLM_userpass │ requests.get(...) │ requests.get(..., verify=False)check_Basic_userpass │ request.get(url, headers=...) │ request.get(url, headers=..., verify=False)check_HTTP_userpass │ request.post(url, ...) │ request.post(url, ..., verify=False)check_interfac_availab │ request.get(url, ...) │ request.get(url, ..., verify=False)check_url_authenticate │ request.get(url, ...) │ request.get(url, ..., verify=False)


3 中文编码问题
强制 UTF-8 编码,解决 Python 2 中文输出/打印时的 UnicodeDecodeError 问题。
reload(sys)sys.setdefaultencoding('utf-8')
通过拨号换IP绕过封禁
这里推测防守方采取了较为严格的防护策略,例如 IP 封禁、账号锁定等。为了尽量降低对业务系统的影响,将 Exchange 邮服密码枚举安排在夜间进行。
针对 IP 封禁问题,我没有选择接入代理池,而是采用了拨号换 IP 的方式。为此,我花费几十元购买了一台支持 PPPoE 拨号的 VPS,通过定时断开并重新拨号,在爆破过程中不断获取新的公网 IP。随后结合 Python 代码,对原有的 EBurst 工具进行了改造,实现了自动拨号动态换 IP 功能,规避目标基于 IP 的频率限制/封禁。
# 新增命令行参数parser.add_option("-a", "--adsl", dest="dialup_name", help=u"宽带连接名称")parser.add_option("--dialup-user", dest="dialup_user", help=u"宽带拨号用户名")parser.add_option("--dialup-pass", dest="dialup_pass", help=u"宽带拨号密码")# 拨号测试,每爆破85次,尝试换一个ipdef __init__(self, ..., dialup_name=None, dialup_user=None, dialup_pass=None):self.dialup_name = dialup_name if dialup_name else "宽带连接"self.dialup_user = dialup_user if dialup_user else "057982394898"self.dialup_pass = dialup_pass if dialup_pass else "072348"self.redial_interval = 85 # 每85次尝试重新拨号self.scan_counter = 0 # 扫描计数器self.dialing_lock = threading.Lock() # 拨号锁self.is_dialing = False # 是否正在拨号
该方案通过调用 Windows 系统命令 rasdial 和 rasphone 控制 PPPoE 拨号连接。当扫描达到设定阈值(如 85 次)时,程序会自动暂停所有扫描线程,记录当前 IP,断开拨号并等待一段时间后重新拨号,再获取新的公网 IP 并验证是否切换成功;若 IP 未发生变化,则继续执行断开、重拨和验证流程,直至成功获取新的 IP,最后恢复扫描任务。借助这一机制,可以在爆破过程中实现动态更换 IP,有效规避目标基于 IP 的访问频率限制或封禁策略。关键代码如下:
扫描达到 85 次→ 暂停所有扫描线程 (is_dialing=True)、→ 记录旧 IP→ 断开拨号 (3次重试 + 紧急兜底)→ 等待 10 秒→ 重新拨号 (3次重试)→ 等待 30 秒网络稳定→ 获取新 IP,验证是否变化→ 若 IP 未变:再次断开→拨号→验证→ 恢复扫描 (is_dialing=False)disconnect_dialup() │ 断开宽带连接,3 次重试 + 紧急强制断开兜底connect_dialup() │ 重新拨号连接,3 次重试show_connection_status() │ 调用 rasdial 显示当前连接状态check_connection_status() │ 检查连接是否处于活动状态get_public_ip() │ 通过 3 个外部服务获取当前公网 IPredial_connection() │ 完整的换 IP 流程:断开 → 等 10s → 拨号 → 等 30s → 验证 IP 是否变化 → 若未变则二次拨号 │
通过延迟等待绕过封禁
后续,钓鱼组成功上线了一枚鱼,通过查看域环境的账号锁定策略,发现目标用户名经过5次密码错误就锁定5分钟。于是对程序进行了调整,不再采用拨号换IP的方式,而是增加了延迟机制,在每轮尝试后加入 5 分钟的等待时间,以规避锁定策略,这样相对于拨号换IP,爆破速度大大提升。
python2 EBurst.py -U extuserpass.txt -d 192.168.237.188 --ews

最终,整个爆破过程持续了约 5 天,累计尝试几百万次,成功仅仅获取了一组有效的 Exchange 邮箱账号密码,为后续测试提供了关键突破口。
Part3 总结
1. 在AI时代,python代码修改起来相对容易。
2. 修正后的Eburst分享在知识星球里。
