aiptx 2.0.7__py3-none-any.whl
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.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +2933 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +341 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interactive_shell.py +559 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/local_tool_installer.py +1467 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2427 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +53 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/runtime/vps.py +830 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
aipt_v2/recon/dns.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT DNS Analyzer
|
|
3
|
+
|
|
4
|
+
DNS enumeration and analysis.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import socket
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# dns.resolver import with fallback
|
|
18
|
+
try:
|
|
19
|
+
import dns.resolver
|
|
20
|
+
import dns.zone
|
|
21
|
+
import dns.query
|
|
22
|
+
DNS_AVAILABLE = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
DNS_AVAILABLE = False
|
|
25
|
+
logger.warning("dnspython not installed. Install with: pip install dnspython")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class DNSRecord:
|
|
30
|
+
"""DNS record"""
|
|
31
|
+
record_type: str # A, AAAA, MX, NS, TXT, CNAME, etc.
|
|
32
|
+
name: str
|
|
33
|
+
value: str
|
|
34
|
+
ttl: int = 0
|
|
35
|
+
priority: int = 0 # For MX records
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> dict:
|
|
38
|
+
return {
|
|
39
|
+
"type": self.record_type,
|
|
40
|
+
"name": self.name,
|
|
41
|
+
"value": self.value,
|
|
42
|
+
"ttl": self.ttl,
|
|
43
|
+
"priority": self.priority,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class DNSResult:
|
|
49
|
+
"""DNS analysis results"""
|
|
50
|
+
domain: str
|
|
51
|
+
records: list[DNSRecord] = field(default_factory=list)
|
|
52
|
+
nameservers: list[str] = field(default_factory=list)
|
|
53
|
+
mail_servers: list[str] = field(default_factory=list)
|
|
54
|
+
ip_addresses: list[str] = field(default_factory=list)
|
|
55
|
+
|
|
56
|
+
# Security analysis
|
|
57
|
+
has_spf: bool = False
|
|
58
|
+
has_dmarc: bool = False
|
|
59
|
+
has_dkim: bool = False
|
|
60
|
+
zone_transfer_possible: bool = False
|
|
61
|
+
|
|
62
|
+
# Additional info
|
|
63
|
+
registrar: str = ""
|
|
64
|
+
creation_date: str = ""
|
|
65
|
+
expiration_date: str = ""
|
|
66
|
+
|
|
67
|
+
analyzed_at: datetime = field(default_factory=datetime.utcnow)
|
|
68
|
+
|
|
69
|
+
def get_records_by_type(self, record_type: str) -> list[DNSRecord]:
|
|
70
|
+
"""Get records of a specific type"""
|
|
71
|
+
return [r for r in self.records if r.record_type == record_type]
|
|
72
|
+
|
|
73
|
+
def to_dict(self) -> dict:
|
|
74
|
+
return {
|
|
75
|
+
"domain": self.domain,
|
|
76
|
+
"nameservers": self.nameservers,
|
|
77
|
+
"mail_servers": self.mail_servers,
|
|
78
|
+
"ip_addresses": self.ip_addresses,
|
|
79
|
+
"records_count": len(self.records),
|
|
80
|
+
"security": {
|
|
81
|
+
"has_spf": self.has_spf,
|
|
82
|
+
"has_dmarc": self.has_dmarc,
|
|
83
|
+
"has_dkim": self.has_dkim,
|
|
84
|
+
"zone_transfer_possible": self.zone_transfer_possible,
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class DNSAnalyzer:
|
|
90
|
+
"""
|
|
91
|
+
DNS enumeration and analysis.
|
|
92
|
+
|
|
93
|
+
Features:
|
|
94
|
+
- Record enumeration (A, AAAA, MX, NS, TXT, CNAME, SOA)
|
|
95
|
+
- Security configuration check (SPF, DMARC, DKIM)
|
|
96
|
+
- Zone transfer attempt
|
|
97
|
+
- Mail server discovery
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
analyzer = DNSAnalyzer()
|
|
101
|
+
result = await analyzer.analyze("example.com")
|
|
102
|
+
|
|
103
|
+
print(f"Nameservers: {result.nameservers}")
|
|
104
|
+
print(f"Has SPF: {result.has_spf}")
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
# Record types to query
|
|
108
|
+
RECORD_TYPES = ["A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA"]
|
|
109
|
+
|
|
110
|
+
def __init__(self, timeout: float = 5.0, nameserver: str = "8.8.8.8"):
|
|
111
|
+
if not DNS_AVAILABLE:
|
|
112
|
+
raise ImportError("dnspython is required. Install with: pip install dnspython")
|
|
113
|
+
|
|
114
|
+
self.timeout = timeout
|
|
115
|
+
self.nameserver = nameserver
|
|
116
|
+
self._resolver = dns.resolver.Resolver()
|
|
117
|
+
self._resolver.nameservers = [nameserver]
|
|
118
|
+
self._resolver.timeout = timeout
|
|
119
|
+
self._resolver.lifetime = timeout
|
|
120
|
+
|
|
121
|
+
async def analyze(self, domain: str) -> DNSResult:
|
|
122
|
+
"""
|
|
123
|
+
Analyze DNS configuration for a domain.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
domain: Target domain
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
DNSResult with discovered records
|
|
130
|
+
"""
|
|
131
|
+
result = DNSResult(domain=domain)
|
|
132
|
+
|
|
133
|
+
# Query all record types
|
|
134
|
+
for record_type in self.RECORD_TYPES:
|
|
135
|
+
records = await self._query_records(domain, record_type)
|
|
136
|
+
result.records.extend(records)
|
|
137
|
+
|
|
138
|
+
# Extract specific info
|
|
139
|
+
if record_type == "A":
|
|
140
|
+
result.ip_addresses.extend([r.value for r in records])
|
|
141
|
+
elif record_type == "NS":
|
|
142
|
+
result.nameservers.extend([r.value for r in records])
|
|
143
|
+
elif record_type == "MX":
|
|
144
|
+
result.mail_servers.extend([r.value for r in records])
|
|
145
|
+
|
|
146
|
+
# Check security records
|
|
147
|
+
result.has_spf = await self._check_spf(domain)
|
|
148
|
+
result.has_dmarc = await self._check_dmarc(domain)
|
|
149
|
+
result.has_dkim = await self._check_dkim(domain)
|
|
150
|
+
|
|
151
|
+
# Attempt zone transfer
|
|
152
|
+
result.zone_transfer_possible = await self._try_zone_transfer(domain, result.nameservers)
|
|
153
|
+
|
|
154
|
+
return result
|
|
155
|
+
|
|
156
|
+
async def _query_records(self, domain: str, record_type: str) -> list[DNSRecord]:
|
|
157
|
+
"""Query DNS records of a specific type"""
|
|
158
|
+
records = []
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
# Run in thread pool for async
|
|
162
|
+
loop = asyncio.get_event_loop()
|
|
163
|
+
answers = await loop.run_in_executor(
|
|
164
|
+
None,
|
|
165
|
+
lambda: self._resolver.resolve(domain, record_type)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
for rdata in answers:
|
|
169
|
+
record = DNSRecord(
|
|
170
|
+
record_type=record_type,
|
|
171
|
+
name=domain,
|
|
172
|
+
value=str(rdata),
|
|
173
|
+
ttl=answers.ttl,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Extract MX priority
|
|
177
|
+
if record_type == "MX":
|
|
178
|
+
record.priority = rdata.preference
|
|
179
|
+
record.value = str(rdata.exchange)
|
|
180
|
+
|
|
181
|
+
records.append(record)
|
|
182
|
+
|
|
183
|
+
except dns.resolver.NXDOMAIN:
|
|
184
|
+
logger.debug(f"Domain {domain} does not exist")
|
|
185
|
+
except dns.resolver.NoAnswer:
|
|
186
|
+
logger.debug(f"No {record_type} records for {domain}")
|
|
187
|
+
except dns.resolver.NoNameservers:
|
|
188
|
+
logger.debug(f"No nameservers for {domain}")
|
|
189
|
+
except Exception as e:
|
|
190
|
+
logger.debug(f"DNS query error ({record_type}): {e}")
|
|
191
|
+
|
|
192
|
+
return records
|
|
193
|
+
|
|
194
|
+
async def _check_spf(self, domain: str) -> bool:
|
|
195
|
+
"""Check if SPF record exists"""
|
|
196
|
+
records = await self._query_records(domain, "TXT")
|
|
197
|
+
return any("v=spf1" in r.value.lower() for r in records)
|
|
198
|
+
|
|
199
|
+
async def _check_dmarc(self, domain: str) -> bool:
|
|
200
|
+
"""Check if DMARC record exists"""
|
|
201
|
+
records = await self._query_records(f"_dmarc.{domain}", "TXT")
|
|
202
|
+
return any("v=dmarc1" in r.value.lower() for r in records)
|
|
203
|
+
|
|
204
|
+
async def _check_dkim(self, domain: str, selectors: list[str] = None) -> bool:
|
|
205
|
+
"""Check if DKIM record exists"""
|
|
206
|
+
selectors = selectors or ["default", "google", "selector1", "selector2", "s1", "s2"]
|
|
207
|
+
|
|
208
|
+
for selector in selectors:
|
|
209
|
+
records = await self._query_records(f"{selector}._domainkey.{domain}", "TXT")
|
|
210
|
+
if records:
|
|
211
|
+
return True
|
|
212
|
+
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
async def _try_zone_transfer(self, domain: str, nameservers: list[str]) -> bool:
|
|
216
|
+
"""Attempt zone transfer"""
|
|
217
|
+
for ns in nameservers[:3]: # Try first 3 nameservers
|
|
218
|
+
try:
|
|
219
|
+
ns_ip = socket.gethostbyname(ns.rstrip("."))
|
|
220
|
+
|
|
221
|
+
# Run in thread pool
|
|
222
|
+
loop = asyncio.get_event_loop()
|
|
223
|
+
zone = await loop.run_in_executor(
|
|
224
|
+
None,
|
|
225
|
+
lambda: dns.zone.from_xfr(dns.query.xfr(ns_ip, domain, timeout=5))
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if zone:
|
|
229
|
+
logger.warning(f"Zone transfer successful from {ns}!")
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
except Exception:
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
async def analyze_dns(domain: str) -> DNSResult:
|
|
239
|
+
"""Quick DNS analysis"""
|
|
240
|
+
analyzer = DNSAnalyzer()
|
|
241
|
+
return await analyzer.analyze(domain)
|
aipt_v2/recon/osint.py
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT OSINT Collector
|
|
3
|
+
|
|
4
|
+
Open-source intelligence gathering from public sources.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class OSINTResult:
|
|
22
|
+
"""OSINT collection results"""
|
|
23
|
+
target: str
|
|
24
|
+
target_type: str # domain, email, ip, username
|
|
25
|
+
|
|
26
|
+
# Discovered data
|
|
27
|
+
emails: list[str] = field(default_factory=list)
|
|
28
|
+
usernames: list[str] = field(default_factory=list)
|
|
29
|
+
social_profiles: list[dict] = field(default_factory=list)
|
|
30
|
+
breaches: list[dict] = field(default_factory=list)
|
|
31
|
+
related_domains: list[str] = field(default_factory=list)
|
|
32
|
+
ip_info: dict = field(default_factory=dict)
|
|
33
|
+
whois_data: dict = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
# Metadata
|
|
36
|
+
sources_checked: list[str] = field(default_factory=list)
|
|
37
|
+
collected_at: datetime = field(default_factory=datetime.utcnow)
|
|
38
|
+
|
|
39
|
+
def to_dict(self) -> dict:
|
|
40
|
+
return {
|
|
41
|
+
"target": self.target,
|
|
42
|
+
"target_type": self.target_type,
|
|
43
|
+
"emails_found": len(self.emails),
|
|
44
|
+
"usernames_found": len(self.usernames),
|
|
45
|
+
"social_profiles": len(self.social_profiles),
|
|
46
|
+
"breaches": len(self.breaches),
|
|
47
|
+
"sources_checked": self.sources_checked,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class OSINTCollector:
|
|
52
|
+
"""
|
|
53
|
+
OSINT data collector from public sources.
|
|
54
|
+
|
|
55
|
+
Sources:
|
|
56
|
+
- Have I Been Pwned (breach data)
|
|
57
|
+
- Hunter.io (email discovery)
|
|
58
|
+
- Shodan (IP/host info)
|
|
59
|
+
- Social media presence checks
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
collector = OSINTCollector()
|
|
63
|
+
result = await collector.collect_domain("example.com")
|
|
64
|
+
|
|
65
|
+
print(f"Emails found: {result.emails}")
|
|
66
|
+
print(f"Breaches: {len(result.breaches)}")
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# Social media platforms to check
|
|
70
|
+
SOCIAL_PLATFORMS = {
|
|
71
|
+
"twitter": "https://twitter.com/{username}",
|
|
72
|
+
"github": "https://github.com/{username}",
|
|
73
|
+
"linkedin": "https://linkedin.com/in/{username}",
|
|
74
|
+
"instagram": "https://instagram.com/{username}",
|
|
75
|
+
"facebook": "https://facebook.com/{username}",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
hibp_api_key: str = "",
|
|
81
|
+
hunter_api_key: str = "",
|
|
82
|
+
shodan_api_key: str = "",
|
|
83
|
+
):
|
|
84
|
+
self.hibp_api_key = hibp_api_key
|
|
85
|
+
self.hunter_api_key = hunter_api_key
|
|
86
|
+
self.shodan_api_key = shodan_api_key
|
|
87
|
+
|
|
88
|
+
async def collect_domain(self, domain: str) -> OSINTResult:
|
|
89
|
+
"""
|
|
90
|
+
Collect OSINT for a domain.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
domain: Target domain
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
OSINTResult with discovered data
|
|
97
|
+
"""
|
|
98
|
+
result = OSINTResult(target=domain, target_type="domain")
|
|
99
|
+
|
|
100
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
101
|
+
tasks = [
|
|
102
|
+
self._discover_emails(client, domain, result),
|
|
103
|
+
self._check_related_domains(client, domain, result),
|
|
104
|
+
self._get_whois(client, domain, result),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
108
|
+
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
async def collect_email(self, email: str) -> OSINTResult:
|
|
112
|
+
"""
|
|
113
|
+
Collect OSINT for an email address.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
email: Target email
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
OSINTResult with discovered data
|
|
120
|
+
"""
|
|
121
|
+
result = OSINTResult(target=email, target_type="email")
|
|
122
|
+
|
|
123
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
124
|
+
tasks = [
|
|
125
|
+
self._check_breaches(client, email, result),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
async def collect_username(self, username: str) -> OSINTResult:
|
|
133
|
+
"""
|
|
134
|
+
Collect OSINT for a username.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
username: Target username
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
OSINTResult with social profile checks
|
|
141
|
+
"""
|
|
142
|
+
result = OSINTResult(target=username, target_type="username")
|
|
143
|
+
|
|
144
|
+
async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
|
|
145
|
+
await self._check_social_profiles(client, username, result)
|
|
146
|
+
|
|
147
|
+
return result
|
|
148
|
+
|
|
149
|
+
async def collect_ip(self, ip: str) -> OSINTResult:
|
|
150
|
+
"""
|
|
151
|
+
Collect OSINT for an IP address.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
ip: Target IP address
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
OSINTResult with IP intelligence
|
|
158
|
+
"""
|
|
159
|
+
result = OSINTResult(target=ip, target_type="ip")
|
|
160
|
+
|
|
161
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
162
|
+
await self._get_ip_info(client, ip, result)
|
|
163
|
+
|
|
164
|
+
return result
|
|
165
|
+
|
|
166
|
+
async def _discover_emails(
|
|
167
|
+
self,
|
|
168
|
+
client: httpx.AsyncClient,
|
|
169
|
+
domain: str,
|
|
170
|
+
result: OSINTResult,
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Discover emails associated with domain"""
|
|
173
|
+
result.sources_checked.append("email_discovery")
|
|
174
|
+
|
|
175
|
+
# Hunter.io if API key available
|
|
176
|
+
if self.hunter_api_key:
|
|
177
|
+
try:
|
|
178
|
+
response = await client.get(
|
|
179
|
+
"https://api.hunter.io/v2/domain-search",
|
|
180
|
+
params={
|
|
181
|
+
"domain": domain,
|
|
182
|
+
"api_key": self.hunter_api_key,
|
|
183
|
+
},
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if response.status_code == 200:
|
|
187
|
+
data = response.json()
|
|
188
|
+
for email in data.get("data", {}).get("emails", []):
|
|
189
|
+
if email.get("value"):
|
|
190
|
+
result.emails.append(email["value"])
|
|
191
|
+
|
|
192
|
+
except Exception as e:
|
|
193
|
+
logger.debug(f"Hunter.io error: {e}")
|
|
194
|
+
|
|
195
|
+
# Fallback: search common patterns
|
|
196
|
+
common_prefixes = [
|
|
197
|
+
"info", "contact", "admin", "support", "sales", "hello",
|
|
198
|
+
"mail", "webmaster", "postmaster", "abuse",
|
|
199
|
+
]
|
|
200
|
+
for prefix in common_prefixes:
|
|
201
|
+
result.emails.append(f"{prefix}@{domain}")
|
|
202
|
+
|
|
203
|
+
async def _check_breaches(
|
|
204
|
+
self,
|
|
205
|
+
client: httpx.AsyncClient,
|
|
206
|
+
email: str,
|
|
207
|
+
result: OSINTResult,
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Check for breaches using HIBP"""
|
|
210
|
+
result.sources_checked.append("hibp")
|
|
211
|
+
|
|
212
|
+
if not self.hibp_api_key:
|
|
213
|
+
logger.debug("No HIBP API key, skipping breach check")
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
response = await client.get(
|
|
218
|
+
f"https://haveibeenpwned.com/api/v3/breachedaccount/{email}",
|
|
219
|
+
headers={
|
|
220
|
+
"hibp-api-key": self.hibp_api_key,
|
|
221
|
+
"User-Agent": "AIPT-Scanner",
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if response.status_code == 200:
|
|
226
|
+
for breach in response.json():
|
|
227
|
+
result.breaches.append({
|
|
228
|
+
"name": breach.get("Name"),
|
|
229
|
+
"date": breach.get("BreachDate"),
|
|
230
|
+
"domain": breach.get("Domain"),
|
|
231
|
+
"data_classes": breach.get("DataClasses", []),
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.debug(f"HIBP error: {e}")
|
|
236
|
+
|
|
237
|
+
async def _check_social_profiles(
|
|
238
|
+
self,
|
|
239
|
+
client: httpx.AsyncClient,
|
|
240
|
+
username: str,
|
|
241
|
+
result: OSINTResult,
|
|
242
|
+
) -> None:
|
|
243
|
+
"""Check social media presence"""
|
|
244
|
+
result.sources_checked.append("social_media")
|
|
245
|
+
|
|
246
|
+
async def check_platform(platform: str, url_template: str) -> Optional[dict]:
|
|
247
|
+
url = url_template.format(username=username)
|
|
248
|
+
try:
|
|
249
|
+
response = await client.get(url)
|
|
250
|
+
if response.status_code == 200:
|
|
251
|
+
return {
|
|
252
|
+
"platform": platform,
|
|
253
|
+
"url": url,
|
|
254
|
+
"exists": True,
|
|
255
|
+
}
|
|
256
|
+
except Exception:
|
|
257
|
+
pass
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
tasks = [
|
|
261
|
+
check_platform(platform, url)
|
|
262
|
+
for platform, url in self.SOCIAL_PLATFORMS.items()
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
results = await asyncio.gather(*tasks)
|
|
266
|
+
for profile in results:
|
|
267
|
+
if profile:
|
|
268
|
+
result.social_profiles.append(profile)
|
|
269
|
+
|
|
270
|
+
async def _check_related_domains(
|
|
271
|
+
self,
|
|
272
|
+
client: httpx.AsyncClient,
|
|
273
|
+
domain: str,
|
|
274
|
+
result: OSINTResult,
|
|
275
|
+
) -> None:
|
|
276
|
+
"""Find related domains"""
|
|
277
|
+
result.sources_checked.append("related_domains")
|
|
278
|
+
|
|
279
|
+
# Check common TLDs
|
|
280
|
+
base = domain.rsplit(".", 1)[0]
|
|
281
|
+
tlds = [".com", ".net", ".org", ".io", ".co", ".app", ".dev"]
|
|
282
|
+
|
|
283
|
+
for tld in tlds:
|
|
284
|
+
test_domain = base + tld
|
|
285
|
+
if test_domain != domain:
|
|
286
|
+
try:
|
|
287
|
+
import socket
|
|
288
|
+
socket.gethostbyname(test_domain)
|
|
289
|
+
result.related_domains.append(test_domain)
|
|
290
|
+
except socket.gaierror:
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
async def _get_ip_info(
|
|
294
|
+
self,
|
|
295
|
+
client: httpx.AsyncClient,
|
|
296
|
+
ip: str,
|
|
297
|
+
result: OSINTResult,
|
|
298
|
+
) -> None:
|
|
299
|
+
"""Get IP intelligence"""
|
|
300
|
+
result.sources_checked.append("ip_info")
|
|
301
|
+
|
|
302
|
+
# ipinfo.io (no API key needed for basic info)
|
|
303
|
+
try:
|
|
304
|
+
response = await client.get(f"https://ipinfo.io/{ip}/json")
|
|
305
|
+
if response.status_code == 200:
|
|
306
|
+
result.ip_info = response.json()
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.debug(f"ipinfo error: {e}")
|
|
309
|
+
|
|
310
|
+
# Shodan if API key available
|
|
311
|
+
if self.shodan_api_key:
|
|
312
|
+
try:
|
|
313
|
+
response = await client.get(
|
|
314
|
+
f"https://api.shodan.io/shodan/host/{ip}",
|
|
315
|
+
params={"key": self.shodan_api_key},
|
|
316
|
+
)
|
|
317
|
+
if response.status_code == 200:
|
|
318
|
+
shodan_data = response.json()
|
|
319
|
+
result.ip_info["shodan"] = {
|
|
320
|
+
"ports": shodan_data.get("ports", []),
|
|
321
|
+
"os": shodan_data.get("os"),
|
|
322
|
+
"hostnames": shodan_data.get("hostnames", []),
|
|
323
|
+
}
|
|
324
|
+
except Exception as e:
|
|
325
|
+
logger.debug(f"Shodan error: {e}")
|
|
326
|
+
|
|
327
|
+
async def _get_whois(
|
|
328
|
+
self,
|
|
329
|
+
client: httpx.AsyncClient,
|
|
330
|
+
domain: str,
|
|
331
|
+
result: OSINTResult,
|
|
332
|
+
) -> None:
|
|
333
|
+
"""Get WHOIS information"""
|
|
334
|
+
result.sources_checked.append("whois")
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
import whois
|
|
338
|
+
w = whois.whois(domain)
|
|
339
|
+
result.whois_data = {
|
|
340
|
+
"registrar": w.registrar,
|
|
341
|
+
"creation_date": str(w.creation_date) if w.creation_date else None,
|
|
342
|
+
"expiration_date": str(w.expiration_date) if w.expiration_date else None,
|
|
343
|
+
"name_servers": w.name_servers if w.name_servers else [],
|
|
344
|
+
}
|
|
345
|
+
except ImportError:
|
|
346
|
+
logger.debug("python-whois not installed")
|
|
347
|
+
except Exception as e:
|
|
348
|
+
logger.debug(f"WHOIS error: {e}")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# Convenience functions
|
|
352
|
+
async def osint_domain(domain: str) -> OSINTResult:
|
|
353
|
+
"""Quick OSINT for domain"""
|
|
354
|
+
collector = OSINTCollector()
|
|
355
|
+
return await collector.collect_domain(domain)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
async def osint_email(email: str, hibp_key: str = "") -> OSINTResult:
|
|
359
|
+
"""Quick OSINT for email"""
|
|
360
|
+
collector = OSINTCollector(hibp_api_key=hibp_key)
|
|
361
|
+
return await collector.collect_email(email)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
async def osint_username(username: str) -> OSINTResult:
|
|
365
|
+
"""Quick social media presence check"""
|
|
366
|
+
collector = OSINTCollector()
|
|
367
|
+
return await collector.collect_username(username)
|