场景说明
账号密码是信息安全的第一道屏障, 在攻防中, 弱密码拥有与高危漏洞同样的重视程度, 除了传统协议(ftp, mysql, ssh等) 的弱密码检测外, http(s) 层面的关键系统越来越多, 例如VPN系统、 SDP系统等,如果能快速对http(s)业务系统的弱密码进行探测, 相当于多了一条渗透路径.
但是http(s) 层面有验证码、密码加密等灵活的防御机制, 且经常配置错误次数限制,一个账号短时间错误次数太多, 直接禁止登陆. 这种情况下, 万能的burpsuite也显得有些无力.
Webpass工具通过驱动浏览器,模拟真实登录场景,形成通用的弱密码检测系统,可以支持http(s) 协议的web登录弱密码检测,也支持针对不同的网络设备定制不同的字典,从而更容易检测到弱密码;
有哪些解决的问题
密码加密传输
填写表单后, 页面代码对填写内容进行加密, 加密后发送到服务器后台, 如图看起来像base64实际上不是, 那么就因为不知道他怎么加密的, 从而无法直接发包爆破.
这种情况可以驱动浏览器来进行爆破, 让系统自己的代码进行加密转化, 最为简单直接通用.
async def create_browser_loop(sema = None, ap = None):
try:
process = get_value('process', 8)
glob.ocr = ddddocr.DdddOcr(old=True, show_ad=False)
# 创建指定数量的浏览器页面
browser = await ap.chromium.launch(headless=not get_value('debug', False), timeout=10 * 1000)
context = await browser.new_context(ignore_https_errors=True)
for _ in range(process):
page = await context.new_page()
await glob.pQu.put(page)
logger.debug(f"浏览器已创建页面总数:{glob.pQu.qsize()}")
except KeyboardInterrupt: kill_myself(1)
except Exception:
err_msg = get_err_msg()
logger.error(err_msg)
return err_msg
上述代码会打开指定数量的页面, 自动填写配置好的用户名密码, 效果图如下:
验证码和Token验证
验证码是很常见的, 当然单纯的验证码, 发包模式也可以解决. 但是有的时候, 页面会带有隐藏表单, 进行token提交, 服务器会对token进行验证.
对于token验证不符合的请求, 直接丢弃, 这个类似于密码加密传输, 不知道生成算法, 直接发包也扑街.
错误次数限制
这种情况也很常见, 当然发包的方式也可以解决这个问题, 本程序主要是密码优先, 枚举用户.
username_list = await aread_file(glob.cfg.pass_dict[key]['user_info'][1])
password_list = await aread_file(glob.cfg.pass_dict[key]['pass_info'][1])
user_pass = [[username_list[i], password_list[i]] for i in range(min(len(username_list), len(password_list)))] # 先写一一对应的
for _pass in password_list: # 再添加剩余的, 密码枚举用户
for _user in username_list:
if [_user, _pass] not in user_pass:
user_pass.append([_user, _pass])
单个目标枚举3次之后, 测试下一个目标, 这个目标切换为等待状态:
另外, webpass最初想法是爆破一些常见的网络设备, 这样常见网络设备的默认密码自然是最高优先级, 比通用弱密码更优先, 上述代码也有体现. 在配置中, 可以配置每类产品独立的字典:
代码配置如上图, 字典文件如下图:
用户名密码表单名灵活多变
Web页面的用户名密码表单, 一般习惯是写username, password但是也有很多情况不是这两个名称, 那么在程序填写用户名密码时, 往往也需要配置或者智能识别, 目前考虑的方案是:
- 列举出所有的input, 类型为password的肯定是password
- 计算表单name与user关键字的相似度, 超过一定值考虑为username字段
- 计算表单name与pass关键字的相似度, 超过一定值考虑为password字段
- 补充一些用户配置字典.
多目标批量
单个web目标的爆破, burpsuite很灵活有效, 但是多个目标, 就比较麻烦了. 新出的国产挑战者Yakit可以支持, 但是在请求次数过多也会程序卡死
实测最新版本8k个请求也会让UI卡死, 可能需要继续等待官方修复.
自己来写程序实现驱动浏览器进行弱密码爆破, 就能解决这个问题:
可以在文件中一行一个目标, 自动识别系统, 调用指定的字典, 未识别的使用通用字典
使用方法
INFO: Showing help with the command 'webpass_call.py -- --help'.
NAME
webpass_call.py - -u: url -f: file -g: 配置模式 -w: 单目标等待时间, 分钟 -j: 只探测识别到的目标
SYNOPSIS
webpass_call.py <flags>
DESCRIPTION
-u: url -f: file -g: 配置模式 -w: 单目标等待时间, 分钟 -j: 只探测识别到的目标
FLAGS
--u=U
Default: ''
--f=F
Default: ''
--debug=DEBUG
Default: False
--gen=GEN
Default: False
--wait=WAIT
Default: 3
--process=PROCESS
Default: 8
--just_discern=JUST_DISCERN
Default: True
python webpass_call.py -u http://1.2.3.4
python webpass_call.py -f /etc/target.txt python webpass_call.py -u http://1.2.3.4 -f /etc/target.txt
python webpass_call.py -g http://baidu.com
基本用法
-u : 探测单个或者多个目标弱密码, 含有url的字符串即可, 会自动解析url
-f : 探测文件中所有的url, 带有url的文件即可, 会自动解析url
上述两个开关可以都配置, 目标相加
注意: url需要带入协议类型如http, https等, 如果不带, 可能导致url无法识别.
配置模式
当然, 在实际使用中, 默认密码的情况比较多见, 所以尽量还是经过配置后使用, 而不是直接通用字典.
-g : 配置模式, 默认以webkit浏览器打开
比较方便于生成配置.
其他开关
-w : 配置单目标等待时长, 默认单目标尝试3次后等待3分钟
-p : 浏览器最大页面数, 即理解成多少个线程, 实际上是协程的程序
-j : 配置True则只爆破已识别的目标, 不进行通用爆破, 是just_discern的首字母
-d : 调试开关, 配置为True, 多很多步骤打印, 浏览器也会显示出来
配置文件说明
配置文件基本结构如上图, 是一个大字典, 每类产品一个小字典, 字典内部是key:list结构.
- keywords:列表类型,识别产品的关键词,各个关键词之间与关系,即登录页html中某些关键词,支持base64编码;留空则直接用“key”作为关键词判断,如果没有找到则归为未识别
- user_info:是包含username 字段名和字典目录的列表,pass_info类似
- button:按钮名称,列表类型,一般用webpass_call.py -g url 获取按钮名称
- CAPTCHA: 长度一般为2,位置0位填充,位置1为截图,如果长度为0,则代表没有验证码
- suc:登录成功后html中内容关键字,支持base64,根据logic计算整体真假
- fail:登录失败后html中内容关键字,支持base64,根据logic计算整体真假
考虑到配置的便捷性,可以使用-g参数激活浏览器自动获取关键字段,便于填写, 如果suc, fail都为空, 则直接根据登陆前后页面相似度进行正确性判断
安装
STEP1. 官方网站下载Python3.7+环境。
STEP2. 安装调用库PlayWright,Fire,loguru 等
- pip install playwright ddddocr fire loguru chardet aiofiles
- python -m playwright install
可能有一些未列举的, 后续提供requirements.txt
装完库就可以运行.
代码结构
代码主要使用Python3, 尽量使用Python3.9及以上的版本
文件结构
- 0-errlog.txt # 报错信息
- 1-unknown_devices.txt # 未知设备
- 2-success.txt # 成功的目标
- 3-fail.txt # 失败的目标
- 4-check.txt # 需要检查的目标
- webpass_call.py # 主程序
- webpass_config.py # 主配置
- webpass_func.py # 功能库
- webpass_report.py # 上报ES, 未完成
整体结构
target_dict数据结构
{
'target_dict':{
'http://1.2.2.2':{
'status':'init',
'title':'',
'content':'',
'key':'下一代防火墙',
'last_time':datetime 对象,
'user_pass':aQueue,
}
}
}
status状态
- init
- identify, unknown, timeout
- tasking, wait
- done, success
后续计划
计划
已实现
- 文档上述内容, 除了计划之外, 均已实现