milenium 0.4.2__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {milenium-0.4.2 → milenium-0.5.0}/PKG-INFO +2 -1
- milenium-0.5.0/README.md +2 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/cli.py +67 -0
- milenium-0.5.0/milenium/modules/aggregator.py +41 -0
- milenium-0.5.0/milenium/modules/email_validator.py +22 -0
- milenium-0.5.0/milenium/modules/exif_extractor.py +22 -0
- milenium-0.5.0/milenium/modules/github_search.py +29 -0
- milenium-0.5.0/milenium/modules/gravatar.py +13 -0
- milenium-0.5.0/milenium/modules/instagram_osint.py +25 -0
- milenium-0.5.0/milenium/modules/ip_geo_advanced.py +16 -0
- milenium-0.5.0/milenium/modules/password_pattern.py +32 -0
- milenium-0.5.0/milenium/modules/pgp_lookup.py +10 -0
- milenium-0.5.0/milenium/modules/reverse_whois.py +24 -0
- milenium-0.5.0/milenium/modules/tg_parser.py +20 -0
- milenium-0.5.0/milenium/modules/twitter_osint.py +13 -0
- milenium-0.5.0/milenium/modules/vk_osint.py +23 -0
- milenium-0.5.0/milenium/modules/wayback.py +18 -0
- milenium-0.5.0/milenium/modules/wifi_geolocation.py +27 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/tui.py +17 -1
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/PKG-INFO +2 -1
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/SOURCES.txt +15 -1
- {milenium-0.4.2 → milenium-0.5.0}/pyproject.toml +1 -1
- milenium-0.4.2/README.md +0 -1
- milenium-0.4.2/milenium/modules/aggregator.py +0 -26
- {milenium-0.4.2 → milenium-0.5.0}/milenium/__init__.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/__init__.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/breach.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/breach_local.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/dehashed.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/dns_history.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/domain.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/dorker.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/email.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/hibp.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/ip.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/ip_reputation.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/leakcheck.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/logger.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/permutator.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/phone.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/pwned_passwords.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/report.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/shodan_lite.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/social.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/telegram_osint.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium/modules/username.py +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/dependency_links.txt +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/entry_points.txt +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/requires.txt +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/milenium.egg-info/top_level.txt +0 -0
- {milenium-0.4.2 → milenium-0.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: milenium
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: OSINT / dox / deanon toolkit
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -13,3 +13,4 @@ Requires-Dist: click
|
|
|
13
13
|
Requires-Dist: python-whois
|
|
14
14
|
|
|
15
15
|
# milenium
|
|
16
|
+
# exiftool
|
milenium-0.5.0/README.md
ADDED
|
@@ -84,6 +84,73 @@ def user(nick, permutations, dorks):
|
|
|
84
84
|
console.print(f"[bold cyan]Лог:[/bold cyan] {logger.path}")
|
|
85
85
|
logger.close()
|
|
86
86
|
|
|
87
|
+
@main.command()
|
|
88
|
+
@click.argument("email")
|
|
89
|
+
def email_check(email):
|
|
90
|
+
"""SMTP-валидация email + Gravatar + PGP + GitHub"""
|
|
91
|
+
logger = _init_logger()
|
|
92
|
+
from milenium.modules import email_validator, gravatar, pgp_lookup, github_search
|
|
93
|
+
res = {
|
|
94
|
+
"smtp": email_validator.check(email),
|
|
95
|
+
"gravatar": gravatar.check(email),
|
|
96
|
+
"pgp": pgp_lookup.check(email),
|
|
97
|
+
"github": github_search.check_email(email),
|
|
98
|
+
}
|
|
99
|
+
for k, v in res.items():
|
|
100
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
101
|
+
logger.close()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@main.command()
|
|
105
|
+
@click.argument("image_path")
|
|
106
|
+
def exif(image_path):
|
|
107
|
+
"""Извлечь EXIF/GPS из фото"""
|
|
108
|
+
logger = _init_logger()
|
|
109
|
+
from milenium.modules import exif_extractor
|
|
110
|
+
res = exif_extractor.check(image_path)
|
|
111
|
+
for k, v in res.items():
|
|
112
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
113
|
+
logger.close()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@main.command()
|
|
117
|
+
@click.argument("domain")
|
|
118
|
+
def whois_rev(domain):
|
|
119
|
+
"""Reverse WHOIS по домену"""
|
|
120
|
+
logger = _init_logger()
|
|
121
|
+
from milenium.modules import reverse_whois
|
|
122
|
+
res = reverse_whois.check(domain)
|
|
123
|
+
for d in res.get("domains", []):
|
|
124
|
+
console.print(d)
|
|
125
|
+
logger.close()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@main.command()
|
|
129
|
+
@click.option("--name", default=None)
|
|
130
|
+
@click.option("--surname", default=None)
|
|
131
|
+
@click.option("--year", default=None)
|
|
132
|
+
@click.option("--nick", default=None)
|
|
133
|
+
def pwgen(name, surname, year, nick):
|
|
134
|
+
"""Генератор вероятных паролей по данным жертвы"""
|
|
135
|
+
logger = _init_logger()
|
|
136
|
+
from milenium.modules import password_pattern
|
|
137
|
+
res = password_pattern.generate(name=name, surname=surname, birth_year=year, nick=nick)
|
|
138
|
+
for p in res["passwords"][:50]:
|
|
139
|
+
console.print(p)
|
|
140
|
+
console.print(f"\n[bold]Всего:[/bold] {res['count']}")
|
|
141
|
+
logger.close()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@main.command()
|
|
145
|
+
@click.argument("image_path")
|
|
146
|
+
def exif_cmd(image_path):
|
|
147
|
+
"""EXIF из фото"""
|
|
148
|
+
logger = _init_logger()
|
|
149
|
+
from milenium.modules import exif_extractor
|
|
150
|
+
res = exif_extractor.check(image_path)
|
|
151
|
+
for k, v in res.items():
|
|
152
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
153
|
+
logger.close()
|
|
87
154
|
|
|
88
155
|
@main.command()
|
|
89
156
|
@click.argument("mail")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from milenium.modules import (
|
|
2
|
+
pwned_passwords, breach_local, hibp, dehashed, leakcheck,
|
|
3
|
+
email_validator, gravatar, pgp_lookup, github_search,
|
|
4
|
+
wayback, reverse_whois, password_pattern,
|
|
5
|
+
ip_geo_advanced, tg_parser,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
def run(email=None, username=None, phone=None, password=None, db_path=None, domain=None):
|
|
9
|
+
out = {}
|
|
10
|
+
|
|
11
|
+
if password:
|
|
12
|
+
out["pwned_passwords"] = pwned_passwords.check_password(password)
|
|
13
|
+
|
|
14
|
+
if email:
|
|
15
|
+
out["hibp"] = hibp.check_email(email)
|
|
16
|
+
out["dehashed_email"] = dehashed.search(f'email:"{email}"')
|
|
17
|
+
out["leakcheck_email"] = leakcheck.search(email)
|
|
18
|
+
out["gravatar"] = gravatar.check(email)
|
|
19
|
+
out["pgp"] = pgp_lookup.check(email)
|
|
20
|
+
out["github_email"] = github_search.check_email(email)
|
|
21
|
+
out["email_valid"] = email_validator.check(email)
|
|
22
|
+
|
|
23
|
+
if username:
|
|
24
|
+
out["dehashed_username"] = dehashed.search(f'username:"{username}"')
|
|
25
|
+
out["leakcheck_username"] = leakcheck.search(username)
|
|
26
|
+
out["github_user"] = github_search.check_username(username)
|
|
27
|
+
out["telegram"] = tg_parser.channel_info(username)
|
|
28
|
+
|
|
29
|
+
if phone:
|
|
30
|
+
out["leakcheck_phone"] = leakcheck.search(phone)
|
|
31
|
+
|
|
32
|
+
if domain:
|
|
33
|
+
out["wayback"] = wayback.check(f"http://{domain}")
|
|
34
|
+
out["reverse_whois"] = reverse_whois.check(domain)
|
|
35
|
+
|
|
36
|
+
if db_path:
|
|
37
|
+
q = email or username or phone
|
|
38
|
+
if q:
|
|
39
|
+
out["breach_local"] = breach_local.search(q, db_path)
|
|
40
|
+
|
|
41
|
+
return out
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import smtplib
|
|
2
|
+
import dns.resolver
|
|
3
|
+
|
|
4
|
+
def check(email):
|
|
5
|
+
"""Проверка существования email через SMTP RCPT TO (без отправки письма)."""
|
|
6
|
+
domain = email.split("@")[1]
|
|
7
|
+
try:
|
|
8
|
+
mx_records = dns.resolver.resolve(domain, "MX")
|
|
9
|
+
mx_host = str(sorted(mx_records, key=lambda r: r.preference)[0].exchange).rstrip(".")
|
|
10
|
+
except Exception as e:
|
|
11
|
+
return {"error": f"MX lookup failed: {e}"}
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
server = smtplib.SMTP(timeout=10)
|
|
15
|
+
server.connect(mx_host, 25)
|
|
16
|
+
server.helo("milenium.local")
|
|
17
|
+
server.mail("check@milenium.local")
|
|
18
|
+
code, _ = server.rcpt(email)
|
|
19
|
+
server.quit()
|
|
20
|
+
return {"exists": code in (250, 251), "code": code, "mx": mx_host}
|
|
21
|
+
except Exception as e:
|
|
22
|
+
return {"error": str(e), "mx": mx_host}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def check(image_path):
|
|
5
|
+
"""Требует exiftool в PATH. Установка: apt install libimage-exiftool-perl / choco install exiftool."""
|
|
6
|
+
if not os.path.exists(image_path):
|
|
7
|
+
return {"error": "file not found"}
|
|
8
|
+
try:
|
|
9
|
+
r = subprocess.run(
|
|
10
|
+
["exiftool", "-j", image_path],
|
|
11
|
+
capture_output=True, text=True, timeout=20
|
|
12
|
+
)
|
|
13
|
+
if r.returncode != 0:
|
|
14
|
+
return {"error": r.stderr.strip() or "exiftool failed"}
|
|
15
|
+
import json
|
|
16
|
+
data = json.loads(r.stdout)[0]
|
|
17
|
+
keys = ["GPSLatitude", "GPSLongitude", "Model", "Make", "DateTimeOriginal", "Software"]
|
|
18
|
+
return {k: data.get(k) for k in keys if data.get(k)}
|
|
19
|
+
except FileNotFoundError:
|
|
20
|
+
return {"error": "exiftool not installed"}
|
|
21
|
+
except Exception as e:
|
|
22
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check_email(email):
|
|
4
|
+
try:
|
|
5
|
+
r = requests.get(
|
|
6
|
+
f"https://api.github.com/search/users?q={email}+in:email",
|
|
7
|
+
headers={"Accept": "application/vnd.github+json"},
|
|
8
|
+
timeout=10,
|
|
9
|
+
)
|
|
10
|
+
if r.status_code == 200:
|
|
11
|
+
items = r.json().get("items", [])
|
|
12
|
+
return {"users": [{"login": u["login"], "url": u["html_url"]} for u in items]}
|
|
13
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
14
|
+
except Exception as e:
|
|
15
|
+
return {"error": str(e)}
|
|
16
|
+
|
|
17
|
+
def check_username(username):
|
|
18
|
+
try:
|
|
19
|
+
r = requests.get(
|
|
20
|
+
f"https://api.github.com/search/users?q={username}+in:login",
|
|
21
|
+
headers={"Accept": "application/vnd.github+json"},
|
|
22
|
+
timeout=10,
|
|
23
|
+
)
|
|
24
|
+
if r.status_code == 200:
|
|
25
|
+
items = r.json().get("items", [])
|
|
26
|
+
return {"users": [{"login": u["login"], "url": u["html_url"]} for u in items]}
|
|
27
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
28
|
+
except Exception as e:
|
|
29
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
def check(email):
|
|
5
|
+
h = hashlib.md5(email.strip().lower().encode()).hexdigest()
|
|
6
|
+
url = f"https://www.gravatar.com/avatar/{h}?d=404"
|
|
7
|
+
try:
|
|
8
|
+
r = requests.get(url, timeout=10)
|
|
9
|
+
if r.status_code == 200:
|
|
10
|
+
return {"exists": True, "url": f"https://www.gravatar.com/{h}"}
|
|
11
|
+
return {"exists": False}
|
|
12
|
+
except Exception as e:
|
|
13
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
def check(username):
|
|
5
|
+
"""Парсит публичный профиль Instagram без API (может ломаться при защите)."""
|
|
6
|
+
headers = {"User-Agent": "Mozilla/5.0"}
|
|
7
|
+
try:
|
|
8
|
+
r = requests.get(f"https://www.instagram.com/{username}/", headers=headers, timeout=15)
|
|
9
|
+
if r.status_code != 200:
|
|
10
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
11
|
+
m = re.search(r'"edge_followed_by":\{"count":(\d+)\}', r.text)
|
|
12
|
+
followers = m.group(1) if m else None
|
|
13
|
+
m2 = re.search(r'"edge_follow":\{"count":(\d+)\}', r.text)
|
|
14
|
+
following = m2.group(1) if m2 else None
|
|
15
|
+
m3 = re.search(r'"biography":"(.*?)"', r.text)
|
|
16
|
+
bio = m3.group(1) if m3 else None
|
|
17
|
+
return {
|
|
18
|
+
"exists": True,
|
|
19
|
+
"followers": followers,
|
|
20
|
+
"following": following,
|
|
21
|
+
"bio": bio,
|
|
22
|
+
"url": f"https://instagram.com/{username}",
|
|
23
|
+
}
|
|
24
|
+
except Exception as e:
|
|
25
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(ip):
|
|
4
|
+
out = {}
|
|
5
|
+
try:
|
|
6
|
+
r = requests.get(f"https://ipapi.co/{ip}/json/", timeout=10).json()
|
|
7
|
+
out["ipapi"] = {k: r.get(k) for k in ["city", "region", "country_name", "org", "asn", "timezone"]}
|
|
8
|
+
except Exception as e:
|
|
9
|
+
out["ipapi_error"] = str(e)
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
r = requests.get(f"https://ipwho.is/{ip}", timeout=10).json()
|
|
13
|
+
out["ipwhois"] = {k: r.get(k) for k in ["city", "region", "country", "connection"]}
|
|
14
|
+
except Exception as e:
|
|
15
|
+
out["ipwhois_error"] = str(e)
|
|
16
|
+
return out
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
def generate(name=None, surname=None, birth_year=None, nick=None, limit=500):
|
|
5
|
+
"""Генерит вероятные пароли по данным жертвы."""
|
|
6
|
+
base = []
|
|
7
|
+
if name: base.append(name.lower())
|
|
8
|
+
if surname: base.append(surname.lower())
|
|
9
|
+
if nick: base.append(nick.lower())
|
|
10
|
+
if name and surname:
|
|
11
|
+
base.append(f"{name.lower()}{surname.lower()}")
|
|
12
|
+
base.append(f"{name.lower()}.{surname.lower()}")
|
|
13
|
+
base.append(f"{name.lower()}_{surname.lower()}")
|
|
14
|
+
|
|
15
|
+
suffixes = ["", "123", "1234", "12345", "2024", "2025", "!", "@", "#", "_"]
|
|
16
|
+
if birth_year:
|
|
17
|
+
suffixes += [str(birth_year), str(birth_year)[-2:], f"{birth_year}!"]
|
|
18
|
+
|
|
19
|
+
out = set()
|
|
20
|
+
for b in base:
|
|
21
|
+
for s in suffixes:
|
|
22
|
+
out.add(b + s)
|
|
23
|
+
out.add(b.capitalize() + s)
|
|
24
|
+
out.add(b.upper() + s)
|
|
25
|
+
|
|
26
|
+
# leet
|
|
27
|
+
leet_map = {"a": "4", "e": "3", "i": "1", "o": "0", "s": "5"}
|
|
28
|
+
for b in list(out)[:100]:
|
|
29
|
+
leet = "".join(leet_map.get(c, c) for c in b)
|
|
30
|
+
out.add(leet)
|
|
31
|
+
|
|
32
|
+
return {"passwords": list(out)[:limit], "count": len(out)}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(email):
|
|
4
|
+
try:
|
|
5
|
+
r = requests.get(f"https://keys.openpgp.org/vks/v1/by-email/{email}", timeout=10)
|
|
6
|
+
if r.status_code == 200:
|
|
7
|
+
return {"found": True, "key": r.text[:500]}
|
|
8
|
+
return {"found": False}
|
|
9
|
+
except Exception as e:
|
|
10
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from bs4 import BeautifulSoup
|
|
3
|
+
|
|
4
|
+
def check(query):
|
|
5
|
+
"""Парсит viewdns.info — бесплатно, без API-ключа."""
|
|
6
|
+
try:
|
|
7
|
+
r = requests.get(
|
|
8
|
+
"https://viewdns.info/reversewhois/",
|
|
9
|
+
params={"q": query},
|
|
10
|
+
headers={"User-Agent": "Mozilla/5.0"},
|
|
11
|
+
timeout=20,
|
|
12
|
+
)
|
|
13
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
|
14
|
+
table = soup.find("table", {"border": "1"})
|
|
15
|
+
if not table:
|
|
16
|
+
return {"domains": []}
|
|
17
|
+
domains = []
|
|
18
|
+
for row in table.find_all("tr")[1:]:
|
|
19
|
+
cols = row.find_all("td")
|
|
20
|
+
if cols:
|
|
21
|
+
domains.append(cols[0].text.strip())
|
|
22
|
+
return {"domains": domains[:100]}
|
|
23
|
+
except Exception as e:
|
|
24
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from bs4 import BeautifulSoup
|
|
3
|
+
|
|
4
|
+
def channel_info(username):
|
|
5
|
+
try:
|
|
6
|
+
r = requests.get(f"https://t.me/{username}", timeout=10)
|
|
7
|
+
if r.status_code != 200:
|
|
8
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
9
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
|
10
|
+
title = soup.find("meta", property="og:title")
|
|
11
|
+
desc = soup.find("meta", property="og:description")
|
|
12
|
+
members = soup.find("div", class_="tgme_page_extra")
|
|
13
|
+
return {
|
|
14
|
+
"title": title["content"] if title else None,
|
|
15
|
+
"description": desc["content"] if desc else None,
|
|
16
|
+
"extra": members.text.strip() if members else None,
|
|
17
|
+
"url": f"https://t.me/{username}",
|
|
18
|
+
}
|
|
19
|
+
except Exception as e:
|
|
20
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(username):
|
|
4
|
+
"""Через nitter-зеркало (может не работать, зеркала меняются)."""
|
|
5
|
+
mirrors = ["https://nitter.net", "https://nitter.privacydev.net"]
|
|
6
|
+
for base in mirrors:
|
|
7
|
+
try:
|
|
8
|
+
r = requests.get(f"{base}/{username}", timeout=10)
|
|
9
|
+
if r.status_code == 200 and "user-profile" in r.text:
|
|
10
|
+
return {"exists": True, "url": f"https://twitter.com/{username}", "mirror": base}
|
|
11
|
+
except Exception:
|
|
12
|
+
continue
|
|
13
|
+
return {"exists": False}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(user_id, token=""):
|
|
4
|
+
"""VK API: нужен сервисный токен. Без него вернёт ошибку."""
|
|
5
|
+
if not token:
|
|
6
|
+
return {"error": "no VK token"}
|
|
7
|
+
try:
|
|
8
|
+
r = requests.get(
|
|
9
|
+
"https://api.vk.com/method/users.get",
|
|
10
|
+
params={
|
|
11
|
+
"user_ids": user_id,
|
|
12
|
+
"fields": "followers_count,country,city,photo_max,bdate",
|
|
13
|
+
"access_token": token,
|
|
14
|
+
"v": "5.199",
|
|
15
|
+
},
|
|
16
|
+
timeout=15,
|
|
17
|
+
)
|
|
18
|
+
data = r.json()
|
|
19
|
+
if "response" in data:
|
|
20
|
+
return data["response"][0]
|
|
21
|
+
return {"error": data.get("error", {}).get("error_msg")}
|
|
22
|
+
except Exception as e:
|
|
23
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(url):
|
|
4
|
+
try:
|
|
5
|
+
r = requests.get(
|
|
6
|
+
"http://archive.org/wayback/available",
|
|
7
|
+
params={"url": url},
|
|
8
|
+
timeout=15,
|
|
9
|
+
)
|
|
10
|
+
if r.status_code == 200:
|
|
11
|
+
data = r.json()
|
|
12
|
+
snap = data.get("archived_snapshots", {}).get("closest")
|
|
13
|
+
if snap:
|
|
14
|
+
return {"available": True, "snapshot": snap["url"], "timestamp": snap["timestamp"]}
|
|
15
|
+
return {"available": False}
|
|
16
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
17
|
+
except Exception as e:
|
|
18
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
def check(bssid):
|
|
4
|
+
"""Wi-Fi геолокация через wigle.net (нужен API-токен)."""
|
|
5
|
+
try:
|
|
6
|
+
r = requests.get(
|
|
7
|
+
"https://api.wigle.net/api/v2/network/search",
|
|
8
|
+
params={"netid": bssid},
|
|
9
|
+
headers={"Authorization": "Basic YOUR_BASE64_TOKEN"},
|
|
10
|
+
timeout=15,
|
|
11
|
+
)
|
|
12
|
+
if r.status_code == 200:
|
|
13
|
+
data = r.json()
|
|
14
|
+
results = data.get("results", [])
|
|
15
|
+
if results:
|
|
16
|
+
first = results[0]
|
|
17
|
+
return {
|
|
18
|
+
"lat": first.get("trilat"),
|
|
19
|
+
"lon": first.get("trilong"),
|
|
20
|
+
"ssid": first.get("ssid"),
|
|
21
|
+
"country": first.get("country"),
|
|
22
|
+
"city": first.get("city"),
|
|
23
|
+
}
|
|
24
|
+
return {"found": False}
|
|
25
|
+
return {"error": f"HTTP {r.status_code}"}
|
|
26
|
+
except Exception as e:
|
|
27
|
+
return {"error": str(e)}
|
|
@@ -66,6 +66,7 @@ def build_layout():
|
|
|
66
66
|
layout.split_column(
|
|
67
67
|
Layout(name="header", size=10),
|
|
68
68
|
Layout(name="body"),
|
|
69
|
+
Layout(name="input", size=3),
|
|
69
70
|
Layout(name="footer", size=3),
|
|
70
71
|
)
|
|
71
72
|
layout["body"].split_row(
|
|
@@ -76,6 +77,14 @@ def build_layout():
|
|
|
76
77
|
return layout
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
def render_input():
|
|
81
|
+
return Panel(
|
|
82
|
+
"[bold red]milenium[/bold red] [white]@[/white] [red]VexxDev200[/red] [white]~[/white] [bold white]введи команду ниже[/bold white]",
|
|
83
|
+
title="[bold red]ВВОД[/bold red]",
|
|
84
|
+
border_style="red",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
79
88
|
def render_header():
|
|
80
89
|
logo = Text.from_markup(ASCII_LOGO)
|
|
81
90
|
banner = Text.from_markup(BANNER)
|
|
@@ -166,14 +175,21 @@ def draw():
|
|
|
166
175
|
|
|
167
176
|
def interactive():
|
|
168
177
|
console.clear()
|
|
169
|
-
|
|
178
|
+
# Live без screen=True — не стирает ввод
|
|
179
|
+
with Live(draw(), refresh_per_second=4) as live:
|
|
170
180
|
while True:
|
|
171
181
|
try:
|
|
182
|
+
# Сначала останавливаем Live, чтобы prompt был виден
|
|
183
|
+
live.stop()
|
|
172
184
|
cmd = Prompt.ask("[bold red]milenium[/bold red] [white]@[/white] [red]VexxDev200[/red] [white]~[/white]")
|
|
185
|
+
live.start()
|
|
186
|
+
|
|
173
187
|
if cmd.strip().lower() in ("exit", "quit", "q"):
|
|
174
188
|
break
|
|
175
189
|
if not cmd.strip():
|
|
190
|
+
live.update(draw())
|
|
176
191
|
continue
|
|
192
|
+
|
|
177
193
|
SESSION["queries"] += 1
|
|
178
194
|
import shlex
|
|
179
195
|
from milenium.cli import main
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: milenium
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: OSINT / dox / deanon toolkit
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -13,3 +13,4 @@ Requires-Dist: click
|
|
|
13
13
|
Requires-Dist: python-whois
|
|
14
14
|
|
|
15
15
|
# milenium
|
|
16
|
+
# exiftool
|
|
@@ -18,16 +18,30 @@ milenium/modules/dns_history.py
|
|
|
18
18
|
milenium/modules/domain.py
|
|
19
19
|
milenium/modules/dorker.py
|
|
20
20
|
milenium/modules/email.py
|
|
21
|
+
milenium/modules/email_validator.py
|
|
22
|
+
milenium/modules/exif_extractor.py
|
|
23
|
+
milenium/modules/github_search.py
|
|
24
|
+
milenium/modules/gravatar.py
|
|
21
25
|
milenium/modules/hibp.py
|
|
26
|
+
milenium/modules/instagram_osint.py
|
|
22
27
|
milenium/modules/ip.py
|
|
28
|
+
milenium/modules/ip_geo_advanced.py
|
|
23
29
|
milenium/modules/ip_reputation.py
|
|
24
30
|
milenium/modules/leakcheck.py
|
|
25
31
|
milenium/modules/logger.py
|
|
32
|
+
milenium/modules/password_pattern.py
|
|
26
33
|
milenium/modules/permutator.py
|
|
34
|
+
milenium/modules/pgp_lookup.py
|
|
27
35
|
milenium/modules/phone.py
|
|
28
36
|
milenium/modules/pwned_passwords.py
|
|
29
37
|
milenium/modules/report.py
|
|
38
|
+
milenium/modules/reverse_whois.py
|
|
30
39
|
milenium/modules/shodan_lite.py
|
|
31
40
|
milenium/modules/social.py
|
|
32
41
|
milenium/modules/telegram_osint.py
|
|
33
|
-
milenium/modules/
|
|
42
|
+
milenium/modules/tg_parser.py
|
|
43
|
+
milenium/modules/twitter_osint.py
|
|
44
|
+
milenium/modules/username.py
|
|
45
|
+
milenium/modules/vk_osint.py
|
|
46
|
+
milenium/modules/wayback.py
|
|
47
|
+
milenium/modules/wifi_geolocation.py
|
milenium-0.4.2/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# milenium
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from milenium.modules import pwned_passwords, breach_local, hibp, dehashed, leakcheck
|
|
2
|
-
|
|
3
|
-
def run(email=None, username=None, phone=None, password=None, db_path=None):
|
|
4
|
-
out = {}
|
|
5
|
-
|
|
6
|
-
if password:
|
|
7
|
-
out["pwned_passwords"] = pwned_passwords.check_password(password)
|
|
8
|
-
|
|
9
|
-
if email:
|
|
10
|
-
out["hibp"] = hibp.check_email(email)
|
|
11
|
-
out["dehashed_email"] = dehashed.search(f'email:"{email}"')
|
|
12
|
-
out["leakcheck_email"] = leakcheck.search(email)
|
|
13
|
-
|
|
14
|
-
if username:
|
|
15
|
-
out["dehashed_username"] = dehashed.search(f'username:"{username}"')
|
|
16
|
-
out["leakcheck_username"] = leakcheck.search(username)
|
|
17
|
-
|
|
18
|
-
if phone:
|
|
19
|
-
out["leakcheck_phone"] = leakcheck.search(phone)
|
|
20
|
-
|
|
21
|
-
if db_path:
|
|
22
|
-
q = email or username or phone
|
|
23
|
-
if q:
|
|
24
|
-
out["breach_local"] = breach_local.search(q, db_path)
|
|
25
|
-
|
|
26
|
-
return out
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|