mirror of
https://github.com/ChronosX88/PyNesca.git
synced 2024-11-24 14:02:17 +00:00
Merge branch 'zecora-z'
This commit is contained in:
commit
371d64d005
47
config.py
47
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,17 +25,18 @@ config = {
|
||||
"init_args":
|
||||
{
|
||||
"path":"results.json",
|
||||
"json_scheme":{
|
||||
"status":
|
||||
{
|
||||
"gdoc_prefix":
|
||||
[
|
||||
{
|
||||
"@hash": "gdoc_hash",
|
||||
"@title": "gdoc_title"
|
||||
}
|
||||
]
|
||||
}
|
||||
"json_scheme":
|
||||
{
|
||||
"ftp_status":
|
||||
[
|
||||
{
|
||||
"@ip":"ipv4_str",
|
||||
"@port":"port",
|
||||
"@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"
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
@ -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(',')]
|
||||
|
98
modules/network_scan/FTPScanner.py
Normal file
98
modules/network_scan/FTPScanner.py
Normal file
@ -0,0 +1,98 @@
|
||||
#!/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):
|
||||
def __init__(self, timeout:"timeout", credentials:"credentials"):
|
||||
self.__timeout__ = timeout
|
||||
self.__credantials__ = credentials
|
||||
|
||||
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['ftp_status'] == 'ok':
|
||||
#Что-то делать с ошибками
|
||||
return result
|
||||
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 = {
|
||||
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)
|
||||
# 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()
|
||||
result['ftp_status'] = 'ok'
|
||||
except ftplib.error_perm as e:
|
||||
if str(e).startswith("530"):
|
||||
result['ftp_status'] = 'ok'
|
||||
result['anonymous_login'] = False
|
||||
except ftplib.all_errors as e:
|
||||
#status - error
|
||||
result['ftp_status'] = str(e)
|
||||
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.
|
||||
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, "")
|
||||
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()
|
||||
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, "")
|
||||
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()
|
||||
return result
|
94
modules/network_scan/test_FTPScanner.py
Normal file
94
modules/network_scan/test_FTPScanner.py
Normal file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from modules.network_scan.FTPScanner import FTPScanner
|
||||
from pyftpdlib.authorizers import DummyAuthorizer
|
||||
from pyftpdlib.handlers import FTPHandler
|
||||
from pyftpdlib.servers import FTPServer
|
||||
import unittest
|
||||
from tempfile import mkdtemp
|
||||
import multiprocessing
|
||||
from time import sleep
|
||||
|
||||
import http.server
|
||||
import socketserver
|
||||
import os
|
||||
|
||||
TEST_CREDS = (("admin", "admin"), ("1", "1"), ('user', 'password'))
|
||||
PORT = 2121
|
||||
|
||||
|
||||
def run_anonymous_ftp(temp_dir):
|
||||
authorizer = DummyAuthorizer()
|
||||
authorizer.add_anonymous(temp_dir)
|
||||
handler = FTPHandler
|
||||
handler.authorizer = authorizer
|
||||
server = FTPServer(("127.0.0.1", PORT), handler)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
def run_bruteforce_ftp(temp_dir):
|
||||
authorizer = DummyAuthorizer()
|
||||
user, password = TEST_CREDS[-1]
|
||||
authorizer.add_user(user, password, temp_dir, perm="elradfmw")
|
||||
handler = FTPHandler
|
||||
handler.authorizer = authorizer
|
||||
handler.max_login_attempts = 2 # Drop connection on each 2 incorrect attempts
|
||||
server = FTPServer(("127.0.0.1", PORT), handler)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
def run_mumble():
|
||||
handler = http.server.SimpleHTTPRequestHandler
|
||||
httpd = socketserver.TCPServer(("127.0.0.1", PORT), handler)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
class TestFTPScanner(unittest.TestCase):
|
||||
def test_closed_port(self):
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', 31337)
|
||||
print(result)
|
||||
#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, 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")
|
||||
p.terminate()
|
||||
|
||||
def test_anonymous_login(self):
|
||||
temp_dir = mkdtemp()
|
||||
p = multiprocessing.Process(target=run_anonymous_ftp, args=(temp_dir,))
|
||||
p.start()
|
||||
sleep(5)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', PORT)
|
||||
print(result)
|
||||
self.assertEqual(result['login'], None, "Should be True")
|
||||
self.assertEqual(result['password'], None, "Should be True")
|
||||
p.terminate()
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
def test_bruteforce(self):
|
||||
temp_dir = mkdtemp()
|
||||
p = multiprocessing.Process(target=run_bruteforce_ftp, args=(temp_dir,))
|
||||
p.start()
|
||||
sleep(5)
|
||||
scanner = FTPScanner(timeout=10, credentials=TEST_CREDS)
|
||||
result = scanner.scan_address('127.0.0.1', PORT)
|
||||
print(result)
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -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