milenium 0.5.2__tar.gz → 0.5.4__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.5.2 → milenium-0.5.4}/PKG-INFO +3 -2
- milenium-0.5.4/milenium/cli.py +227 -0
- milenium-0.5.4/milenium/modules/neon_db.py +78 -0
- milenium-0.5.4/milenium/tui.py +245 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/PKG-INFO +3 -2
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/SOURCES.txt +1 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/requires.txt +1 -0
- {milenium-0.5.2 → milenium-0.5.4}/pyproject.toml +7 -2
- milenium-0.5.2/milenium/cli.py +0 -309
- milenium-0.5.2/milenium/tui.py +0 -235
- {milenium-0.5.2 → milenium-0.5.4}/README.md +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/__init__.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/__init__.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/aggregator.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/breach.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/breach_local.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/dehashed.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/dns_history.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/domain.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/dorker.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/email.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/email_validator.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/exif_extractor.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/github_search.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/gravatar.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/hibp.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/instagram_osint.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/ip.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/ip_geo_advanced.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/ip_reputation.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/leakcheck.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/logger.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/password_pattern.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/permutator.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/pgp_lookup.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/phone.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/pwned_passwords.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/report.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/reverse_whois.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/shodan_lite.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/social.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/telegram_osint.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/tg_parser.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/twitter_osint.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/username.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/vk_osint.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/wayback.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium/modules/wifi_geolocation.py +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/dependency_links.txt +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/entry_points.txt +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/milenium.egg-info/top_level.txt +0 -0
- {milenium-0.5.2 → milenium-0.5.4}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: milenium
|
|
3
|
-
Version: 0.5.
|
|
4
|
-
Summary: OSINT
|
|
3
|
+
Version: 0.5.4
|
|
4
|
+
Summary: OSINT toolkit
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: requests
|
|
@@ -11,6 +11,7 @@ Requires-Dist: beautifulsoup4
|
|
|
11
11
|
Requires-Dist: rich
|
|
12
12
|
Requires-Dist: click
|
|
13
13
|
Requires-Dist: python-whois
|
|
14
|
+
Requires-Dist: psycopg2-binary
|
|
14
15
|
|
|
15
16
|
# milenium
|
|
16
17
|
# exiftool
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import json
|
|
3
|
+
import click
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from milenium.modules.logger import TeeLogger
|
|
7
|
+
from milenium.modules import (
|
|
8
|
+
username, email, phone, ip, domain, breach, social, report,
|
|
9
|
+
dorker, pwned_passwords, breach_local, hibp, dehashed, leakcheck,
|
|
10
|
+
aggregator, shodan_lite, ip_reputation, dns_history, telegram_osint,
|
|
11
|
+
neon_db,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _init_logger():
|
|
18
|
+
logger = TeeLogger("milenium.log", mode="a")
|
|
19
|
+
sys.stdout = logger
|
|
20
|
+
return logger
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _save_neon(command, target, module, data):
|
|
24
|
+
try:
|
|
25
|
+
neon_db.init()
|
|
26
|
+
neon_db.save(command, target, module, data)
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@click.group()
|
|
32
|
+
def main():
|
|
33
|
+
"""milenium — OSINT toolkit"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@main.command()
|
|
38
|
+
@click.argument("nick")
|
|
39
|
+
@click.option("--permutations", default=30)
|
|
40
|
+
@click.option("--dorks/--no-dorks", default=True)
|
|
41
|
+
def user(nick, permutations, dorks):
|
|
42
|
+
"""Глубокий поиск по нику"""
|
|
43
|
+
logger = _init_logger()
|
|
44
|
+
results = username.check(nick, permutations=permutations)
|
|
45
|
+
_save_neon("user", nick, "username", results)
|
|
46
|
+
|
|
47
|
+
table = Table(title=f"Username: {nick}")
|
|
48
|
+
table.add_column("Вариация")
|
|
49
|
+
table.add_column("Сайт")
|
|
50
|
+
table.add_column("Статус")
|
|
51
|
+
table.add_column("URL")
|
|
52
|
+
found = 0
|
|
53
|
+
for variant, sites in results.items():
|
|
54
|
+
for site, info in sites.items():
|
|
55
|
+
if info["found"]:
|
|
56
|
+
table.add_row(variant, site, "[green]FOUND[/green]", info["url"])
|
|
57
|
+
found += 1
|
|
58
|
+
console.print(table)
|
|
59
|
+
console.print(f"[bold green]Найдено: {found}[/bold green]")
|
|
60
|
+
|
|
61
|
+
if dorks:
|
|
62
|
+
d = dorker.search_nick(nick, num=10)
|
|
63
|
+
for q, links in d.items():
|
|
64
|
+
console.print(f"\n[bold]{q}[/bold]")
|
|
65
|
+
for link in links:
|
|
66
|
+
console.print(f" {link}")
|
|
67
|
+
|
|
68
|
+
logger.close()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@main.command()
|
|
72
|
+
@click.argument("mail")
|
|
73
|
+
def mail(mail):
|
|
74
|
+
"""Проверка email"""
|
|
75
|
+
logger = _init_logger()
|
|
76
|
+
results = email.check(mail)
|
|
77
|
+
_save_neon("mail", mail, "email", results)
|
|
78
|
+
for k, v in results.items():
|
|
79
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
80
|
+
logger.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@main.command()
|
|
84
|
+
@click.argument("number")
|
|
85
|
+
def phone_cmd(number):
|
|
86
|
+
"""Инфа по номеру"""
|
|
87
|
+
logger = _init_logger()
|
|
88
|
+
results = phone.check(number)
|
|
89
|
+
_save_neon("phone", number, "phone", results)
|
|
90
|
+
for k, v in results.items():
|
|
91
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
92
|
+
logger.close()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@main.command()
|
|
96
|
+
@click.argument("address")
|
|
97
|
+
def ip_cmd(address):
|
|
98
|
+
"""Гео, ASN, Shodan, репутация"""
|
|
99
|
+
logger = _init_logger()
|
|
100
|
+
results = ip.check(address)
|
|
101
|
+
results["shodan"] = shodan_lite.check_ip(address)
|
|
102
|
+
results["reputation"] = ip_reputation.check(address)
|
|
103
|
+
_save_neon("ip", address, "ip", results)
|
|
104
|
+
for k, v in results.items():
|
|
105
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
106
|
+
logger.close()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@main.command()
|
|
110
|
+
@click.argument("host")
|
|
111
|
+
def dom(host):
|
|
112
|
+
"""WHOIS, DNS, crt.sh"""
|
|
113
|
+
logger = _init_logger()
|
|
114
|
+
results = domain.check(host)
|
|
115
|
+
results["crt"] = dns_history.check(host)
|
|
116
|
+
_save_neon("dom", host, "domain", results)
|
|
117
|
+
for k, v in results.items():
|
|
118
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
119
|
+
logger.close()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@main.command()
|
|
123
|
+
@click.argument("query")
|
|
124
|
+
def leaks(query):
|
|
125
|
+
"""HIBP утечки"""
|
|
126
|
+
logger = _init_logger()
|
|
127
|
+
results = breach.check(query)
|
|
128
|
+
_save_neon("leaks", query, "breach", results)
|
|
129
|
+
for k, v in results.items():
|
|
130
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
131
|
+
logger.close()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@main.command()
|
|
135
|
+
@click.argument("nick")
|
|
136
|
+
def social_cmd(nick):
|
|
137
|
+
"""Поиск соцсетей"""
|
|
138
|
+
logger = _init_logger()
|
|
139
|
+
results = social.check(nick)
|
|
140
|
+
_save_neon("social", nick, "social", results)
|
|
141
|
+
for site, info in results.items():
|
|
142
|
+
status = "FOUND" if info["found"] else "NO"
|
|
143
|
+
console.print(f"[bold]{site}:[/bold] {status} — {info['url']}")
|
|
144
|
+
logger.close()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@main.command()
|
|
148
|
+
@click.argument("password")
|
|
149
|
+
def pwned(password):
|
|
150
|
+
"""Проверка пароля"""
|
|
151
|
+
logger = _init_logger()
|
|
152
|
+
res = pwned_passwords.check_password(password)
|
|
153
|
+
if res.get("pwned"):
|
|
154
|
+
console.print(f"[red]ПАРОЛЬ В УТЕЧКАХ[/red] — {res['count']} раз")
|
|
155
|
+
elif "error" in res:
|
|
156
|
+
console.print(f"[yellow]Ошибка:[/yellow] {res['error']}")
|
|
157
|
+
else:
|
|
158
|
+
console.print("[green]Не найден[/green]")
|
|
159
|
+
logger.close()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@main.command()
|
|
163
|
+
@click.argument("channel")
|
|
164
|
+
def telegram(channel):
|
|
165
|
+
"""Парсинг TG-канала"""
|
|
166
|
+
logger = _init_logger()
|
|
167
|
+
res = telegram_osint.check_channel(channel)
|
|
168
|
+
for k, v in res.items():
|
|
169
|
+
console.print(f"[bold]{k}:[/bold] {v}")
|
|
170
|
+
logger.close()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@main.command()
|
|
174
|
+
@click.option("--email", default=None)
|
|
175
|
+
@click.option("--username", default=None)
|
|
176
|
+
@click.option("--phone", default=None)
|
|
177
|
+
@click.option("--password", default=None)
|
|
178
|
+
@click.option("--db-path", default=None)
|
|
179
|
+
def full(email, username, phone, password, db_path):
|
|
180
|
+
"""Агрегатор"""
|
|
181
|
+
logger = _init_logger()
|
|
182
|
+
res = aggregator.run(email=email, username=username, phone=phone,
|
|
183
|
+
password=password, db_path=db_path)
|
|
184
|
+
_save_neon("full", email or username or phone, "aggregator", res)
|
|
185
|
+
console.print(json.dumps(res, indent=2, ensure_ascii=False))
|
|
186
|
+
logger.close()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@main.command()
|
|
190
|
+
@click.option("--target", default=None)
|
|
191
|
+
@click.option("--module", default=None)
|
|
192
|
+
@click.option("--limit", default=50)
|
|
193
|
+
@click.option("--stats", is_flag=True)
|
|
194
|
+
def db_neon(target, module, limit, stats):
|
|
195
|
+
"""NeonDB работа"""
|
|
196
|
+
logger = _init_logger()
|
|
197
|
+
try:
|
|
198
|
+
neon_db.init()
|
|
199
|
+
if stats:
|
|
200
|
+
s = neon_db.stats()
|
|
201
|
+
console.print(f"[bold]Всего:[/bold] {s['total']}")
|
|
202
|
+
for m, c in s["by_module"].items():
|
|
203
|
+
console.print(f" [red]{m}[/red]: {c}")
|
|
204
|
+
else:
|
|
205
|
+
rows = neon_db.query(target=target, module=module, limit=limit)
|
|
206
|
+
table = Table(title="NeonDB")
|
|
207
|
+
table.add_column("Время")
|
|
208
|
+
table.add_column("Команда")
|
|
209
|
+
table.add_column("Цель")
|
|
210
|
+
table.add_column("Модуль")
|
|
211
|
+
for r in rows:
|
|
212
|
+
table.add_row(r["ts"][:19], r["command"], r["target"], r["module"])
|
|
213
|
+
console.print(table)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
console.print(f"[bold red]Ошибка Neon:[/bold red] {e}")
|
|
216
|
+
logger.close()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@main.command()
|
|
220
|
+
def tui():
|
|
221
|
+
"""TUI-интерфейс"""
|
|
222
|
+
from milenium.tui import interactive
|
|
223
|
+
interactive()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import psycopg2
|
|
4
|
+
import psycopg2.extras
|
|
5
|
+
|
|
6
|
+
DATABASE_URL = os.environ.get("DATABASE_URL", "postgresql://neondb_owner:npg_JNAjnTaz0UK5@ep-twilight-bar-b1ofdyot-pooler.c-5.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _conn():
|
|
10
|
+
if not DATABASE_URL:
|
|
11
|
+
raise RuntimeError("DATABASE_URL not set")
|
|
12
|
+
return psycopg2.connect(DATABASE_URL, sslmode="require")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def init():
|
|
16
|
+
with _conn() as conn:
|
|
17
|
+
with conn.cursor() as cur:
|
|
18
|
+
cur.execute("""
|
|
19
|
+
CREATE TABLE IF NOT EXISTS results (
|
|
20
|
+
id SERIAL PRIMARY KEY,
|
|
21
|
+
ts TIMESTAMP DEFAULT NOW(),
|
|
22
|
+
command TEXT,
|
|
23
|
+
target TEXT,
|
|
24
|
+
module TEXT,
|
|
25
|
+
data JSONB
|
|
26
|
+
)
|
|
27
|
+
""")
|
|
28
|
+
cur.execute("""
|
|
29
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
30
|
+
id SERIAL PRIMARY KEY,
|
|
31
|
+
ts TIMESTAMP DEFAULT NOW(),
|
|
32
|
+
queries INTEGER,
|
|
33
|
+
results INTEGER
|
|
34
|
+
)
|
|
35
|
+
""")
|
|
36
|
+
conn.commit()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def save(command, target, module, data):
|
|
40
|
+
with _conn() as conn:
|
|
41
|
+
with conn.cursor() as cur:
|
|
42
|
+
cur.execute(
|
|
43
|
+
"INSERT INTO results (command, target, module, data) VALUES (%s, %s, %s, %s)",
|
|
44
|
+
(command, target, module, json.dumps(data, ensure_ascii=False)),
|
|
45
|
+
)
|
|
46
|
+
conn.commit()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def query(target=None, module=None, limit=50):
|
|
50
|
+
sql = "SELECT ts, command, target, module, data FROM results WHERE 1=1"
|
|
51
|
+
params = []
|
|
52
|
+
if target:
|
|
53
|
+
sql += " AND target LIKE %s"
|
|
54
|
+
params.append(f"%{target}%")
|
|
55
|
+
if module:
|
|
56
|
+
sql += " AND module = %s"
|
|
57
|
+
params.append(module)
|
|
58
|
+
sql += " ORDER BY id DESC LIMIT %s"
|
|
59
|
+
params.append(limit)
|
|
60
|
+
with _conn() as conn:
|
|
61
|
+
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
|
62
|
+
cur.execute(sql, params)
|
|
63
|
+
rows = cur.fetchall()
|
|
64
|
+
return [
|
|
65
|
+
{"ts": str(r["ts"]), "command": r["command"], "target": r["target"],
|
|
66
|
+
"module": r["module"], "data": r["data"]}
|
|
67
|
+
for r in rows
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def stats():
|
|
72
|
+
with _conn() as conn:
|
|
73
|
+
with conn.cursor() as cur:
|
|
74
|
+
cur.execute("SELECT COUNT(*) FROM results")
|
|
75
|
+
total = cur.fetchone()[0]
|
|
76
|
+
cur.execute("SELECT module, COUNT(*) FROM results GROUP BY module")
|
|
77
|
+
by_module = dict(cur.fetchall())
|
|
78
|
+
return {"total": total, "by_module": by_module}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import shlex
|
|
3
|
+
import io
|
|
4
|
+
import contextlib
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.layout import Layout
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.align import Align
|
|
12
|
+
from rich.live import Live
|
|
13
|
+
from rich.prompt import Prompt
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
LOGO = r"""
|
|
18
|
+
███╗ ███╗██╗██╗ ███████╗███╗ ██╗██╗██╗ ██╗███╗ ███╗
|
|
19
|
+
████╗ ████║██║██║ ██╔════╝████╗ ██║██║██║ ██║████╗ ████║
|
|
20
|
+
██╔████╔██║██║██║ █████╗ ██╔██╗ ██║██║██║ ██║██╔████╔██║
|
|
21
|
+
██║╚██╔╝██║██║██║ ██╔══╝ ██║╚██╗██║██║██║ ██║██║╚██╔╝██║
|
|
22
|
+
██║ ╚═╝ ██║██║███████╗███████╗██║ ╚████║██║╚██████╔╝██║ ╚═╝ ██║
|
|
23
|
+
╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
COMMANDS = {
|
|
27
|
+
"ПОИСК": [
|
|
28
|
+
("user <ник>", "Ник + вариации + Dorks"),
|
|
29
|
+
("social_cmd <ник>", "Соцсети по нику"),
|
|
30
|
+
("email_check <email>", "SMTP + Gravatar + PGP"),
|
|
31
|
+
("mail <email>", "MX, HIBP, Gravatar"),
|
|
32
|
+
("phone_cmd <номер>", "Оператор, страна"),
|
|
33
|
+
("telegram <канал>", "TG-канал"),
|
|
34
|
+
],
|
|
35
|
+
"УТЕЧКИ": [
|
|
36
|
+
("pwned <пароль>", "Проверка пароля"),
|
|
37
|
+
("leaks <email>", "HIBP утечки"),
|
|
38
|
+
("pwgen --name N", "Генератор паролей"),
|
|
39
|
+
("full --email E", "Агрегатор"),
|
|
40
|
+
],
|
|
41
|
+
"СЕТЬ": [
|
|
42
|
+
("ip_cmd <IP>", "Гео, Shodan, репутация"),
|
|
43
|
+
("dom <домен>", "WHOIS, DNS, crt.sh"),
|
|
44
|
+
("whois_rev <домен>", "Reverse WHOIS"),
|
|
45
|
+
("ssl_info <host>", "SSL-сертификат"),
|
|
46
|
+
("robots <домен>", "robots.txt"),
|
|
47
|
+
],
|
|
48
|
+
"ФАЙЛЫ": [
|
|
49
|
+
("exif <path>", "EXIF из фото"),
|
|
50
|
+
("eml <path>", "Заголовки письма"),
|
|
51
|
+
("wayback <url>", "Wayback Machine"),
|
|
52
|
+
],
|
|
53
|
+
"NEON DB": [
|
|
54
|
+
("db_neon --stats", "Статистика"),
|
|
55
|
+
("db_neon --target X", "Поиск"),
|
|
56
|
+
],
|
|
57
|
+
"СИСТЕМА": [
|
|
58
|
+
("help", "Список команд"),
|
|
59
|
+
("clear", "Очистить вывод"),
|
|
60
|
+
("exit / q", "Выход"),
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
SESSION = {"start": datetime.now(), "queries": 0, "results": 0, "last": []}
|
|
65
|
+
OUTPUT_LOG = []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def push_output(text):
|
|
69
|
+
ts = datetime.now().strftime("%H:%M:%S")
|
|
70
|
+
OUTPUT_LOG.append(f"[red]{ts}[/red] [white]{text}[/white]")
|
|
71
|
+
if len(OUTPUT_LOG) > 50:
|
|
72
|
+
OUTPUT_LOG.pop(0)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def build_layout():
|
|
76
|
+
layout = Layout()
|
|
77
|
+
layout.split_column(
|
|
78
|
+
Layout(name="header", size=9),
|
|
79
|
+
Layout(name="body", ratio=2),
|
|
80
|
+
Layout(name="output", size=12),
|
|
81
|
+
Layout(name="input", size=3),
|
|
82
|
+
Layout(name="footer", size=3),
|
|
83
|
+
)
|
|
84
|
+
layout["body"].split_row(
|
|
85
|
+
Layout(name="left", ratio=3),
|
|
86
|
+
Layout(name="center", ratio=2),
|
|
87
|
+
Layout(name="right", ratio=1),
|
|
88
|
+
)
|
|
89
|
+
return layout
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def render_header():
|
|
93
|
+
logo_text = Text(LOGO, style="bold red")
|
|
94
|
+
banner = Text.from_markup(
|
|
95
|
+
"[bold white]MILENIUM[/bold white] [red]|[/red] [bold white]@VexxDev200[/bold white] "
|
|
96
|
+
"[red]|[/red] [bold white]TG:[/bold white] [red]@milenium[/red] "
|
|
97
|
+
"[red]|[/red] [bold white]STATUS:[/bold white] [green]● ONLINE[/green]"
|
|
98
|
+
)
|
|
99
|
+
return Panel(Align.center(logo_text + banner), border_style="red",
|
|
100
|
+
title="[bold red]MILENIUM[/bold red]", subtitle="[red]v0.6.1[/red]")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def render_commands():
|
|
104
|
+
from rich.columns import Columns
|
|
105
|
+
left_lines, right_lines = [], []
|
|
106
|
+
items = []
|
|
107
|
+
for cat, cmds in COMMANDS.items():
|
|
108
|
+
items.append(("cat", cat))
|
|
109
|
+
for cmd, desc in cmds:
|
|
110
|
+
items.append(("cmd", (cmd, desc)))
|
|
111
|
+
half = len(items) // 2
|
|
112
|
+
for i, (kind, val) in enumerate(items):
|
|
113
|
+
target = left_lines if i < half else right_lines
|
|
114
|
+
if kind == "cat":
|
|
115
|
+
target.append(Text(f"━━ {val} ━━", style="bold red"))
|
|
116
|
+
else:
|
|
117
|
+
cmd, desc = val
|
|
118
|
+
t = Text()
|
|
119
|
+
t.append(cmd, style="bold white")
|
|
120
|
+
t.append(" — ", style="red")
|
|
121
|
+
t.append(desc, style="red")
|
|
122
|
+
target.append(t)
|
|
123
|
+
left = Text("\n").join(left_lines)
|
|
124
|
+
right = Text("\n").join(right_lines)
|
|
125
|
+
cols = Columns([left, right], expand=True, equal=True)
|
|
126
|
+
return Panel(cols, title="[bold red]КОМАНДЫ[/bold red]", border_style="red")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def render_session():
|
|
130
|
+
lines = []
|
|
131
|
+
uptime = datetime.now() - SESSION["start"]
|
|
132
|
+
mins, secs = divmod(int(uptime.total_seconds()), 60)
|
|
133
|
+
lines.append(f"[white]Uptime:[/white] [red]{mins:02d}:{secs:02d}[/red]")
|
|
134
|
+
lines.append(f"[white]Запросов:[/white] [red]{SESSION['queries']}[/red]")
|
|
135
|
+
lines.append(f"[white]Найдено:[/white] [red]{SESSION['results']}[/red]")
|
|
136
|
+
lines.append("")
|
|
137
|
+
lines.append("[bold red]━━ ПОСЛЕДНИЕ ━━[/bold red]")
|
|
138
|
+
if SESSION["last"]:
|
|
139
|
+
lines.extend(SESSION["last"])
|
|
140
|
+
else:
|
|
141
|
+
lines.append("[white]Пока ничего.[/white]")
|
|
142
|
+
return Panel("\n".join(lines), title="[bold red]СЕССИЯ[/bold red]", border_style="red")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def render_status():
|
|
146
|
+
from milenium.modules import neon_db
|
|
147
|
+
lines = []
|
|
148
|
+
lines.append("[bold red]━━ NEON DB ━━[/bold red]")
|
|
149
|
+
try:
|
|
150
|
+
s = neon_db.stats()
|
|
151
|
+
lines.append(f"[white]Записей:[/white] [red]{s['total']}[/red]")
|
|
152
|
+
for m, c in list(s["by_module"].items())[:3]:
|
|
153
|
+
lines.append(f"[white]{m}[/white] [red]{c}[/red]")
|
|
154
|
+
except Exception:
|
|
155
|
+
lines.append("[red]NOT CONNECTED[/red]")
|
|
156
|
+
lines.append("")
|
|
157
|
+
lines.append("[bold red]━━ МОДУЛИ ━━[/bold red]")
|
|
158
|
+
for m in ["whois", "dns", "requests", "rich", "click"]:
|
|
159
|
+
lines.append(f"[white]{m}[/white] [green]OK[/green]")
|
|
160
|
+
return Panel("\n".join(lines), title="[bold red]СТАТУС[/bold red]", border_style="red")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def render_output():
|
|
164
|
+
lines = ["[bold red]━━ ПРОЦЕСС ━━[/bold red]"]
|
|
165
|
+
if OUTPUT_LOG:
|
|
166
|
+
lines.extend(OUTPUT_LOG[-10:])
|
|
167
|
+
else:
|
|
168
|
+
lines.append("[white]Введи команду ниже.[/white]")
|
|
169
|
+
return Panel("\n".join(lines), title="[bold red]ВЫВОД[/bold red]", border_style="red")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def render_input():
|
|
173
|
+
return Panel(
|
|
174
|
+
"[bold red]milenium[/bold red] [white]@[/white] [red]VexxDev200[/red] [white]~[/white] [bold white]введи команду[/bold white]",
|
|
175
|
+
title="[bold red]ВВОД[/bold red]", border_style="red")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def render_footer():
|
|
179
|
+
return Panel(Align.center(
|
|
180
|
+
"[bold red]milenium[/bold red] [white]@[/white] [red]VexxDev200[/red] "
|
|
181
|
+
"[white]|[/white] [green]SYSTEM READY[/green] "
|
|
182
|
+
"[white]|[/white] [red]Ctrl+C для выхода[/red]"), border_style="red")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def draw():
|
|
186
|
+
layout = build_layout()
|
|
187
|
+
layout["header"].update(render_header())
|
|
188
|
+
layout["left"].update(render_commands())
|
|
189
|
+
layout["center"].update(render_session())
|
|
190
|
+
layout["right"].update(render_status())
|
|
191
|
+
layout["output"].update(render_output())
|
|
192
|
+
layout["input"].update(render_input())
|
|
193
|
+
layout["footer"].update(render_footer())
|
|
194
|
+
return layout
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _handle_builtin(cmd):
|
|
198
|
+
c = cmd.strip().lower()
|
|
199
|
+
if c == "help":
|
|
200
|
+
push_output("список команд в левой панели")
|
|
201
|
+
return True
|
|
202
|
+
if c == "clear":
|
|
203
|
+
OUTPUT_LOG.clear()
|
|
204
|
+
push_output("вывод очищен")
|
|
205
|
+
return True
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def interactive():
|
|
210
|
+
console.clear()
|
|
211
|
+
push_output("сессия запущена")
|
|
212
|
+
push_output("введи 'help'")
|
|
213
|
+
with Live(draw(), refresh_per_second=4) as live:
|
|
214
|
+
while True:
|
|
215
|
+
try:
|
|
216
|
+
live.stop()
|
|
217
|
+
cmd = Prompt.ask("[bold red]milenium[/bold red] [white]@[/white] [red]VexxDev200[/red] [white]~[/white]")
|
|
218
|
+
live.start()
|
|
219
|
+
if cmd.strip().lower() in ("exit", "quit", "q"):
|
|
220
|
+
break
|
|
221
|
+
if not cmd.strip():
|
|
222
|
+
live.update(draw())
|
|
223
|
+
continue
|
|
224
|
+
SESSION["queries"] += 1
|
|
225
|
+
push_output(f"выполняю: {cmd}")
|
|
226
|
+
if _handle_builtin(cmd):
|
|
227
|
+
live.update(draw())
|
|
228
|
+
continue
|
|
229
|
+
from milenium.cli import main
|
|
230
|
+
args = shlex.split(cmd)
|
|
231
|
+
buf = io.StringIO()
|
|
232
|
+
try:
|
|
233
|
+
with contextlib.redirect_stdout(buf):
|
|
234
|
+
main(args, standalone_mode=False)
|
|
235
|
+
for line in buf.getvalue().splitlines():
|
|
236
|
+
if line.strip():
|
|
237
|
+
push_output(line)
|
|
238
|
+
except SystemExit:
|
|
239
|
+
pass
|
|
240
|
+
except Exception as e:
|
|
241
|
+
push_output(f"[red]ошибка: {e}[/red]")
|
|
242
|
+
live.update(draw())
|
|
243
|
+
except KeyboardInterrupt:
|
|
244
|
+
break
|
|
245
|
+
console.clear()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: milenium
|
|
3
|
-
Version: 0.5.
|
|
4
|
-
Summary: OSINT
|
|
3
|
+
Version: 0.5.4
|
|
4
|
+
Summary: OSINT toolkit
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: requests
|
|
@@ -11,6 +11,7 @@ Requires-Dist: beautifulsoup4
|
|
|
11
11
|
Requires-Dist: rich
|
|
12
12
|
Requires-Dist: click
|
|
13
13
|
Requires-Dist: python-whois
|
|
14
|
+
Requires-Dist: psycopg2-binary
|
|
14
15
|
|
|
15
16
|
# milenium
|
|
16
17
|
# exiftool
|
|
@@ -29,6 +29,7 @@ milenium/modules/ip_geo_advanced.py
|
|
|
29
29
|
milenium/modules/ip_reputation.py
|
|
30
30
|
milenium/modules/leakcheck.py
|
|
31
31
|
milenium/modules/logger.py
|
|
32
|
+
milenium/modules/neon_db.py
|
|
32
33
|
milenium/modules/password_pattern.py
|
|
33
34
|
milenium/modules/permutator.py
|
|
34
35
|
milenium/modules/pgp_lookup.py
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "milenium"
|
|
3
|
-
version = "0.5.
|
|
4
|
-
description = "OSINT
|
|
3
|
+
version = "0.5.4"
|
|
4
|
+
description = "OSINT toolkit"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9"
|
|
7
7
|
dependencies = [
|
|
@@ -12,10 +12,15 @@ dependencies = [
|
|
|
12
12
|
"rich",
|
|
13
13
|
"click",
|
|
14
14
|
"python-whois",
|
|
15
|
+
"psycopg2-binary",
|
|
15
16
|
]
|
|
16
17
|
|
|
17
18
|
[project.scripts]
|
|
18
19
|
milenium = "milenium.cli:main"
|
|
19
20
|
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
23
|
+
build-backend = "setuptools.build_meta"
|
|
24
|
+
|
|
20
25
|
[tool.setuptools.packages.find]
|
|
21
26
|
include = ["milenium*"]
|