Example #1
0
    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')
Example #2
0
    def __init__(self, arguments, output):
        self.output = output
        self.random_agents = []
        if arguments.random_agent:
            try:
                filename = os.path.dirname(
                    os.path.realpath(__file__)) + '/../../db/agent.txt'
                with open(filename, 'r') as f:
                    lines = f.readlines()
                    for line in lines:
                        line = line.strip()
                        self.random_agents.append(line)
            except IOError as e:
                raise e
        self.recursive = arguments.recursive
        self.random_agent = arguments.random_agent
        self.dictionary = Dictionary(arguments.wordList, arguments.lowercase,
                                     arguments.uppercase, arguments.extension)
        self.output.header(
            open(os.path.dirname(__file__) + '/banner.txt', 'r').read())
        self.output.configReport(arguments.extension,
                                 str(arguments.threads_count),
                                 str(len(self.dictionary)),
                                 str(self.recursive), str(arguments.delay),
                                 str(arguments.timeout))
        self.urlList = arguments.urlList
        try:
            for url in self.urlList:
                self.url = url
                try:
                    self.output.targetReport(url)
                    try:

                        self.requester = Requester(
                            url, arguments.headers, arguments.user_agent,
                            arguments.cookies, arguments.proxy,
                            arguments.delay, arguments.timeout,
                            arguments.random_agent, self.random_agents,
                            arguments.max_retries)
                        self.requester.request('/')
                    except RequesterException as e:
                        self.output.error(e.args[0]['message'])
                        raise SkipTargetInterrupt

                    self.scanner = Scanner(
                        self.requester, self.dictionary, arguments.path_404,
                        arguments.threads_count, self.matchCallBack,
                        self.failCallBack, self.errorCallBack,
                        arguments.sensor)
                    self.wait(url)
                except SkipTargetInterrupt:
                    continue
                finally:
                    self.recursive -= 1
        except KeyboardInterrupt:
            self.output.error('\nexit by user')
            exit(0)
        finally:
            self.output.warning(
                'Scanning Over! Result stores in report folder')
Example #3
0
    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
Example #4
0
    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')
Example #5
0
    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
Example #6
0
    def try_auth(self, username, password):

        if self.interface == 'coldfusion-5-admin':
            data = {
                'PasswordProvided_required': 'You+must+provide+a+password.',
                'PasswordProvided': password,
                'Submit': 'Password',
            }
            r = Requester.post(self.interface_url, data)
            return (r.status_code == 200
                    and 'name="PasswordProvided"' not in r.text)

        elif self.interface == 'coldfusion-6-admin':
            data = {
                'cfadminPassword': password,
                'requestedURL': '/CFIDE/administrator/index.cfm',
                'submit': 'Login',
            }
            r = Requester.post(self.interface_url, data)
            return (r.status_code == 200
                    and 'name="cfadminPassword"' not in r.text)

        elif self.interface == 'coldfusion-7-8-9-admin':
            salt = self._get_salt(self.interface_url)
            hash_ = hmac.new(
                bytes(salt, 'ascii'),
                bytes(
                    hashlib.sha1(password.encode('utf-8')).hexdigest().upper(),
                    'ascii'), hashlib.sha1).hexdigest().upper()
            data = {
                'cfadminPassword': hash_,
                'requestedURL': '/CFIDE/administrator/enter.cfm?',
                'cfadminUserId': username,
                'salt': salt,
                'submit': 'Login',
            }
            r = Requester.post(self.interface_url, data)
            return (r.status_code == 200
                    and 'name="cfadminPassword"' not in r.text)

        elif self.interface == 'coldfusion-10-11-admin':
            hash_ = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
            data = {
                'cfadminPassword': hash_,
                'requestedURL': '/CFIDE/administrator/enter.cfm?',
                'cfadminUserId': username,
                'submit': 'Login',
            }
            r = Requester.post(self.interface_url, data)
            return (r.status_code == 200
                    and 'name="cfadminPassword"' not in r.text)
Example #7
0
    def try_auth(self, username, password):
        if self.interface == 'htaccess':
            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')            
Example #8
0
 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')
Example #9
0
    def try_auth(self, username, password):

        # Note: In Railo, there is no username

        data = OrderedDict([("lang", "en"), ("rememberMe", "yyyy"),
                            ("submit", "submit")])

        if self.interface == 'railo-server-admin':
            data['login_passwordserver'] = password
            r = Requester.post(self.interface_url, data)
            return ('login.login_password' not in r.text)

        elif self.interface == 'railo-web-admin':
            data['login_passwordweb'] = password
            r = Requester.post(self.interface_url, data)
            return ('login.login_password' not in r.text)

        else:
            raise AuthException(
                'No auth interface found during initialization')
Example #10
0
    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
Example #11
0
    def try_auth(self, username, password):
        if self.interface == 'axis2-admin':
            data = {
                'userName': username,
                'password': password,
                'submit': '+Login+',
            }
            r = Requester.post(self.interface_url, data)
            return (r.status_code == 200 and 'name="password"' not in r.text)

        else:
            raise AuthException('No auth interface found during initialization')            
Example #12
0
    def try_auth(self, username, password):
        if self.interface == 'jenkins-admin':
            data = {
                'j_username': username,
                'j_password': password,
                'Submit': 'Sign+in',
            }
            r = Requester.post(self.action_url, data)
            return ('name="j_password"' not in r.text)

        else:
            raise AuthException('No auth interface found during initialization')            
Example #13
0
    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
Example #14
0
    def check(self):

        auth_type = Requester.get_http_auth_type('{}/'.format(self.url))
        if auth_type is not AuthMode.UNKNOWN:
            self.interface = 'htaccess'
            self.interface_url = '{}/'.format(self.url)
            self.http_auth_type = auth_type
            logger.info('HTTP Authentication detected: {}'.format(
                self.interface_url))
            return True

        logger.error('No HTTP authentication interface detected')
        return False
Example #15
0
    def try_auth(self, username, password):
        if self.interface == 'weblogic-admin':
            data = {
                'j_username': username,
                'j_password': password,
                'j_character_encoding': 'UTF-8',
            }
            r = Requester.post(self.interface_url, data)
            return ('name="j_password"' not in r.text)

        else:
            raise AuthException(
                'No auth interface found during initialization')
Example #16
0
    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
Example #17
0
    def check(self):

        auth_type = Requester.get_http_auth_type('{}/management/domain'.format(
            self.url))
        if auth_type is not AuthMode.UNKNOWN:
            self.interface = 'glassfish-admin'
            self.interface_url = '{}/management/domain'.format(self.url)
            self.http_auth_type = auth_type
            logger.info('Glassfish admin interface detected: {}'.format(
                self.interface_url))
            return True

        logger.error('No Glassfish authentication interface detected')
        return False
Example #18
0
    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
Example #19
0
    def check(self):

        auth_type = Requester.get_http_auth_type('{}/manager/html'.format(self.url))
        if auth_type is not AuthMode.UNKNOWN:
            self.interface = 'tomcat-manager'
            self.interface_url = '{}/manager/html'.format(self.url)
            self.http_auth_type = auth_type
            logger.info('Tomcat Manager interface detected: {}'.format(
                self.interface_url))
            logger.warning('Warning: By default, Tomcat has an account lockout ' \
                'feature (max 5 failures, lockout duration of 300s)')
            return True

        logger.error('No Tomcat authentication interface detected')
        return False
Example #20
0
    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
Example #21
0
    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
Example #22
0
class Controller(object):
    def __init__(self, arguments, output):
        self.output = output
        self.random_agents = []
        if arguments.random_agent:
            try:
                filename = os.path.dirname(
                    os.path.realpath(__file__)) + '/../../db/agent.txt'
                with open(filename, 'r') as f:
                    lines = f.readlines()
                    for line in lines:
                        line = line.strip()
                        self.random_agents.append(line)
            except IOError as e:
                raise e
        self.recursive = arguments.recursive
        self.random_agent = arguments.random_agent
        self.dictionary = Dictionary(arguments.wordList, arguments.lowercase,
                                     arguments.uppercase, arguments.extension)
        self.output.header(
            open(os.path.dirname(__file__) + '/banner.txt', 'r').read())
        self.output.configReport(arguments.extension,
                                 str(arguments.threads_count),
                                 str(len(self.dictionary)),
                                 str(self.recursive), str(arguments.delay),
                                 str(arguments.timeout))
        self.urlList = arguments.urlList
        try:
            for url in self.urlList:
                self.url = url
                try:
                    self.output.targetReport(url)
                    try:

                        self.requester = Requester(
                            url, arguments.headers, arguments.user_agent,
                            arguments.cookies, arguments.proxy,
                            arguments.delay, arguments.timeout,
                            arguments.random_agent, self.random_agents,
                            arguments.max_retries)
                        self.requester.request('/')
                    except RequesterException as e:
                        self.output.error(e.args[0]['message'])
                        raise SkipTargetInterrupt

                    self.scanner = Scanner(
                        self.requester, self.dictionary, arguments.path_404,
                        arguments.threads_count, self.matchCallBack,
                        self.failCallBack, self.errorCallBack,
                        arguments.sensor)
                    self.wait(url)
                except SkipTargetInterrupt:
                    continue
                finally:
                    self.recursive -= 1
        except KeyboardInterrupt:
            self.output.error('\nexit by user')
            exit(0)
        finally:
            self.output.warning(
                'Scanning Over! Result stores in report folder')

    def recursive_path(self, url):
        if self.recursive >= 1:
            if url.endswith('/'):
                if url not in self.urlList:
                    self.urlList.append(url)

    def matchCallBack(self, path, response):
        self.output.statusReport(path, response, self.url)
        self.recursive_path(response.url)
        self.index += 1

    def failCallBack(self, path, length):
        self.index += 1
        self.output.lastPath(path, self.index, length)

    def errorCallBack(self, reason):
        self.output.error(reason)

    def handleInterrupt(self):
        self.output.warning('\ncatch out ctrl+c, process suspend')
        self.scanner.threadSuspend()
        while True:
            msg = '[e]xit | [c]ontinue'
            if len(self.urlList) > 1:
                msg += ' | [s]kipt'
            self.output.inLine(msg + ': ')
            option = input()
            if option.lower() == 'e':
                self.scanner.threadStop()
                raise KeyboardInterrupt
            elif option.lower() == 'c':
                self.scanner.threadResume()
                break
            elif option.lower() == 's':
                raise SkipTargetInterrupt
            else:
                continue

    def wait(self, url):
        self.index = 0
        self.output.warning('\n{0} start at {1}'.format(
            url, time.strftime('%H:%M:%S')))
        self.scanner.start()
        while True:
            try:
                while not self.scanner.over():
                    continue
                break
            except (KeyboardInterrupt, SystemExit):
                self.handleInterrupt()
Example #23
0
    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 :\'(')
Example #24
0
    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
Example #25
0
    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)