Adapted zecora-z FTPScanner class.

This commit is contained in:
Zloooy 2019-11-13 22:17:42 +03:00
parent c0dbcf154a
commit b05cd4373d
8 changed files with 90 additions and 53 deletions

View File

@ -2,18 +2,22 @@
config = { config = {
"parser" : "parser" :
{ {
"name":"GDocsHashParser", "name":"Parser",
"init_args":{} "init_args":{}
}, },
"address_generator" : "address_generator" :
{ {
"name":"GDocsAddressGenerator", "name":"IpGenerator",
"init_args":{} "init_args":{}
}, },
"scanner" : "scanner" :
{ {
"name":"GDocsScanner", "name":"FTPScanner",
"init_args":{} "init_args":{
"credentials": (
("admin", "admin")
)
}
}, },
"storage" : "storage" :
{ {
@ -21,17 +25,18 @@ config = {
"init_args": "init_args":
{ {
"path":"results.json", "path":"results.json",
"json_scheme":{ "json_scheme":
"status": {
{ "ftp_status":
"gdoc_prefix": [
[ {
{ "@ip":"ipv4_str",
"@hash": "gdoc_hash", "@port":"port",
"@title": "gdoc_title" "@login":"login",
} "@password":"password",
] "@ftp_version":"ftp_version",
} }
]
} }
} }
} }
@ -54,3 +59,15 @@ config = {
} }
} }
}''' }'''
'''scheme for gdocs scanner
"status":
{
"gdoc_prefix":
[
{
"@hash": "gdoc_hash",
"@title": "gdoc_title"
}
]
}
'''

View File

@ -72,6 +72,7 @@ class MainPresenter:
get_return_annotations(IpGenerator.get_next_address).union(get_return_annotations(CoreModel.scan_address)), get_return_annotations(IpGenerator.get_next_address).union(get_return_annotations(CoreModel.scan_address)),
get_argument_annotations(self.storage.put_responce) get_argument_annotations(self.storage.put_responce)
) )
input()
self.exit_lock = RLock() self.exit_lock = RLock()
def startScan(self, ipRanges, portsStr, threadNumber, timeout): def startScan(self, ipRanges, portsStr, threadNumber, timeout):
@ -174,6 +175,7 @@ class ScanWorker(QObject):
) )
print(scan_result) print(scan_result)
scan_address.update(scan_result) scan_address.update(scan_result)
print(scan_address)
self.previous_address = scan_address self.previous_address = scan_address
self.storage.put_responce( self.storage.put_responce(
*convert_for_storage(scan_address) *convert_for_storage(scan_address)
@ -181,9 +183,9 @@ class ScanWorker(QObject):
string_scan_address = " ".join(key + ":" + str(scan_address[key]) for string_scan_address = " ".join(key + ":" + str(scan_address[key]) for
key in scan_address.keys()) key in scan_address.keys())
if scan_result == 0: if scan_result == 0:
self.log_signal.emit('%s is open' % string_scan_address) self.log_signal.emit(string_scan_address)
else: else:
self.log_signal.emit('%s is closed' % string_scan_address) self.log_signal.emit(string_scan_address)
self.stop() self.stop()
def stop(self): def stop(self):

View File

@ -30,6 +30,7 @@ class ConvertTable():
function.__annotations__.items() if function.__annotations__.items() if
key!='return') key!='return')
if input_args.issubset(from_keys) and to_key.issubset(function.__annotations__['return']): if input_args.issubset(from_keys) and to_key.issubset(function.__annotations__['return']):
print("found converter for %s!!!" % to_key)
return input_args, function return input_args, function
raise Exception("There is no converter for %s to %s" % (from_keys, raise Exception("There is no converter for %s to %s" % (from_keys,
to_key)) to_key))
@ -38,23 +39,31 @@ class ConvertTable():
def get_metaconverter(self, from_keys, to_keys): def get_metaconverter(self, from_keys, to_keys):
'''This function constructs and returns new function used to provide fast '''This function constructs and returns new function used to provide fast
conversion from from_keys to to_keys''' conversion from from_keys to to_keys'''
print("from_keys",from_keys)
print("to_keys",to_keys)
converters_args = [] converters_args = []
converters = [] converters = []
for key in to_keys: for key in to_keys:
keys_to_convert, converter = None, None keys_to_convert, converter = None, None
if key in from_keys: if key in from_keys:
print("%s is in from_keys" % key)
keys_to_convert = [key] keys_to_convert = [key]
converter = lambda x : {key: x} converter = lambda x : {key: x}
else: else:
print("getting converter for %s." % key)
keys_to_convert, converter = self.get_converter(from_keys, key) keys_to_convert, converter = self.get_converter(from_keys, key)
print("needed keys: %s" % " ".join(keys_to_convert))
converters_args.append(keys_to_convert) converters_args.append(keys_to_convert)
converters.append(converter) converters.append(converter)
def metaconverter(args_dict): def metaconverter(args_dict):
if args_dict == None: if args_dict == None:
return [None] * len(converters) return [None] * len(converters)
res = [] res = []
print(converters)
print(converters_args)
for i,conv in enumerate(converters): for i,conv in enumerate(converters):
print(converters_args[i])
print(args_dict)
args = [args_dict[arg] for arg in converters_args[i]] args = [args_dict[arg] for arg in converters_args[i]]
res.append(*[value for key, value in conv(*args).items()]) res.append(*[value for key, value in conv(*args).items()])
return res return res

View File

@ -1,17 +1,15 @@
from core.prototypes.AbstractAddressGenerator import AbstractAddressGenerator from core.prototypes.AbstractAddressGenerator import AbstractAddressGenerator
from core.prototypes.AbstractModuleClass import internal
from threading import RLock from threading import RLock
import ipaddress import ipaddress
from types import GeneratorType from types import GeneratorType
class IpGenerator(AbstractAddressGenerator): class IpGenerator(AbstractAddressGenerator):
def set_parsed_fields(self, ips : 'ipv4_ranges', ports : 'ports') -> None: def set_parsed_fields(self, ips : 'ipv4_objects', ports : 'ports') -> None:
self.ips = ips self.ips = ips
self.ports = ports self.ports = ports
self.lock = RLock() self.lock = RLock()
@internal
def get_next_port_number(self, previous_port): def get_next_port_number(self, previous_port):
return (self.ports.index(previous_port) + 1) % len(self.ports) return (self.ports.index(previous_port) + 1) % len(self.ports)

View File

@ -1,6 +1,5 @@
import ipaddress import ipaddress
from core.prototypes.AbstractParser import AbstractParser from core.prototypes.AbstractParser import AbstractParser
from core.prototypes.AbstractModuleClass import internal
class Parser(AbstractParser): class Parser(AbstractParser):
@ -9,10 +8,9 @@ class Parser(AbstractParser):
'ports'}: 'ports'}:
result = dict() result = dict()
result['ports'] = self.parse_port_field(ports) result['ports'] = self.parse_port_field(ports)
result['ipv4_ranges'] = self.get_all_addresses(ips) result['ipv4_objects'] = self.get_all_addresses(ips)
return result return result
@internal
def parse_port_field(self, ports): def parse_port_field(self, ports):
""" """
Parses ports from string, returns them as integers in the list. Parses ports from string, returns them as integers in the list.
@ -40,7 +38,6 @@ class Parser(AbstractParser):
# Change to default ports from constant # Change to default ports from constant
return [21, 22, 23, 25, 80, 443, 110, 111, 135, 139, 445, 8080, 8443, 53, 143, 989, 990, 3306, 1080, 5554, 6667, 2222, 4444, 666, 6666, 1337, 2020, 31337] return [21, 22, 23, 25, 80, 443, 110, 111, 135, 139, 445, 8080, 8443, 53, 143, 989, 990, 3306, 1080, 5554, 6667, 2222, 4444, 666, 6666, 1337, 2020, 31337]
@internal
def parse_address_field(self, ips): def parse_address_field(self, ips):
""" """
Parses ip input string, returns the generator over them. Parses ip input string, returns the generator over them.
@ -62,7 +59,6 @@ class Parser(AbstractParser):
for host in ip_obj: for host in ip_obj:
yield host yield host
@internal
def get_all_addresses(self, ips): def get_all_addresses(self, ips):
ip_objects = set() ip_objects = set()
inputs = [ip.strip() for ip in ips.split(',')] inputs = [ip.strip() for ip in ips.split(',')]

View File

@ -9,21 +9,27 @@ MAX_ERRORS = 3
class FTPScanner(AbstractScanner): class FTPScanner(AbstractScanner):
def __init__(self, timeout): def __init__(self, timeout:"timeout", credentials:"credentials"):
self.__timeout__ = timeout self.__timeout__ = timeout
self.__credantials__ = credentials
def scan_address(self, host: 'ipv4_str or hostname', port: 'port', credentials: 'tuples with login and password') -> {'scan_result'}: def scan_address(self, host: 'ipv4_str', port: 'port') -> {'ftp_version', 'ftp_status', 'login', 'password'}:
result = self.ftp_anonymous_login(host, port, self.__timeout__) result = self.ftp_anonymous_login(host, port, self.__timeout__)
if result['status'] == 'error' or result['anonymous_login']: if result['ftp_status'] == 'ok':
#Что-то делать с ошибками
return result return result
result['credentials'] = self.ftp_bruteforce( if not result['ftp_status'].startswith('530'):
host, port, credentials, self.__timeout__) return result
return result return self.ftp_bruteforce(
host, port, self.__credentials__, self.__timeout__)
@staticmethod @staticmethod
def ftp_anonymous_login(host, port, timeout): def ftp_anonymous_login(host, port, timeout):
'''Get version and check if anonympous login is enabled''' '''Get version and check if anonympous login is enabled'''
result = {} result = {
key:None for key in ['ftp_version', 'ftp_status', 'login',
'password']
}
ftp_connection = FTP(timeout=timeout) ftp_connection = FTP(timeout=timeout)
try: try:
version = ftp_connection.connect(host=host, port=port) version = ftp_connection.connect(host=host, port=port)
@ -31,15 +37,14 @@ class FTPScanner(AbstractScanner):
result['ftp_version'] = version.lstrip('220 ') result['ftp_version'] = version.lstrip('220 ')
# Try to login as anonymous user # Try to login as anonymous user
ftp_connection.login() ftp_connection.login()
result['anonymous_login'] = True result['ftp_status'] = 'ok'
result['status'] = 'ok'
except ftplib.error_perm as e: except ftplib.error_perm as e:
if str(e).startswith("530"): if str(e).startswith("530"):
result['status'] = 'ok' result['ftp_status'] = 'ok'
result['anonymous_login'] = False result['anonymous_login'] = False
except ftplib.all_errors as e: except ftplib.all_errors as e:
result['status'] = 'error' #status - error
result['error_type'] = str(e) result['ftp_status'] = str(e)
return result return result
finally: finally:
ftp_connection.close() ftp_connection.close()
@ -52,6 +57,11 @@ class FTPScanner(AbstractScanner):
# but we also want to reconnect if necessary. # but we also want to reconnect if necessary.
# That is why I use cred iterator to pick up new login/pass only when # That is why I use cred iterator to pick up new login/pass only when
# we need to. # we need to.
result = {
key:None for key in ['ftp_version', 'ftp_status', 'login',
'password']
}
result['ftp_status'] = "error"
error_count = 0 error_count = 0
it = iter(creds) it = iter(creds)
cred = next(it, "") cred = next(it, "")
@ -66,7 +76,10 @@ class FTPScanner(AbstractScanner):
try: try:
ftp_connection.login(user, password) ftp_connection.login(user, password)
ftp_connection.close() ftp_connection.close()
return user, password result['ftp_status'] = 'ok'
result['login'] = user
result['password'] = password
return result
except ftplib.error_perm as e: except ftplib.error_perm as e:
# Password was wrong, checking another # Password was wrong, checking another
cred = next(it, "") cred = next(it, "")
@ -82,4 +95,4 @@ class FTPScanner(AbstractScanner):
break break
finally: finally:
ftp_connection.close() ftp_connection.close()
return None return result

View File

@ -46,18 +46,18 @@ def run_mumble():
class TestFTPScanner(unittest.TestCase): class TestFTPScanner(unittest.TestCase):
def test_closed_port(self): def test_closed_port(self):
scanner = FTPScanner(timeout=10) scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
result = scanner.scan_address('127.0.0.1', 31337, credentials=TEST_CREDS) result = scanner.scan_address('127.0.0.1', 31337)
print(result) print(result)
self.assertEqual(result['status'], 'error', "Should be error") #self.assertEqual(result['status'], 'Connection refused', "Should be error")
self.assertTrue("Connection refused" in result['error_type'], "Connection refused") self.assertTrue("Connection refused" in result['ftp_status'], "Connection refused")
def test_mumble(self): def test_mumble(self):
p = multiprocessing.Process(target=run_mumble) p = multiprocessing.Process(target=run_mumble)
p.start() p.start()
sleep(5) sleep(5)
scanner = FTPScanner(timeout=10) scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS) result = scanner.scan_address('127.0.0.1', PORT)
print(result) print(result)
self.assertEqual(result['status'], 'error', "Should be error") self.assertEqual(result['status'], 'error', "Should be error")
self.assertTrue("timed out" in result['error_type'], "Timed out") self.assertTrue("timed out" in result['error_type'], "Timed out")
@ -68,10 +68,11 @@ class TestFTPScanner(unittest.TestCase):
p = multiprocessing.Process(target=run_anonymous_ftp, args=(temp_dir,)) p = multiprocessing.Process(target=run_anonymous_ftp, args=(temp_dir,))
p.start() p.start()
sleep(5) sleep(5)
scanner = FTPScanner(timeout=10) scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS) result = scanner.scan_address('127.0.0.1', PORT)
print(result) print(result)
self.assertEqual(result['anonymous_login'], True, "Should be True") self.assertEqual(result['login'], None, "Should be True")
self.assertEqual(result['password'], None, "Should be True")
p.terminate() p.terminate()
os.rmdir(temp_dir) os.rmdir(temp_dir)
@ -80,10 +81,11 @@ class TestFTPScanner(unittest.TestCase):
p = multiprocessing.Process(target=run_bruteforce_ftp, args=(temp_dir,)) p = multiprocessing.Process(target=run_bruteforce_ftp, args=(temp_dir,))
p.start() p.start()
sleep(5) sleep(5)
scanner = FTPScanner(timeout=10) scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS) result = scanner.scan_address('127.0.0.1', PORT)
print(result) print(result)
self.assertEqual(result['credentials'], TEST_CREDS[-1], "Should be True") self.assertEqual(result['login'], TEST_CREDS[-1][0], "Should be True")
self.assertEqual(result['password'], TEST_CREDS[-1][1], "Should be True")
p.terminate() p.terminate()
os.rmdir(temp_dir) os.rmdir(temp_dir)

View File

@ -1 +1 @@
{"200": {"https://docs.google.com/document/d/": [{"hash": "11Sz_PyqL268V9xmcEjYqEhufFGleT5TowdKEu5cTFak", "title": null}, {"hash": "11Sz_PyqL268V9xmcEjYqEhufFGleT5TowdKEu5cTFal", "title": null}, {"hash": "11Sz_PyqL268V9xmcEjYqEhufFGleT5TowdKEu5cTFam", "title": null}]}} {"timed out": [{"ip": "122.3.42.1", "port": 20, "login": null, "password": null, "ftp_version": null}, {"ip": "122.3.42.1", "port": 21, "login": null, "password": null, "ftp_version": null}]}