2019-07-29 10:25:11 +00:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding:utf-8 -*-
|
|
|
|
|
|
|
|
|
|
from core.prototypes.AbstractScanner import AbstractScanner
|
|
|
|
|
import ftplib
|
|
|
|
|
from ftplib import FTP
|
|
|
|
|
|
|
|
|
|
MAX_ERRORS = 3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FTPScanner(AbstractScanner):
|
2019-11-13 19:17:42 +00:00
|
|
|
|
def __init__(self, timeout:"timeout", credentials:"credentials"):
|
2019-07-29 10:25:11 +00:00
|
|
|
|
self.__timeout__ = timeout
|
2019-11-13 19:17:42 +00:00
|
|
|
|
self.__credantials__ = credentials
|
2019-07-29 10:25:11 +00:00
|
|
|
|
|
2019-11-13 19:17:42 +00:00
|
|
|
|
def scan_address(self, host: 'ipv4_str', port: 'port') -> {'ftp_version', 'ftp_status', 'login', 'password'}:
|
2019-07-29 10:25:11 +00:00
|
|
|
|
result = self.ftp_anonymous_login(host, port, self.__timeout__)
|
2019-11-13 19:17:42 +00:00
|
|
|
|
if result['ftp_status'] == 'ok':
|
|
|
|
|
#Что-то делать с ошибками
|
2019-07-29 10:25:11 +00:00
|
|
|
|
return result
|
2019-11-13 19:17:42 +00:00
|
|
|
|
if not result['ftp_status'].startswith('530'):
|
|
|
|
|
return result
|
|
|
|
|
return self.ftp_bruteforce(
|
|
|
|
|
host, port, self.__credentials__, self.__timeout__)
|
2019-07-29 10:25:11 +00:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ftp_anonymous_login(host, port, timeout):
|
|
|
|
|
'''Get version and check if anonympous login is enabled'''
|
2019-11-13 19:17:42 +00:00
|
|
|
|
result = {
|
|
|
|
|
key:None for key in ['ftp_version', 'ftp_status', 'login',
|
|
|
|
|
'password']
|
|
|
|
|
}
|
2019-07-29 10:25:11 +00:00
|
|
|
|
ftp_connection = FTP(timeout=timeout)
|
|
|
|
|
try:
|
|
|
|
|
version = ftp_connection.connect(host=host, port=port)
|
|
|
|
|
# Get something like "220 Twisted 16.6.0 FTP Server"
|
|
|
|
|
result['ftp_version'] = version.lstrip('220 ')
|
|
|
|
|
# Try to login as anonymous user
|
|
|
|
|
ftp_connection.login()
|
2019-11-13 19:17:42 +00:00
|
|
|
|
result['ftp_status'] = 'ok'
|
2019-07-29 10:25:11 +00:00
|
|
|
|
except ftplib.error_perm as e:
|
|
|
|
|
if str(e).startswith("530"):
|
2019-11-13 19:17:42 +00:00
|
|
|
|
result['ftp_status'] = 'ok'
|
2019-07-29 10:25:11 +00:00
|
|
|
|
result['anonymous_login'] = False
|
|
|
|
|
except ftplib.all_errors as e:
|
2019-11-13 19:17:42 +00:00
|
|
|
|
#status - error
|
|
|
|
|
result['ftp_status'] = str(e)
|
2019-07-29 10:25:11 +00:00
|
|
|
|
return result
|
|
|
|
|
finally:
|
|
|
|
|
ftp_connection.close()
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ftp_bruteforce(host, port, creds, timeout):
|
|
|
|
|
'''Attempt to brute force login/password pair'''
|
|
|
|
|
# We want maintain connection to speed up bruteforce
|
|
|
|
|
# but we also want to reconnect if necessary.
|
|
|
|
|
# That is why I use cred iterator to pick up new login/pass only when
|
|
|
|
|
# we need to.
|
2019-11-13 19:17:42 +00:00
|
|
|
|
result = {
|
|
|
|
|
key:None for key in ['ftp_version', 'ftp_status', 'login',
|
|
|
|
|
'password']
|
|
|
|
|
}
|
|
|
|
|
result['ftp_status'] = "error"
|
2019-07-29 10:25:11 +00:00
|
|
|
|
error_count = 0
|
|
|
|
|
it = iter(creds)
|
|
|
|
|
cred = next(it, "")
|
|
|
|
|
ftp_connection = FTP(timeout=timeout)
|
|
|
|
|
while error_count < MAX_ERRORS:
|
|
|
|
|
try:
|
|
|
|
|
# Connecting to server
|
|
|
|
|
ftp_connection.connect(host=host, port=port)
|
|
|
|
|
while cred and error_count < MAX_ERRORS:
|
|
|
|
|
user, password = cred
|
|
|
|
|
# Trying to log in
|
|
|
|
|
try:
|
|
|
|
|
ftp_connection.login(user, password)
|
|
|
|
|
ftp_connection.close()
|
2019-11-13 19:17:42 +00:00
|
|
|
|
result['ftp_status'] = 'ok'
|
|
|
|
|
result['login'] = user
|
|
|
|
|
result['password'] = password
|
|
|
|
|
return result
|
2019-07-29 10:25:11 +00:00
|
|
|
|
except ftplib.error_perm as e:
|
|
|
|
|
# Password was wrong, checking another
|
|
|
|
|
cred = next(it, "")
|
|
|
|
|
continue
|
|
|
|
|
except ftplib.all_errors as e:
|
|
|
|
|
error_count += 1
|
|
|
|
|
# Connection was dropped or another network error happened
|
|
|
|
|
# We must connection, error_count would help us to
|
|
|
|
|
# avoid deadlock on mumbling host
|
|
|
|
|
break
|
|
|
|
|
except ftplib.all_errors as e:
|
|
|
|
|
# Cannot reconnect, give up
|
|
|
|
|
break
|
|
|
|
|
finally:
|
|
|
|
|
ftp_connection.close()
|
2019-11-13 19:17:42 +00:00
|
|
|
|
return result
|