def check(self): # Server Administration r = Requester.get('{}/railo-context/admin/server.cfm'.format(self.url)) if r.status_code == 200 and 'type="password"' in r.text: self.interface = 'railo-server-admin' self.interface_url = '{}/railo-context/admin/server.cfm'.format( self.url) logger.info( 'Railo Server administration console detected: {}'.format( self.interface_url)) return True # Web Administration r = Requester.get('{}/railo-context/admin/web.cfm'.format(self.url)) if r.status_code == 200 and 'type="password"' in r.text: self.interface = 'railo-server-admin' self.interface_url = '{}/railo-context/admin/web.cfm'.format( self.url) logger.info('Railo Web administration console detected: {}'.format( self.interface_url)) return True logger.error('No Railo authentication interface detected') return False
def check(self): # Interface 1: admin-console r = Requester.get('{}/admin-console/login.seam'.format(self.url)) if r.status_code == 200: self.interface = 'admin-console' self.interface_url = '{}/admin-console/login.seam'.format(self.url) logger.info('Jboss authentication interface detected: {}'.format( self.interface_url)) return True # Interface 2: jmx-console auth_type = Requester.get_http_auth_type('{}/jmx-console/'.format( self.url)) if auth_type is not AuthMode.UNKNOWN: self.interface = 'jmx-console' self.interface_url = '{}/jmx-console/'.format(self.url) self.http_auth_type = auth_type logger.info('Jboss jmx-console interface detected: {}'.format( self.interface_url)) return True # Interface 3: web-console auth_type = Requester.get_http_auth_type('{}/web-console/'.format( self.url)) if auth_type is not AuthMode.UNKNOWN: self.interface = 'web-console' self.interface_url = '{}/web-console/'.format(self.url) self.http_auth_type = auth_type logger.info('Jboss web-console interface detected: {}'.format( self.interface_url)) return True # Interface 4: management auth_type = Requester.get_http_auth_type('{}/management/'.format( self.url)) if auth_type is not AuthMode.UNKNOWN: self.interface = 'management' self.interface_url = '{}/management/'.format(self.url) self.http_auth_type = auth_type logger.info('Jboss management interface detected: {}'.format( self.interface_url)) return True # Interface 5: management 2 r = Requester.get('{}/console'.format(self.url)) if r.status_code == 200: tmp = r.url[:r.url.rindex('/')] self.interface_url = '{0}/management'.format(tmp[:tmp.rindex('/')]) auth_type = Requester.get_http_auth_type(self.interface_url) if auth_type is not AuthMode.UNKNOWN: self.interface = 'management' self.http_auth_type = auth_type logger.info('Jboss management interface detected: {}'.format( self.interface_url)) return True logger.error('No Jboss authentication interface detected') return False
def check(self): r = Requester.get('{}/CFIDE/administrator/enter.cfm'.format(self.url)) if r.status_code == 200 and 'type="password"' in r.text.lower(): self.interface_url = '{}/CFIDE/administrator/enter.cfm'.format( self.url) # Version 6 if 'name="cfadminPassword"' in r.text \ and 'name="requestedURL"' in r.text \ and 'name="cfadminUserId"' not in r.text \ and 'name="salt"' not in r.text: self.interface = 'coldfusion-6-admin' logger.info( 'Coldfusion 6 administration console detected: {}'.format( self.interface_url)) return True # Versions 7/8/9 elif 'name="cfadminPassword"' in r.text \ and 'name="requestedURL"' in r.text \ and 'name="cfadminUserId"' in r.text \ and 'name="salt"' in r.text: self.interface = 'coldfusion-7-8-9-admin' logger.info( 'Coldfusion 7/8/9 administration console detected: {}'. format(self.interface_url)) return True # Versions 10/11 elif 'name="cfadminPassword"' in r.text \ and 'name="requestedURL"' in r.text \ and 'name="cfadminUserId"' in r.text \ and 'name="salt"' not in r.text: self.interface = 'coldfusion-10-11-admin' logger.info( 'Coldfusion 10/11 administration console detected: {}'. format(self.interface_url)) return True r = Requester.get('{}/CFIDE/administrator/index.cfm'.format(self.url)) if r.status_code == 200 and 'type="password"' in r.text.lower(): self.interface_url = '{}/CFIDE/administrator/index.cfm'.format( self.url) # Version 5 if 'name="PasswordProvided_required"' in r.text \ and 'name="PasswordProvided"' in r.text: self.interface = 'coldfusion-5-admin' logger.info( 'Coldfusion 5 administration console detected: {}'.format( self.interface_url)) return True logger.error('No Coldfusion authentication interface detected') return False
def try_auth(self, username, password): if self.interface == 'joomla-admin': r = Requester.get(self.interface_url) data = { 'username': username, 'passwd': password, #'lang': 'en-GB', 'option': self.option, 'task': 'login', self.token: '1', } r = Requester.post(self.interface_url, data, headers={ 'Cookie': self.cookie, }) if 'input name="passwd"' not in r.text: self.cookie = 'a=a' return True else: return False else: raise AuthException('No auth interface found during intialization')
def _get_salt(self, url): r = Requester.get(url) m = re.search( '<input name="salt" type="hidden" value="(?P<salt>\S+?)">', r.text) if not m: raise RequestException( 'Unable to retrieve salt from {}'.format(url)) else: return m.group('salt')
def try_auth(self, username, password): if self.interface == 'admin-console': # We need to retrieve ViewState value r = Requester.get(self.interface_url) m = re.search('<input type="hidden" name="javax\.faces\.ViewState" ' \ 'id="javax\.faces\.ViewState" value="(?P<viewstate>.*?)"', r.text) if not m: raise RequestException( 'Unable to retrieve ViewState from {}'.format( self.interface_url)) data = OrderedDict([ ("login_form", "login_form"), ("login_form:name", username), ("login_form:password", password), ("login_form:submit", "Login"), ("javax.faces.ViewState", m.group('viewstate')), ]) # We also need to retrieve JSESSIONID value m = re.search( r'JSESSIONID=(?P<jsessionid>.*); Path=\/admin-console', r.headers['Set-Cookie']) if not m: raise RequestException('Unable to retrieve JSESSIONID value ' \ 'from {}'.format(self.interface_url)) r = Requester.post(self.interface_url, data, headers={ 'Cookie': 'JSESSIONID={}'.format( m.group('jsessionid')) }, allow_redirects=False) status = ('name="login_form:password"' not in r.text \ and 'Not logged in' not in r.text) return status elif self.interface == 'jmx-console': r = Requester.http_auth(self.interface_url, self.http_auth_type, username, password) return (r.status_code != 401) elif self.interface == 'management': r = Requester.http_auth(self.interface_url, self.http_auth_type, username, password) return (r.status_code != 401) elif self.interface == 'web-console': r = Requester.http_auth(self.interface_url, self.http_auth_type, username, password) return (r.status_code != 401) else: raise AuthException( 'No auth interface found during initialization')
def check(self): r = Requester.get('{}/axis2/axis2-admin/login'.format(self.url)) if r.status_code == 200 and 'name="password"' in r.text: self.interface = 'axis2-admin' self.interface_url = '{}/axis2/axis2-admin/login'.format(self.url) logger.info('Axis2 administration console detected: {}'.format( self.interface_url)) return True logger.error('No Axis2 authentication interface detected') return False
def check(self): r = Requester.get('{}/ibm/console/logon.jsp'.format(self.url)) if 'name="j_password"' in r.text: self.interface = 'websphere-admin' self.interface_url = '{}/ibm/console/logon.jsp'.format(self.url) self.action_url = '{}/ibm/console/j_security_check'.format(self.url) logger.info('Websphere administration console detected: {}'.format( self.interface_url)) return True logger.error('No Websphere authentication interface detected') return False
def check(self): r = Requester.get('{}/console/j_security_check'.format(self.url)) if 'name="j_password"' in r.text: self.interface = 'weblogic-admin' self.interface_url = '{}/console/j_security_check'.format(self.url) logger.info('Weblogic administration console detected: {}'.format( self.interface_url)) logger.warning('Warning: By default, Weblogic has an account lockout ' \ 'feature (max 5 failures per 5 minutes, lockout duration of 30min)') return True logger.error('No Weblogic authentication interface detected') return False
def check(self): """ <form method="post" name="login" action="j_acegi_security_check" style="text-size:smaller"> <table><tr><td>User:</td><td><input type="text" name="j_username" id="j_username" autocorrect="off" autocapitalize="off" /> </td></tr><tr><td>Password:</td><td><input type="password" name="j_password" /></td></tr><tr><td align="right"> <input id="remember_me" type="checkbox" name="remember_me" /></td><td><label for="remember_me">Remember me on this computer</label> </td></tr></table><input name="from" type="hidden" value="/" /> <input name="Submit" type="submit" value="log in" class="submit-button primary" /><script> $('j_username').focus(); </script></form> """ r = Requester.get('{}/login'.format(self.url)) if r.status_code == 200 and 'name="j_password"' in r.text: self.interface = 'jenkins-admin' self.interface_url = '{}/login'.format(self.url) self.action_url = '{}/j_acegi_security_check'.format(self.url) logger.info('Jenkins administration console detected: {}'.format( self.interface_url)) return True logger.error('No Jenkins authentication interface detected') return False
def check(self): r = Requester.get('{}/administrator/index.php'.format(self.url)) #print(r.headers) if 'form action="/administrator/index.php"' in r.text: self.interface = 'joomla-admin' self.interface_url = '{}/administrator/index.php'.format(self.url) logger.info('Joomla administration page detected: {}'.format( self.interface_url)) # Extract session cookie try: self.cookie = r.headers['Set-Cookie'].split(';')[0] logger.info('Extracted session cookie: {}'.format(self.cookie)) except: logger.error('Unable to extract session cookie') return False # Extract token m = re.search('type="hidden" name="(.*)" value="1"', r.text) try: self.token = m.group(1) logger.info('Extracted token value: {}'.format(self.token)) except: logger.error('Unable to extract token from page !') return False # Extract option m = re.search('type="hidden" name="option" value="(.*)"', r.text) try: self.option = m.group(1) except: # Default option value self.option = 'com_login' return True logger.error('No Joomla administration interface detected') return False
def try_auth(self, username, password): # If anti-CSRF token might be present, reload the page before every attempt # and re-extract form fields if self.has_csrftoken: r = Requester.get(self.url) self.cookies = r.cookies soup = BeautifulSoup(r.text, 'html.parser') try: target_form = soup.find_all('form')[self.form_number] except: raise AuthException( 'Problem occured when reloading page. Maybe some WAF/Protection ' 'is blocking us ?') self.parameters = self.__extract_form_fields(target_form) if self.password_field not in self.parameters.keys() \ or (self.username_field and self.username_field not in self.parameters.keys()): raise AuthException( 'Problem occured when reloading page. Maybe some WAF/Protection ' 'is blocking us ?') # Send authentication request if self.username_field: self.parameters[self.username_field] = username self.parameters[self.password_field] = password if self.method == 'GET': r = Requester.get(self.action_url, params=self.parameters, cookies=self.cookies) else: r = Requester.post(self.action_url, data=self.parameters, cookies=self.cookies) if self.verbose: logger.info('Raw HTTP Request/Response:') data = dump.dump_all(r) print(data.decode('utf-8')) # Check authentication status # HTTP response code check if r.status_code >= 400: return False # Check if response page contains password field soup = BeautifulSoup(r.text, 'html.parser') input_password = soup.find('input', attrs={'name': self.password_field}) if input_password: return False # Heuristic check of failed attemps based on possible error messages if re.search( '(username\s+or\s+password|cannot\s+log\s*in|unauthorized' '|auth(entication)?\s+fail|(invalid|wrong)\s+(cred|user|login|mail|email|e-mail|pass)' '|error\s+during\s+(login|auth))', r.text, re.IGNORECASE): return False # Heuristic check of successful attempt based on page content if re.search('(log\s*out|log\s*off|deconn?e|disconn?ec)', r.text, re.IGNORECASE): return True # Heuristic check of account lockout based on possible error messages if re.search( '(too\s+many\s+(failed)?\s*(attempt|try|tri)|account\s+(lock|block))', r.text, re.IGNORECASE): return False # Heuristic check based on source code difference with original page s = difflib.SequenceMatcher(None, self.page_html, r.text) return (s.quick_ratio() < 0.60)
def check(self): r = Requester.get(self.url) # Cookie potentially returned are kept to be sent in the auth request # because sometimes application might reject request if those cookies are not present self.cookies = r.cookies # Keep HTML source code of the page for diff calculation in heuristics checks # to determine if auth has failed/succeeded self.page_html = r.text soup = BeautifulSoup(r.text, 'html.parser') logger.warning('This module is based on heuristics and is prone to ' 'false negatives/positives') # Get all <form> on the page forms = soup.find_all('form') #print(forms) if not forms: logger.error('No standard web <form> found on the page') return False # Detect form with password field # 1. Check for standard <input type="password" ...> field # 2. Otherwise, check for <input type="text" ...> field with evocative name target_form = None is_input_password_type_text = False i = 0 for f in forms: input_password = f.find( 'input', type=lambda x: x and x.lower() == 'password', attrs={'name': True}) if not input_password: input_password = f.find( 'input', type=lambda x: x and x.lower() == 'text', attrs={ 'name': re.compile('.*(pass|pwd|pswd|pssw|pswrd).*', re.IGNORECASE) }) if input_password: is_input_password_type_text = True if input_password: target_form = f self.password_field = input_password.attrs['name'] self.form_number = i break i += 1 if target_form: logger.info( 'Standard web authentication <form> seems present on the page') logger.info('Detected password field name = {name}'.format( name=self.password_field)) else: logger.error( 'No standard web auth <form> with password field has been detected ' 'on the page') return False # Get action url (target) used when submitting form if target_form.has_attr('action'): form_action = target_form.attrs['action'] # Absolute URL if form_action.lower().startswith('http://') \ or form_action.lower().startswith('https://'): self.action_url = form_action # Relative path else: self.action_url = urljoin(self.url, form_action) else: self.action_url = r.url logger.info( 'Detected form action URL = {url}'.format(url=self.action_url)) # Get form method (default POST) try: self.method = target_form.attrs['method'].upper() except: self.method = 'POST' # Detect username field inputs_text = target_form.find_all('input', type=lambda x: x and x.lower() in ('text', 'email'), attrs={'name': True}) if is_input_password_type_text: try: inputs_text.remove( target_form.find('input', type=lambda x: x and x.lower() == 'text', attrs={'name': self.password_field})) except: pass if len(inputs_text) == 0: self.username_field = None elif len(inputs_text) == 1: # If only one input field type=text (except password field), take this one self.username_field = inputs_text[0].attrs['name'] else: # Take the one with the most explicit name if found, otherwise the first one self.username_field = self.__find_username_field_via_name( inputs_text) if not self.username_field: self.username_field = inputs_text[0].attrs['name'] # In rare case, username field can have no type if not self.username_field: inputs_no_type = target_form.find_all('input', type=False, attrs={'name': True}) self.username_field = self.__find_username_field_via_name( inputs_no_type) if self.username_field: logger.info('Detected username field name = {name}'.format( name=self.username_field)) else: logger.info( 'No username field detected, probably password-only authentication' ) # Heuristic check of anti-CSRF token self.has_csrftoken = target_form.find( 'input', type=lambda x: x and x.lower() == 'hidden') is not None if self.has_csrftoken: logger.info( 'Heuristic check determines form might have anti-CSRF token') # Get ordered list of all form parameters self.parameters = self.__extract_form_fields(target_form) return True
def run(self): # List supported modules if self.args.list: print('List of supported modules:') for mod in Utils.list_modules(): print('- {}'.format(mod)) return # Check if target is available logger.info( 'Check if target {url} is reachable...'.format(url=self.args.url)) try: r = Requester.get(self.args.url) except RequestException as e: logger.error('Target URL seems not reachable:') logger.error(e) sys.exit(0) logger.success('Connection to target OK. HTTP Status {}'.format( r.status_code)) # Handle potential <meta> refresh meta_refresh_url = Requester.get_meta_redirect_url( r.text, self.args.url) if meta_refresh_url: logger.info( 'Meta refresh mechanism has been detected. Following redirect...' ) self.args.url = meta_refresh_url try: r = Requester.get(self.args.url) except RequestException as e: logger.error('Redirected URL seems not reachable:') logger.error(e) sys.exit(0) logger.success( 'Connection to redirection OK. HTTP Status {}'.format( r.status_code)) # Create wordlist queue try: self.wordlist = Wordlist(self.args.username, self.args.userlist, self.args.password, self.args.passlist, self.args.combolist) logger.info('Number of creds that will be tested: {}'.format( self.wordlist.length)) except Exception as e: logger.error(e) sys.exit(0) # Initialize module try: mod = importlib.import_module('lib.modules.{}'.format( self.args.type.capitalize())) except Exception as e: logger.error('Error while importing module lib.modules.{}'.format( self.args.type.capitalize())) traceback.print_exc() return module = getattr(mod, self.args.type.capitalize())( self.args.url, verbose=self.args.verbose) # Detect authentication interface try: if not module.check(): return except RequestException as e: logger.warning(e) # Run bruteforce self.run_bruteforcer(module) self.output.newline('') logger.info('Bruteforce finished !') if self.args.verbose: if len(self.creds_found) > 0: logger.success('{} valid credentials found:'.format( len(self.creds_found))) for username, password in self.creds_found: logger.success('{}:{}'.format(username, password)) else: logger.error('No valid credentials found :\'(')