mirror of
https://github.com/ChronosX88/PyNesca.git
synced 2024-11-21 20:52:18 +00:00
Adapted zecora-z FTPScanner class.
This commit is contained in:
parent
c0dbcf154a
commit
b05cd4373d
37
config.py
37
config.py
@ -2,18 +2,22 @@
|
||||
config = {
|
||||
"parser" :
|
||||
{
|
||||
"name":"GDocsHashParser",
|
||||
"name":"Parser",
|
||||
"init_args":{}
|
||||
},
|
||||
"address_generator" :
|
||||
{
|
||||
"name":"GDocsAddressGenerator",
|
||||
"name":"IpGenerator",
|
||||
"init_args":{}
|
||||
},
|
||||
"scanner" :
|
||||
{
|
||||
"name":"GDocsScanner",
|
||||
"init_args":{}
|
||||
"name":"FTPScanner",
|
||||
"init_args":{
|
||||
"credentials": (
|
||||
("admin", "admin")
|
||||
)
|
||||
}
|
||||
},
|
||||
"storage" :
|
||||
{
|
||||
@ -21,20 +25,21 @@ config = {
|
||||
"init_args":
|
||||
{
|
||||
"path":"results.json",
|
||||
"json_scheme":{
|
||||
"status":
|
||||
"json_scheme":
|
||||
{
|
||||
"gdoc_prefix":
|
||||
"ftp_status":
|
||||
[
|
||||
{
|
||||
"@hash": "gdoc_hash",
|
||||
"@title": "gdoc_title"
|
||||
"@ip":"ipv4_str",
|
||||
"@port":"port",
|
||||
"@login":"login",
|
||||
"@password":"password",
|
||||
"@ftp_version":"ftp_version",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
'''scheme for url scanner
|
||||
@ -54,3 +59,15 @@ config = {
|
||||
}
|
||||
}
|
||||
}'''
|
||||
'''scheme for gdocs scanner
|
||||
"status":
|
||||
{
|
||||
"gdoc_prefix":
|
||||
[
|
||||
{
|
||||
"@hash": "gdoc_hash",
|
||||
"@title": "gdoc_title"
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
@ -72,6 +72,7 @@ class MainPresenter:
|
||||
get_return_annotations(IpGenerator.get_next_address).union(get_return_annotations(CoreModel.scan_address)),
|
||||
get_argument_annotations(self.storage.put_responce)
|
||||
)
|
||||
input()
|
||||
self.exit_lock = RLock()
|
||||
|
||||
def startScan(self, ipRanges, portsStr, threadNumber, timeout):
|
||||
@ -174,6 +175,7 @@ class ScanWorker(QObject):
|
||||
)
|
||||
print(scan_result)
|
||||
scan_address.update(scan_result)
|
||||
print(scan_address)
|
||||
self.previous_address = scan_address
|
||||
self.storage.put_responce(
|
||||
*convert_for_storage(scan_address)
|
||||
@ -181,9 +183,9 @@ class ScanWorker(QObject):
|
||||
string_scan_address = " ".join(key + ":" + str(scan_address[key]) for
|
||||
key in scan_address.keys())
|
||||
if scan_result == 0:
|
||||
self.log_signal.emit('%s is open' % string_scan_address)
|
||||
self.log_signal.emit(string_scan_address)
|
||||
else:
|
||||
self.log_signal.emit('%s is closed' % string_scan_address)
|
||||
self.log_signal.emit(string_scan_address)
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
|
@ -30,6 +30,7 @@ class ConvertTable():
|
||||
function.__annotations__.items() if
|
||||
key!='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
|
||||
raise Exception("There is no converter for %s to %s" % (from_keys,
|
||||
to_key))
|
||||
@ -38,23 +39,31 @@ class ConvertTable():
|
||||
def get_metaconverter(self, from_keys, to_keys):
|
||||
'''This function constructs and returns new function used to provide fast
|
||||
conversion from from_keys to to_keys'''
|
||||
print("from_keys",from_keys)
|
||||
print("to_keys",to_keys)
|
||||
converters_args = []
|
||||
converters = []
|
||||
for key in to_keys:
|
||||
keys_to_convert, converter = None, None
|
||||
if key in from_keys:
|
||||
print("%s is in from_keys" % key)
|
||||
keys_to_convert = [key]
|
||||
converter = lambda x : {key: x}
|
||||
else:
|
||||
print("getting converter for %s." % 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.append(converter)
|
||||
|
||||
def metaconverter(args_dict):
|
||||
if args_dict == None:
|
||||
return [None] * len(converters)
|
||||
res = []
|
||||
print(converters)
|
||||
print(converters_args)
|
||||
for i,conv in enumerate(converters):
|
||||
print(converters_args[i])
|
||||
print(args_dict)
|
||||
args = [args_dict[arg] for arg in converters_args[i]]
|
||||
res.append(*[value for key, value in conv(*args).items()])
|
||||
return res
|
||||
|
@ -1,17 +1,15 @@
|
||||
from core.prototypes.AbstractAddressGenerator import AbstractAddressGenerator
|
||||
from core.prototypes.AbstractModuleClass import internal
|
||||
from threading import RLock
|
||||
import ipaddress
|
||||
from types import GeneratorType
|
||||
|
||||
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.ports = ports
|
||||
self.lock = RLock()
|
||||
|
||||
@internal
|
||||
def get_next_port_number(self, previous_port):
|
||||
return (self.ports.index(previous_port) + 1) % len(self.ports)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import ipaddress
|
||||
from core.prototypes.AbstractParser import AbstractParser
|
||||
from core.prototypes.AbstractModuleClass import internal
|
||||
|
||||
|
||||
class Parser(AbstractParser):
|
||||
@ -9,10 +8,9 @@ class Parser(AbstractParser):
|
||||
'ports'}:
|
||||
result = dict()
|
||||
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
|
||||
|
||||
@internal
|
||||
def parse_port_field(self, ports):
|
||||
"""
|
||||
Parses ports from string, returns them as integers in the list.
|
||||
@ -40,7 +38,6 @@ class Parser(AbstractParser):
|
||||
# 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]
|
||||
|
||||
@internal
|
||||
def parse_address_field(self, ips):
|
||||
"""
|
||||
Parses ip input string, returns the generator over them.
|
||||
@ -62,7 +59,6 @@ class Parser(AbstractParser):
|
||||
for host in ip_obj:
|
||||
yield host
|
||||
|
||||
@internal
|
||||
def get_all_addresses(self, ips):
|
||||
ip_objects = set()
|
||||
inputs = [ip.strip() for ip in ips.split(',')]
|
||||
|
@ -9,21 +9,27 @@ MAX_ERRORS = 3
|
||||
|
||||
|
||||
class FTPScanner(AbstractScanner):
|
||||
def __init__(self, timeout):
|
||||
def __init__(self, timeout:"timeout", credentials:"credentials"):
|
||||
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__)
|
||||
if result['status'] == 'error' or result['anonymous_login']:
|
||||
if result['ftp_status'] == 'ok':
|
||||
#Что-то делать с ошибками
|
||||
return result
|
||||
result['credentials'] = self.ftp_bruteforce(
|
||||
host, port, credentials, self.__timeout__)
|
||||
if not result['ftp_status'].startswith('530'):
|
||||
return result
|
||||
return self.ftp_bruteforce(
|
||||
host, port, self.__credentials__, self.__timeout__)
|
||||
|
||||
@staticmethod
|
||||
def ftp_anonymous_login(host, port, timeout):
|
||||
'''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)
|
||||
try:
|
||||
version = ftp_connection.connect(host=host, port=port)
|
||||
@ -31,15 +37,14 @@ class FTPScanner(AbstractScanner):
|
||||
result['ftp_version'] = version.lstrip('220 ')
|
||||
# Try to login as anonymous user
|
||||
ftp_connection.login()
|
||||
result['anonymous_login'] = True
|
||||
result['status'] = 'ok'
|
||||
result['ftp_status'] = 'ok'
|
||||
except ftplib.error_perm as e:
|
||||
if str(e).startswith("530"):
|
||||
result['status'] = 'ok'
|
||||
result['ftp_status'] = 'ok'
|
||||
result['anonymous_login'] = False
|
||||
except ftplib.all_errors as e:
|
||||
result['status'] = 'error'
|
||||
result['error_type'] = str(e)
|
||||
#status - error
|
||||
result['ftp_status'] = str(e)
|
||||
return result
|
||||
finally:
|
||||
ftp_connection.close()
|
||||
@ -52,6 +57,11 @@ class FTPScanner(AbstractScanner):
|
||||
# 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.
|
||||
result = {
|
||||
key:None for key in ['ftp_version', 'ftp_status', 'login',
|
||||
'password']
|
||||
}
|
||||
result['ftp_status'] = "error"
|
||||
error_count = 0
|
||||
it = iter(creds)
|
||||
cred = next(it, "")
|
||||
@ -66,7 +76,10 @@ class FTPScanner(AbstractScanner):
|
||||
try:
|
||||
ftp_connection.login(user, password)
|
||||
ftp_connection.close()
|
||||
return user, password
|
||||
result['ftp_status'] = 'ok'
|
||||
result['login'] = user
|
||||
result['password'] = password
|
||||
return result
|
||||
except ftplib.error_perm as e:
|
||||
# Password was wrong, checking another
|
||||
cred = next(it, "")
|
||||
@ -82,4 +95,4 @@ class FTPScanner(AbstractScanner):
|
||||
break
|
||||
finally:
|
||||
ftp_connection.close()
|
||||
return None
|
||||
return result
|
||||
|
@ -46,18 +46,18 @@ def run_mumble():
|
||||
|
||||
class TestFTPScanner(unittest.TestCase):
|
||||
def test_closed_port(self):
|
||||
scanner = FTPScanner(timeout=10)
|
||||
result = scanner.scan_address('127.0.0.1', 31337, credentials=TEST_CREDS)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', 31337)
|
||||
print(result)
|
||||
self.assertEqual(result['status'], 'error', "Should be error")
|
||||
self.assertTrue("Connection refused" in result['error_type'], "Connection refused")
|
||||
#self.assertEqual(result['status'], 'Connection refused', "Should be error")
|
||||
self.assertTrue("Connection refused" in result['ftp_status'], "Connection refused")
|
||||
|
||||
def test_mumble(self):
|
||||
p = multiprocessing.Process(target=run_mumble)
|
||||
p.start()
|
||||
sleep(5)
|
||||
scanner = FTPScanner(timeout=10)
|
||||
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', PORT)
|
||||
print(result)
|
||||
self.assertEqual(result['status'], 'error', "Should be error")
|
||||
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.start()
|
||||
sleep(5)
|
||||
scanner = FTPScanner(timeout=10)
|
||||
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', PORT)
|
||||
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()
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
@ -80,10 +81,11 @@ class TestFTPScanner(unittest.TestCase):
|
||||
p = multiprocessing.Process(target=run_bruteforce_ftp, args=(temp_dir,))
|
||||
p.start()
|
||||
sleep(5)
|
||||
scanner = FTPScanner(timeout=10)
|
||||
result = scanner.scan_address('127.0.0.1', PORT, credentials=TEST_CREDS)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', PORT)
|
||||
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()
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
|
@ -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}]}
|
Loading…
Reference in New Issue
Block a user