Example #1
0
def is_dynupdate_enabled(domain: str, nameserver: str) -> bool:
    """
    Check if zone updating is enabled.

    :param domain: Name of the zone to transfer.
    :param nameserver: IPv4 or 6 to test.
    """
    newrecord = 'newrecord'

    try:
        update = dns.update.Update(domain)
        update.add(newrecord, 3600, dns.rdatatype.A, '10.10.10.10')
        response = dns.query.tcp(update, nameserver, timeout=5)

        result = True

        if response.rcode() > 0:
            show_close('Zone update not enabled on server',
                       details=dict(domain=domain, nameserver=nameserver))
            result = False
        else:
            show_open('Zone update enabled on server',
                      details=dict(domain=domain, nameserver=nameserver))
            result = True
    except dns.query.BadResponse:
        show_close('Zone update not enabled on server',
                   details=dict(domain=domain, nameserver=nameserver))
        result = False
    except (socket.error, dns.exception.Timeout) as exc:
        show_unknown('Could not connect',
                     details=dict(domain=domain,
                                  nameserver=nameserver,
                                  error=str(exc).replace(':', ',')))
        result = False
    return result
Example #2
0
def has_generic_exceptions(rpg_dest: str, exclude: list = None) -> bool:
    """
    Search for on-error empty.

    See `REQ. 161
    <https://fluidattacks.com/web/rules/161/>`_.

    :param rpg_dest: Path to a RPG source or directory.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    tk_on = CaselessKeyword('on')
    tk_error = CaselessKeyword('error')
    tk_monitor = tk_on + Literal('-') + tk_error + Literal(';')

    result = False
    try:
        matches = lang.check_grammar(tk_monitor, rpg_dest, LANGUAGE_SPECS,
                                     exclude)
        if not matches:
            show_close('Code does not have empty monitors',
                       details=dict(code_dest=rpg_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=rpg_dest))
        return False
    else:
        result = True
        show_open('Code has empty monitors',
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #3
0
def has_unitialized_vars(rpg_dest: str, exclude: list = None) -> bool:
    """
    Search for unitialized variables.

    :param rpg_dest: Path to a RPG source or directory.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    tk_data = Keyword('D')
    tk_first = Word(alphas + "_", exact=1)
    tk_rest = Word(alphanums + "_")
    tk_vartype = Word(alphas, exact=1)
    tk_varlen = Word(nums) + Word(alphas, exact=1)
    tk_inz = CaselessKeyword('inz')
    tk_varname = tk_first + tk_rest

    unitialized = tk_data + tk_varname + Optional(tk_vartype) + \
        Optional(tk_varlen) + Optional(Word(nums)) + NotAny(tk_inz)

    result = False
    try:
        matches = lang.check_grammar(unitialized, rpg_dest, LANGUAGE_SPECS,
                                     exclude)
        if not matches:
            show_close('Code does not have unitialized variables',
                       details=dict(code_dest=rpg_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=rpg_dest))
        return False
    else:
        result = True
        show_open('Code has unitialized variables',
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #4
0
def uses_eval(js_dest: str, exclude: list = None) -> bool:
    """
    Search for ``eval()`` calls in a JavaScript file or directory.

    :param js_dest: Path to a JavaScript source file or directory.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    method = 'eval()'
    tk_method = CaselessKeyword('eval')
    call_function = tk_method + Suppress(nestedExpr())
    result = False
    try:
        matches = lang.check_grammar(call_function, js_dest, LANGUAGE_SPECS,
                                     exclude)
        if not matches:
            show_close('Code does not use {} method'.format(method),
                       details=dict(code_dest=js_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=js_dest))
        return False
    else:
        result = True
        show_open('Code uses {} method'.format(method),
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #5
0
def _ftp_do_auth(ip_address: str, username: str,
                 password: str, port: int = PORT) -> bool:
    """
    Perform FTP auth.

    :param ip_address: IP address to connect to.
    :param username: Username to check.
    :param password: Password to check.
    :param port: If necessary, specifiy port to connect to.
    """
    result = False
    try:
        with FTP() as ftp:
            ftp.connect(ip_address, port)
            ftp.login(username, password)
            ftp.makepasv()
            result = True
            show_open('FTP Authentication {}:{}'.format(ip_address, port),
                      details=dict(username=username, password=password))
    except error_perm:
        show_close('FTP Authentication {}:{}'.format(ip_address, port),
                   details=dict(username=username, password=password))
        result = False
    except OSError as exc:
        show_unknown('Could not connect',
                     details=dict(ip=ip_address, username=username,
                                  password=password,
                                  error=str(exc)))
        result = False
    return result
Example #6
0
def uses_localstorage(js_dest: str, exclude: list = None) -> bool:
    """
    Search for ``localStorage`` calls in a JavaScript source file or directory.

    :param js_dest: Path to a JavaScript source file or directory.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    method = 'window.localStorage'
    tk_object = CaselessKeyword('localstorage')
    tk_method = Word(alphanums)

    lsto = tk_object + Literal('.') + tk_method + Suppress(nestedExpr())

    result = False
    try:
        matches = lang.check_grammar(lsto, js_dest, LANGUAGE_SPECS, exclude)
        if not matches:
            show_close('Code does not use {} method'.format(method),
                       details=dict(code_dest=js_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=js_dest))
        return False
    else:
        result = True
        show_open('Code uses {} method'.format(method),
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #7
0
def swallows_exceptions(js_dest: str, exclude: list = None) -> bool:
    """
    Search for ``catch`` blocks that are empty or only have comments.

    See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_.

    See `CWE-391 <https://cwe.mitre.org/data/definitions/391.html>`_.

    :param js_dest: Path to a JavaScript source file or package.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    # Empty() grammar matches 'anything'
    # ~Empty() grammar matches 'not anything' or 'nothing'
    classic = Suppress(Keyword('catch')) + nestedExpr(opener='(', closer=')') \
        + nestedExpr(opener='{', closer='}', content=~Empty())

    modern = Suppress('.' + Keyword('catch')) + nestedExpr(
        opener='(', closer=')', content=~Empty())

    grammar = MatchFirst([classic, modern])
    grammar.ignore(cppStyleComment)

    try:
        matches = lang.path_contains_grammar(grammar, js_dest, LANGUAGE_SPECS,
                                             exclude)
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=js_dest))
    else:
        if matches:
            show_open('Code has empty "catch" blocks',
                      details=dict(matched=matches))
            return True
        show_close('Code does not have empty "catch" blocks',
                   details=dict(code_dest=js_dest))
    return False
Example #8
0
def root_without_mfa(key_id: str, secret: str) -> bool:
    """
    Check if root account does not have MFA.

    CIS 1.13: Ensure MFA is enabled for the root account (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    result = False
    try:
        summary = aws.get_account_summary(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    if summary['AccountMFAEnabled'] == 1:
        show_close('Root password has MFA enabled',
                   details=dict(account_summary=summary))
        result = False
    else:
        show_open('Root password has MFA disabled',
                  details=dict(account_summary=summary))
        result = True
    return result
Example #9
0
def policies_attached_to_users(key_id: str, secret: str) -> bool:
    """
    Check if there are policies attached to users.

    CIS 1.16: Ensure IAM policies are attached only to groups or
    roles (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    result = False
    try:
        users = aws.list_users(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    for user in users:
        user_pol = aws.list_attached_user_policies(key_id, secret,
                                                   user['UserName'])
        if user_pol:
            show_open('User has policies directly attached',
                      details=(dict(user=user['UserName'],
                                    user_policy=user_pol)))
            result = True
        else:
            show_close('User does not have policies attached',
                       details=(dict(user=user['UserName'])))
    return result
Example #10
0
def min_password_len_unsafe(key_id: str, secret: str, min_len=14) -> bool:
    """
    Check if password policy requires passwords greater than 14 chars.

    CIS 1.9: Ensure IAM password policy requires minimum length of 14 or
    greater (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    :param min_len: Mininum length required. Default 14
    """
    result = False
    try:
        policy = aws.get_account_password_policy(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    if policy['MinimumPasswordLength'] >= min_len:
        show_close('Password policy requires long passwords',
                   details=dict(min_length=min_len, policy=policy))
        result = False
    else:
        show_open('Password policy does not require long passwords',
                  details=dict(min_length=min_len, policy=policy))
        result = True
    return result
Example #11
0
def has_mfa_disabled(key_id: str, secret: str) -> bool:
    """
    Search users with password enabled and without MFA.

    CIS 1.2: Ensure multi-factor authentication (MFA) is enabled for all IAM
    users that have a console password (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    result = False
    try:
        users = aws.get_credentials_report(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    for user in users:
        if user[3] == 'true':
            if user[7] == 'false':
                show_open('User has password enabled without MFA',
                          details=dict(user=user[0]))
                result = True
            else:
                show_close('User has password enabled with MFA',
                           details=dict(user=user[0]))
        else:
            show_close('User does not have password enabled',
                       details=dict(user=user[0]))
    return result
Example #12
0
def not_requires_numbers(key_id: str, secret: str) -> bool:
    """
    Check if password policy requires numbers.

    CIS 1.8: Ensure IAM password policy require at least one number (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    result = False
    try:
        policy = aws.get_account_password_policy(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    if policy['RequireNumbers']:
        show_close('Password policy requires numbers',
                   details=dict(policy=policy))
        result = False
    else:
        show_open('Password policy does not require numbers',
                  details=dict(policy=policy))
        result = True
    return result
Example #13
0
def root_has_access_keys(key_id: str, secret: str) -> bool:
    """
    Check if root account has access keys.

    CIS 1.12: Ensure no root account access key exists (Scored)

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    result = False
    try:
        users = aws.get_credentials_report(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    root_user = next(users)
    if root_user[8] == 'true' or root_user[13] == 'true':
        show_open('Root user has access keys', details=dict(user=root_user))
        result = True
    else:
        show_close('Root user does not have access keys',
                   details=dict(user=root_user))
        result = False
    return result
Example #14
0
def not_pinned(file_dest: str, exclude: list = None) -> bool:
    """
    Check if the Dockerfile uses a ``FROM:...latest`` (unpinned) base image.

    :param file_dest: Path to the Dockerfile to be tested.
    :param exclude: Paths that contains any string from this list are ignored.
    :returns: True if unpinned (bad), False if pinned (good).
    """
    tk_from = Literal('FROM')
    tk_image = Word(alphas)
    tk_version = Literal('latest')

    pinned = tk_from + tk_image + Literal(':') + tk_version

    result = False
    try:
        matches = lang.check_grammar(pinned, file_dest,
                                     LANGUAGE_SPECS, exclude)
        if not matches:
            show_close('Dockerfile has pinned base image(s)',
                       details=dict(code_dest=file_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=file_dest))
        return False
    else:
        result = True
        show_open('Dockerfile uses unpinned base image(s)',
                  details=dict(file=matches,
                               total_vulns=len(matches)))
    return result
Example #15
0
def not_use_ssl(server: str,
                username: str,
                password: str,
                port: int = 3306) -> bool:
    """Check if MySQL server uses SSL."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = 'SHOW variables WHERE variable_name = "have_ssl"'
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        _result = list(mycursor)
        result = _result[0][1] == 'DISABLED'

        if result:
            show_open('Server don\'t use SSL', details=dict(server=server))
        else:
            show_close('Server uses SSL', details=dict(server=server))
        return result
Example #16
0
def is_anonymous_enabled(server: str, domain: str = 'WORKGROUP') -> bool:
    """
    Check if anonymous login is possible over SMB.

    :param server: The NetBIOS machine name of the remote server.
    :param domain: The network domain/workgroup. Defaults to 'WORKGROUP'
    """
    user = '******'
    password = ''
    try:
        with SMBConnection.SMBConnection(user,
                                         password,
                                         CLIENT_MACHINE_NAME,
                                         server,
                                         domain=domain,
                                         use_ntlm_v2=True,
                                         is_direct_tcp=True) as conn:
            ret = conn.connect(server, port=445)
            if not ret:
                show_close('Anonymous login not possible',
                           details=dict(domain=domain,
                                        user=user,
                                        server=server))
                return False
            show_open('Anonymous login enabled',
                      details=dict(domain=domain, user=user, server=server))
            return True
    except OSError as exc:
        show_unknown('There was an error connecting to SMB',
                     details=dict(server=server, domain=domain,
                                  error=str(exc)))
        return False
Example #17
0
def has_unencrypted_volumes(key_id: str, secret: str) -> bool:
    """
    Check if there are unencrypted volumes.

    :param key_id: AWS Key Id
    :param secret: AWS Key Secret
    """
    try:
        volumes = aws.list_volumes(key_id, secret)
    except aws.ConnError as exc:
        show_unknown('Could not connect',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    except aws.ClientErr as exc:
        show_unknown('Error retrieving info. Check credentials.',
                     details=dict(error=str(exc).replace(':', '')))
        return False
    if not volumes:
        show_close('Not volumes found')
        return False

    result = False

    for volume in volumes:
        if not volume['Encrypted']:
            show_open('Volume is not encrypted', details=dict(volume=volume))
            result = True
        else:
            show_close('Volume is encrypted', details=dict(volume=volume))
    return result
Example #18
0
def local_infile_enabled(server: str,
                         username: str,
                         password: str,
                         port: int = 3306) -> bool:
    """Check if 'local_infile' parameter is set to ON."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = "SHOW VARIABLES WHERE Variable_name = 'local_infile'"
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False
        result = ('local_infile', 'ON') in list(mycursor)

        if result:
            show_open('Parameter "local_infile" is ON on server',
                      details=dict(server=server))
        else:
            show_close('Parameter "local_infile" is OFF on server',
                       details=dict(server=server))
        return result
Example #19
0
def has_insecure_randoms(js_dest: str, exclude: list = None) -> bool:
    r"""
    Check if code uses ``Math.Random()``\ .

    See `REQ.224 <https://fluidattacks.com/web/rules/224/>`_.

    :param js_dest: Path to a JavaScript source file or package.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    method = 'Math.random()'
    tk_class = CaselessKeyword('math')
    tk_method = CaselessKeyword('random')
    tk_params = nestedExpr()
    call_function = tk_class + Literal('.') + tk_method + Suppress(tk_params)

    result = False
    try:
        matches = lang.check_grammar(call_function, js_dest, LANGUAGE_SPECS,
                                     exclude)
        if not matches:
            show_close('Code does not use {} method'.format(method),
                       details=dict(code_dest=js_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=js_dest))
        return False
    else:
        result = True
        show_open('Code uses {} method'.format(method),
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #20
0
def symlinks_enabled(server: str,
                     username: str,
                     password: str,
                     port: str = 3306) -> bool:
    """Check if symbolic links are enabled on MySQL server."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = "SHOW variables LIKE 'have_symlink'"
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        result = ('have_symlink', 'DISABLED') not in list(mycursor)

        if result:
            show_open('Symbolic links are supported by server',
                      details=dict(server=server))
        else:
            show_close('Symbolic links are not supported by server',
                       details=dict(server=server))
        return result
Example #21
0
def has_switch_without_default(js_dest: str, exclude: list = None) -> bool:
    r"""
    Check if all ``switch``\ es have a ``default`` clause.

    See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_.

    See `CWE-478 <https://cwe.mitre.org/data/definitions/478.html>`_.

    :param js_dest: Path to a JavaScript source file or package.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    switch = Keyword('switch') + nestedExpr(opener='(', closer=')')
    switch_block = Suppress(switch) + nestedExpr(opener='{', closer='}')
    switch_block.ignore(cppStyleComment)
    switch_block.ignore(L_CHAR)
    switch_block.ignore(L_STRING)
    switch_block.addCondition(lambda x: not RE_HAVES_DEFAULT.search(str(x)))
    try:
        matches = lang.path_contains_grammar(switch_block, js_dest,
                                             LANGUAGE_SPECS, exclude)
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=js_dest))
        return False
    if matches:
        show_open('Code does not have "switch" with "default" clause',
                  details=dict(matched=matches))
        return True
    show_close('Code has "switch" with "default" clause',
               details=dict(code_dest=js_dest))
    return False
Example #22
0
def strict_all_tables_disabled(server: str,
                               username: str,
                               password: str,
                               port: int = 3306) -> bool:
    """Check if STRICT_ALL_TABLES is enabled on MySQL server."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = "SHOW VARIABLES LIKE 'sql_mode'"
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        result = 'STRICT_ALL_TABLES' not in list(mycursor)[0][1]

        if result:
            show_open('STRICT_ALL_TABLES not enabled on by server',
                      details=dict(server=server))
        else:
            show_close('STRICT_ALL_TABLES enabled on by server',
                       details=dict(server=server))
        return result
Example #23
0
def has_not_autocomplete(filename: str) -> bool:
    """
    Check the autocomplete attribute.

    Check if tags ``form`` and ``input`` have the ``autocomplete``
    attribute set to ``off``.

    :param filename: Path to the ``HTML`` source.
    :returns: True if tags ``form`` and ``input`` have attribute
              ``autocomplete`` set as specified, False otherwise.
    """
    tk_off = CaselessKeyword('off')
    attr = {'autocomplete': tk_off}
    tag_i = 'input'
    tag_f = 'form'
    try:
        has_input = _has_attributes(filename, tag_i, attr)
        has_form = _has_attributes(filename, tag_f, attr)
    except FileNotFoundError as exc:
        show_unknown('There was an error', details=dict(error=str(exc)))
        return False

    if not (has_input or has_form):
        result = True
        show_open('Attribute in {}'.format(filename),
                  details=dict(atributes=str(attr)))
    else:
        result = False
        show_close('Attribute in {}'.format(filename),
                   details=dict(atributes=str(attr)))
    return result
Example #24
0
def log_error_disabled(server: str,
                       username: str,
                       password: str,
                       port: int = 3306) -> bool:
    """Check if 'log_error' parameter is set on MySQL server."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = "SHOW variables LIKE 'log_error'"
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        result = ('log_error', '') in list(mycursor)

        if result:
            show_open('Parameter "log_error" not set on server',
                      details=dict(server=server))
        else:
            show_close('Parameter "log_error" is set on server',
                       details=dict(server=server))
        return result
Example #25
0
def has_no_password_protection(path: str) -> bool:
    """
    Check if .jks files are password protected.

    :param path: path to check
    """
    if not os.path.exists(path):
        show_unknown('Path does not exist',
                     details=dict(path=path))
        return False
    jks_with_password: list = []
    jks_without_password: list = []
    for full_path in full_paths_in_dir(path):
        if not full_path.endswith('.jks'):
            continue
        try:
            jks.KeyStore.load(full_path, '')
        except jks.util.KeystoreSignatureException:
            # has password
            jks_with_password.append(dict(path=full_path,
                                          sha256=get_sha256(full_path)))
        else:
            # has not password
            jks_without_password.append(dict(path=full_path,
                                             sha256=get_sha256(full_path)))
    if jks_without_password:
        show_open('JKS is/are not password protected',
                  details=dict(jks_without_password=jks_without_password))
        return True
    show_close('JKS is/are password protected',
               details=dict(jks_with_password=jks_with_password))
    return False
Example #26
0
def logs_on_system_fs(server: str,
                      username: str,
                      password: str,
                      port: int = 3306) -> bool:
    """Check if logs are stored on a system filesystem on server."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        query = "SELECT @@global.log_bin_basename"
        try:
            mycursor.execute(query)
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        _result = list(mycursor)[0][0]
        result = _result.startswith('/var') or _result.startswith('/usr')

        if result:
            show_open('Logs are stored on system filesystems on server',
                      details=dict(server=server))
        else:
            show_close('Logs are outside system filesystems on server',
                       details=dict(server=server))
        return result
Example #27
0
def has_dos_dow_sqlcod(rpg_dest: str, exclude: list = None) -> bool:
    r"""
    Search for DoS for using ``DoW SQLCOD = <ZERO>``\ .

    :param rpg_dest: Path to a RPG source or directory.
    :param exclude: Paths that contains any string from this list are ignored.
    """
    tk_dow = CaselessKeyword('dow')
    tk_sqlcod = CaselessKeyword('sqlcod')
    tk_literal_zero = CaselessKeyword('*zeros')
    tk_zeros = MatchFirst([Literal('0'), tk_literal_zero])

    dos_dow_sqlcod = tk_dow + tk_sqlcod + Literal('=') + tk_zeros

    result = False
    try:
        matches = lang.check_grammar(dos_dow_sqlcod, rpg_dest, LANGUAGE_SPECS,
                                     exclude)
        if not matches:
            show_close('Code does not have DoS for using "DoW SQLCOD = 0"',
                       details=dict(code_dest=rpg_dest))
            return False
    except FileNotFoundError:
        show_unknown('File does not exist', details=dict(code_dest=rpg_dest))
        return False
    else:
        result = True
        show_open('Code has DoS for using "DoW SQLCOD = 0"',
                  details=dict(matched=matches, total_vulns=len(matches)))
    return result
Example #28
0
def test_db_exists(server: str,
                   username: str,
                   password: str,
                   port: int = 3306) -> bool:
    """Check if "test" database exists."""
    try:
        mydb = _get_mysql_cursor(server, username, password, port)
    except ConnError as exc:
        show_unknown('There was an error connecting to MySQL engine',
                     details=dict(server=server, user=username,
                                  error=str(exc)))
        return False
    else:
        mycursor = mydb.cursor()

        try:
            mycursor.execute("SHOW DATABASES")
        except mysql.connector.errors.ProgrammingError as exc:
            show_unknown('There was an error executing query',
                         details=dict(server=server,
                                      username=username,
                                      error=str(exc).replace(':', ',')))
            return False

        result = ('test', ) in list(mycursor)

        if result:
            show_open('Database "test" is present',
                      details=dict(server=server))
        else:
            show_close('Database "test" not present',
                       details=dict(server=server))
        return result
Example #29
0
def has_insecure_expiration_time(jwt_token: str,
                                 max_expiration_time: int = 600) -> bool:
    """
    Check if the given JWT has an insecure expiration time.

    :param jwt_token: JWT to test.
    :param max_expiration_time: According to the bussiness rule, (in seconds).
    """
    try:
        claimset = decode(jwt_token, verify=False)
    except InvalidTokenError:
        show_unknown('Unable to decode token.',
                     details=dict(jwt_token=jwt_token))
        return False
    iat = claimset.get('iat')
    exp = claimset.get('exp')
    if not iat or not exp:
        show_open('Token does not include an `iat` or `exp` claims',
                  details=dict(claims_set=claimset))
        return True
    expiration_time = (exp - iat)
    if expiration_time > max_expiration_time:
        show_open('Token has an insecure expiration time',
                  details=dict(claims_set=claimset,
                               expiration_time=expiration_time,
                               max_expiration_time=max_expiration_time))

    else:
        show_close('Token has a secure expiration time',
                   details=dict(claims_set=claimset,
                                expiration_time=expiration_time,
                                max_expiration_time=max_expiration_time))
    return expiration_time > max_expiration_time
Example #30
0
def has_recursion(nameserver: str) -> bool:
    """
    Check if nameserver has recursion enabled.

    :param nameserver: IPv4 or 6 to test.
    """
    domain = 'google.com.'
    name = dns.name.from_text(domain)

    try:
        # Make a recursive request
        request = dns.message.make_query(name, dns.rdatatype.A,
                                         dns.rdataclass.IN)

        response = dns.query.udp(request, nameserver, timeout=5)
        result = True
        if response.rcode() == 0:
            show_open('Recursion possible on server',
                      details=dict(domain=domain, nameserver=nameserver))
            result = True
        else:
            show_close('Recursion not possible on server',
                       details=dict(domain=domain, nameserver=nameserver))
            result = False
    except dns.exception.SyntaxError:
        show_close('Recursion not possible on server',
                   details=dict(domain=domain, nameserver=nameserver))
        result = False
    except (socket.error, dns.exception.Timeout) as exc:
        show_unknown('Could not connect',
                     details=dict(domain=domain,
                                  nameserver=nameserver,
                                  error=str(exc).replace(':', ',')))
        result = False
    return result