phonsint 0.1.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.
- phonsint-0.1.0/LICENSE +21 -0
- phonsint-0.1.0/PKG-INFO +189 -0
- phonsint-0.1.0/README.md +151 -0
- phonsint-0.1.0/phonsint/__init__.py +5 -0
- phonsint-0.1.0/phonsint/__main__.py +150 -0
- phonsint-0.1.0/phonsint/core/__init__.py +3 -0
- phonsint-0.1.0/phonsint/core/helpers.py +74 -0
- phonsint-0.1.0/phonsint/core/impersonate.py +97 -0
- phonsint-0.1.0/phonsint/core/orchestrator.py +176 -0
- phonsint-0.1.0/phonsint/core/parser.py +60 -0
- phonsint-0.1.0/phonsint/core/result.py +276 -0
- phonsint-0.1.0/phonsint/modules/__init__.py +3 -0
- phonsint-0.1.0/phonsint/modules/auth/__init__.py +1 -0
- phonsint-0.1.0/phonsint/modules/auth/microsoft.py +133 -0
- phonsint-0.1.0/phonsint/modules/crm/__init__.py +0 -0
- phonsint-0.1.0/phonsint/modules/crm/zoho.py +129 -0
- phonsint-0.1.0/phonsint/modules/shopping/__init__.py +1 -0
- phonsint-0.1.0/phonsint/modules/shopping/amazon.py +95 -0
- phonsint-0.1.0/phonsint/modules/social/__init__.py +1 -0
- phonsint-0.1.0/phonsint/modules/social/facebook.py +243 -0
- phonsint-0.1.0/phonsint/modules/social/instagram.py +227 -0
- phonsint-0.1.0/pyproject.toml +95 -0
phonsint-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kaif
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
phonsint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phonsint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-throughput silent phone number OSINT suite for platform footprinting, profile enrichment, and masked email discovery
|
|
5
|
+
Keywords: osint,phone-osint,reconnaissance,security,threat-intelligence,footprinting,identity-enrichment,metadata-extraction
|
|
6
|
+
Author-email: Kaif <kaifcodec@gmail.com>
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Information Technology
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: phonenumbers>=8.13.0
|
|
25
|
+
Requires-Dist: httpx[http2]>=0.27.0,<0.29.0
|
|
26
|
+
Requires-Dist: curl_cffi>=0.7.0,<1.0.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: colorama>=0.4.0,<1.0.0
|
|
29
|
+
Requires-Dist: pytest>=7.0.0 ; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-cov ; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff ; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy ; extra == "dev"
|
|
33
|
+
Project-URL: Bug Tracker, https://github.com/kaifcodec/phonsint/issues
|
|
34
|
+
Project-URL: Homepage, https://github.com/kaifcodec/phonsint
|
|
35
|
+
Project-URL: Repository, https://github.com/kaifcodec/phonsint.git
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
|
|
38
|
+
# phonsint
|
|
39
|
+
<img width="1713" height="420" alt="phonsint-banner" src="https://github.com/user-attachments/assets/a76a73a2-8aac-4813-a265-6802b2230eb0" />
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="https://img.shields.io/badge/Version-0.1.0-blueviolet?style=for-the-badge&logo=github" />
|
|
43
|
+
<img src="https://img.shields.io/badge/Tested%20on-Linux%20%7C%20macOS%20%7C%20Windows%20%7C%20Termux-black?style=for-the-badge" />
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<strong>High-Throughput Silent Phone Number OSINT & Identity Footprinting Suite</strong><br>
|
|
48
|
+
<em>Uncover account registrations, masked emails, display names, and profile photos across major platforms—without ever dispatching an SMS or alerting the target.</em>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ⚡ What is phonsint?
|
|
54
|
+
|
|
55
|
+
Most phone number tools stop at basic format parsing or risk blowing an investigation by triggering loud 2FA/SMS verification codes to the target's mobile device.
|
|
56
|
+
|
|
57
|
+
**phonsint** is engineered for **silent, high-fidelity reconnaissance**. It interrogates pre-authentication discovery endpoints, recovery validation flows, and cryptographic challenge APIs (such as Meta LOX proof-of-work) to determine account existence and extract rich profile metadata—**completely silently**.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## ✨ Key Capabilities
|
|
62
|
+
|
|
63
|
+
* **Guaranteed Silent Operation**: Exploits unauthenticated query endpoints and pre-dispatch recovery flows that **never send SMS verification codes or push notifications** to the target.
|
|
64
|
+
* **Deep Identity Enrichment**:
|
|
65
|
+
* 📧 **Masked Email Addresses**: Surfaces linked emails (e.g. `j******e@gmail.com`) associated with the phone number.
|
|
66
|
+
* 👤 **Full Display Names**: Pulls account owner names from public platform recovery APIs.
|
|
67
|
+
* 🖼️ **High-Resolution Avatars**: Extracts direct CDN profile photo URLs without logging in.
|
|
68
|
+
* 🏢 **Tenant & Region Routing**: Identifies regional datacenters (e.g., Zoho US, EU, IN, CN, JP).
|
|
69
|
+
* **High-Throughput Concurrency**: Built with Python `asyncio` and `curl_cffi` for browser TLS fingerprint impersonation (bypasses Cloudflare, Akamai, and Shape Security without heavy headless browsers).
|
|
70
|
+
* **Zero False Positives**: Uses strict, dual-state validation markers (matching the design philosophy of [`user-scanner`](https://github.com/kaifcodec/user-scanner)).
|
|
71
|
+
* **Multi-Format Reports**: Automated structured exports to **JSON** and **CSV** for immediate ingestion into investigation pipelines.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🖥️ Live Terminal Preview
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
Checking phone: +14155552671
|
|
79
|
+
|
|
80
|
+
== SOCIAL SITES ==
|
|
81
|
+
[✔] Facebook (+14155552671) [Registered]
|
|
82
|
+
├── name: John Doe
|
|
83
|
+
├── masked_email: j******e@gmail.com
|
|
84
|
+
└── avatar: https://scontent.xx.fbcdn.net/v/t39.30808-1/s200x200/412...
|
|
85
|
+
|
|
86
|
+
[✔] Instagram (+14155552671) [Registered]
|
|
87
|
+
└── contact_point: Phone Number Verified
|
|
88
|
+
|
|
89
|
+
== AUTH SITES ==
|
|
90
|
+
[✔] Microsoft (+14155552671) [Registered]
|
|
91
|
+
└── auth_type: Live ID / Account Exists
|
|
92
|
+
|
|
93
|
+
== SHOPPING SITES ==
|
|
94
|
+
[✔] Amazon (+14155552671) [Registered]
|
|
95
|
+
└── claim_endpoint: Active Sign-in Route
|
|
96
|
+
|
|
97
|
+
== SUMMARY ==
|
|
98
|
+
Total Sites Scanned: 5
|
|
99
|
+
Registered / Found: 4
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 🚀 Installation
|
|
105
|
+
|
|
106
|
+
### 🐍 Via Virtual Environment (Recommended)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Clone the repository
|
|
110
|
+
git clone https://github.com/kaifcodec/phonsint.git
|
|
111
|
+
cd phonsint
|
|
112
|
+
|
|
113
|
+
# Create and activate virtual environment
|
|
114
|
+
python3 -m venv .venv
|
|
115
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\Activate.ps1
|
|
116
|
+
|
|
117
|
+
# Install dependencies and package in editable mode
|
|
118
|
+
pip install -e .
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 📦 Direct Installation
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install phonsint
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 💻 Usage Guide
|
|
130
|
+
|
|
131
|
+
### 1. Standard Target Scan (E.164 Format)
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
phonsint -p +14155552671
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 2. Scanning Local Format Numbers with Default Region
|
|
138
|
+
|
|
139
|
+
If a target number does not include an international `+` country prefix, specify the default ISO country code:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
phonsint -p 07911123456 -r GB
|
|
143
|
+
phonsint -p 9876543210 -r IN
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 3. Targeted Category & Module Filtering
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Scan authentication providers only (e.g., Microsoft)
|
|
150
|
+
phonsint -p +14155552671 -c auth
|
|
151
|
+
|
|
152
|
+
# Scan specific platforms
|
|
153
|
+
phonsint -p +14155552671 -m facebook,instagram
|
|
154
|
+
|
|
155
|
+
# List all available modules and categories in a clean table
|
|
156
|
+
phonsint -l
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 4. Verbose Mode & Exporting Results
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Show all results including unregistered platforms and diagnostic reasons
|
|
163
|
+
phonsint -p +14155552671 --all -v
|
|
164
|
+
|
|
165
|
+
# Export results to JSON or CSV
|
|
166
|
+
phonsint -p +14155552671 -o report.json
|
|
167
|
+
phonsint -p +14155552671 -f csv -o report.csv
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🛡️ OPSEC & Silent-by-Default Guarantee
|
|
173
|
+
|
|
174
|
+
In phone OSINT, accidental SMS verification codes sent to the target's phone are a critical operational failure. `phonsint` operates **100% silently by default**, targeting pre-dispatch query endpoints, session credential checks, and unauthenticated recovery validations that **do not dispatch SMS messages**.
|
|
175
|
+
|
|
176
|
+
Any module that could potentially trigger a notification is registered under `LOUD_MODULES` in `phonsint/core/helpers.py` and will be automatically skipped unless the user explicitly passes `--allow-loud`.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🤝 Contributing
|
|
181
|
+
|
|
182
|
+
We welcome contributions! Adding a new platform check takes around ~30 lines of clean Python. Check out **[CONTRIBUTING.md](CONTRIBUTING.md)** for developer conventions, validator signatures, and instructions.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 📜 License
|
|
187
|
+
|
|
188
|
+
Distributed under the **MIT License**.
|
|
189
|
+
|
phonsint-0.1.0/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# phonsint
|
|
2
|
+
<img width="1713" height="420" alt="phonsint-banner" src="https://github.com/user-attachments/assets/a76a73a2-8aac-4813-a265-6802b2230eb0" />
|
|
3
|
+
|
|
4
|
+
<p align="center">
|
|
5
|
+
<img src="https://img.shields.io/badge/Version-0.1.0-blueviolet?style=for-the-badge&logo=github" />
|
|
6
|
+
<img src="https://img.shields.io/badge/Tested%20on-Linux%20%7C%20macOS%20%7C%20Windows%20%7C%20Termux-black?style=for-the-badge" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>High-Throughput Silent Phone Number OSINT & Identity Footprinting Suite</strong><br>
|
|
11
|
+
<em>Uncover account registrations, masked emails, display names, and profile photos across major platforms—without ever dispatching an SMS or alerting the target.</em>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ⚡ What is phonsint?
|
|
17
|
+
|
|
18
|
+
Most phone number tools stop at basic format parsing or risk blowing an investigation by triggering loud 2FA/SMS verification codes to the target's mobile device.
|
|
19
|
+
|
|
20
|
+
**phonsint** is engineered for **silent, high-fidelity reconnaissance**. It interrogates pre-authentication discovery endpoints, recovery validation flows, and cryptographic challenge APIs (such as Meta LOX proof-of-work) to determine account existence and extract rich profile metadata—**completely silently**.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ✨ Key Capabilities
|
|
25
|
+
|
|
26
|
+
* **Guaranteed Silent Operation**: Exploits unauthenticated query endpoints and pre-dispatch recovery flows that **never send SMS verification codes or push notifications** to the target.
|
|
27
|
+
* **Deep Identity Enrichment**:
|
|
28
|
+
* 📧 **Masked Email Addresses**: Surfaces linked emails (e.g. `j******e@gmail.com`) associated with the phone number.
|
|
29
|
+
* 👤 **Full Display Names**: Pulls account owner names from public platform recovery APIs.
|
|
30
|
+
* 🖼️ **High-Resolution Avatars**: Extracts direct CDN profile photo URLs without logging in.
|
|
31
|
+
* 🏢 **Tenant & Region Routing**: Identifies regional datacenters (e.g., Zoho US, EU, IN, CN, JP).
|
|
32
|
+
* **High-Throughput Concurrency**: Built with Python `asyncio` and `curl_cffi` for browser TLS fingerprint impersonation (bypasses Cloudflare, Akamai, and Shape Security without heavy headless browsers).
|
|
33
|
+
* **Zero False Positives**: Uses strict, dual-state validation markers (matching the design philosophy of [`user-scanner`](https://github.com/kaifcodec/user-scanner)).
|
|
34
|
+
* **Multi-Format Reports**: Automated structured exports to **JSON** and **CSV** for immediate ingestion into investigation pipelines.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🖥️ Live Terminal Preview
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
Checking phone: +14155552671
|
|
42
|
+
|
|
43
|
+
== SOCIAL SITES ==
|
|
44
|
+
[✔] Facebook (+14155552671) [Registered]
|
|
45
|
+
├── name: John Doe
|
|
46
|
+
├── masked_email: j******e@gmail.com
|
|
47
|
+
└── avatar: https://scontent.xx.fbcdn.net/v/t39.30808-1/s200x200/412...
|
|
48
|
+
|
|
49
|
+
[✔] Instagram (+14155552671) [Registered]
|
|
50
|
+
└── contact_point: Phone Number Verified
|
|
51
|
+
|
|
52
|
+
== AUTH SITES ==
|
|
53
|
+
[✔] Microsoft (+14155552671) [Registered]
|
|
54
|
+
└── auth_type: Live ID / Account Exists
|
|
55
|
+
|
|
56
|
+
== SHOPPING SITES ==
|
|
57
|
+
[✔] Amazon (+14155552671) [Registered]
|
|
58
|
+
└── claim_endpoint: Active Sign-in Route
|
|
59
|
+
|
|
60
|
+
== SUMMARY ==
|
|
61
|
+
Total Sites Scanned: 5
|
|
62
|
+
Registered / Found: 4
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 🚀 Installation
|
|
68
|
+
|
|
69
|
+
### 🐍 Via Virtual Environment (Recommended)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Clone the repository
|
|
73
|
+
git clone https://github.com/kaifcodec/phonsint.git
|
|
74
|
+
cd phonsint
|
|
75
|
+
|
|
76
|
+
# Create and activate virtual environment
|
|
77
|
+
python3 -m venv .venv
|
|
78
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\Activate.ps1
|
|
79
|
+
|
|
80
|
+
# Install dependencies and package in editable mode
|
|
81
|
+
pip install -e .
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 📦 Direct Installation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install phonsint
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 💻 Usage Guide
|
|
93
|
+
|
|
94
|
+
### 1. Standard Target Scan (E.164 Format)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
phonsint -p +14155552671
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Scanning Local Format Numbers with Default Region
|
|
101
|
+
|
|
102
|
+
If a target number does not include an international `+` country prefix, specify the default ISO country code:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
phonsint -p 07911123456 -r GB
|
|
106
|
+
phonsint -p 9876543210 -r IN
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Targeted Category & Module Filtering
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Scan authentication providers only (e.g., Microsoft)
|
|
113
|
+
phonsint -p +14155552671 -c auth
|
|
114
|
+
|
|
115
|
+
# Scan specific platforms
|
|
116
|
+
phonsint -p +14155552671 -m facebook,instagram
|
|
117
|
+
|
|
118
|
+
# List all available modules and categories in a clean table
|
|
119
|
+
phonsint -l
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. Verbose Mode & Exporting Results
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Show all results including unregistered platforms and diagnostic reasons
|
|
126
|
+
phonsint -p +14155552671 --all -v
|
|
127
|
+
|
|
128
|
+
# Export results to JSON or CSV
|
|
129
|
+
phonsint -p +14155552671 -o report.json
|
|
130
|
+
phonsint -p +14155552671 -f csv -o report.csv
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🛡️ OPSEC & Silent-by-Default Guarantee
|
|
136
|
+
|
|
137
|
+
In phone OSINT, accidental SMS verification codes sent to the target's phone are a critical operational failure. `phonsint` operates **100% silently by default**, targeting pre-dispatch query endpoints, session credential checks, and unauthenticated recovery validations that **do not dispatch SMS messages**.
|
|
138
|
+
|
|
139
|
+
Any module that could potentially trigger a notification is registered under `LOUD_MODULES` in `phonsint/core/helpers.py` and will be automatically skipped unless the user explicitly passes `--allow-loud`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🤝 Contributing
|
|
144
|
+
|
|
145
|
+
We welcome contributions! Adding a new platform check takes around ~30 lines of clean Python. Check out **[CONTRIBUTING.md](CONTRIBUTING.md)** for developer conventions, validator signatures, and instructions.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 📜 License
|
|
150
|
+
|
|
151
|
+
Distributed under the **MIT License**.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import csv
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from colorama import Fore, Style, init
|
|
7
|
+
|
|
8
|
+
from phonsint import __version__
|
|
9
|
+
from phonsint.core.helpers import ScanConfig, load_categories, load_modules
|
|
10
|
+
from phonsint.core.orchestrator import run_scan
|
|
11
|
+
from phonsint.core.parser import parse_phone_number
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
init(autoreset=True)
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
BANNER = rf"""{Fore.CYAN}
|
|
19
|
+
____ __ _ __
|
|
20
|
+
/ __ \/ /_ ____ ____ _____ (_)___ / /_
|
|
21
|
+
/ /_/ / __ \/ __ \/ __ \/ ___// / __ \/ __/
|
|
22
|
+
/ ____/ / / / /_/ / / / (__ )/ / / / / /_
|
|
23
|
+
/_/ /_/ /_/\____/_/ /_/____//_/_/ /_/\__/
|
|
24
|
+
{Fore.LIGHTBLACK_EX} phonsint v{__version__} | Silent Phone Reconnaissance & Intelligence Suite
|
|
25
|
+
{Style.RESET_ALL}"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def print_banner() -> None:
|
|
29
|
+
print(BANNER)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def list_modules() -> None:
|
|
35
|
+
print_banner()
|
|
36
|
+
categories = load_categories()
|
|
37
|
+
if not categories:
|
|
38
|
+
print(f"{Fore.YELLOW}[!] No modules found.{Style.RESET_ALL}")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
table = Table(title="[bold cyan]Available Scan Modules[/bold cyan]", border_style="cyan")
|
|
42
|
+
table.add_column("Category", style="magenta", width=18)
|
|
43
|
+
table.add_column("Module", style="green", width=22)
|
|
44
|
+
table.add_column("Path", style="dim white")
|
|
45
|
+
|
|
46
|
+
for cat_name, cat_path in sorted(categories.items()):
|
|
47
|
+
mods = load_modules(cat_path)
|
|
48
|
+
for m in mods:
|
|
49
|
+
table.add_row(cat_name.capitalize(), m.__name__.split(".")[-1], str(m.__file__))
|
|
50
|
+
|
|
51
|
+
console.print(table)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def export_results(results, file_path: str, fmt: str = "json") -> None:
|
|
55
|
+
out = Path(file_path)
|
|
56
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
|
|
58
|
+
if fmt == "csv" or out.suffix.lower() == ".csv":
|
|
59
|
+
with open(out, "w", newline="", encoding="utf-8") as f:
|
|
60
|
+
writer = csv.writer(f)
|
|
61
|
+
writer.writerow(["site_name", "category", "phone", "status", "url", "extra", "media", "reason"])
|
|
62
|
+
for r in results:
|
|
63
|
+
writer.writerow([
|
|
64
|
+
r.site_name,
|
|
65
|
+
r.category,
|
|
66
|
+
r.phone,
|
|
67
|
+
r.status.to_label(),
|
|
68
|
+
r.url,
|
|
69
|
+
json.dumps(r.extra) if r.extra else "",
|
|
70
|
+
json.dumps(r.media) if r.media else "",
|
|
71
|
+
r.reason or "",
|
|
72
|
+
])
|
|
73
|
+
else:
|
|
74
|
+
with open(out, "w", encoding="utf-8") as f:
|
|
75
|
+
json.dump([r.to_dict() for r in results], f, indent=2)
|
|
76
|
+
|
|
77
|
+
print(f"\n{Fore.GREEN}[+] Results successfully exported to: {out}{Style.RESET_ALL}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main() -> None:
|
|
81
|
+
parser = argparse.ArgumentParser(
|
|
82
|
+
prog="phonsint",
|
|
83
|
+
description="phonsint: High-Throughput Silent Phone Number Reconnaissance & Identity Footprinting",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument("-p", "--phone", help="Target phone number (e.g. +14155552671)")
|
|
86
|
+
parser.add_argument("-r", "--region", help="Default ISO country code if number lacks '+' prefix (e.g. US, GB, IN, FR)")
|
|
87
|
+
parser.add_argument("-c", "--category", help="Limit scan to specific comma-separated categories (e.g. auth,social)")
|
|
88
|
+
parser.add_argument("-m", "--module", help="Limit scan to specific comma-separated modules (e.g. microsoft,facebook)")
|
|
89
|
+
parser.add_argument("-l", "--list", action="store_true", help="List all available categories and modules")
|
|
90
|
+
parser.add_argument("--all", action="store_true", help="Show all results including not registered and errors")
|
|
91
|
+
parser.add_argument("-v", "--verbose", action="store_true", help="Display URLs and diagnostic reasons")
|
|
92
|
+
parser.add_argument("-t", "--timeout", type=float, default=15.0, help="Per-module request timeout in seconds")
|
|
93
|
+
parser.add_argument("-C", "--concurrency", type=int, default=15, help="Maximum concurrent requests")
|
|
94
|
+
parser.add_argument("--allow-loud", action="store_true", help="Allow modules that may trigger SMS/notifications")
|
|
95
|
+
parser.add_argument("-f", "--format", choices=["json", "csv"], default="json", help="Export format (json/csv)")
|
|
96
|
+
parser.add_argument("-o", "--output", help="Save scan results to file")
|
|
97
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
98
|
+
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
|
|
101
|
+
if args.list:
|
|
102
|
+
list_modules()
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
if not args.phone:
|
|
106
|
+
print_banner()
|
|
107
|
+
parser.print_help()
|
|
108
|
+
print(f"\n{Fore.RED}[!] Error: Please provide a target phone number using -p / --phone.{Style.RESET_ALL}")
|
|
109
|
+
sys.exit(1)
|
|
110
|
+
|
|
111
|
+
print_banner()
|
|
112
|
+
|
|
113
|
+
# Step 1: Parse and validate phone number
|
|
114
|
+
phone_info = parse_phone_number(args.phone, args.region)
|
|
115
|
+
print(f"\n{Fore.CYAN} Checking phone: {phone_info.e164}{Style.RESET_ALL}")
|
|
116
|
+
|
|
117
|
+
if not phone_info.is_valid:
|
|
118
|
+
print(f"{Fore.YELLOW}[!] Warning: '{args.phone}' is not a standard allocated number according to E.164.{Style.RESET_ALL}")
|
|
119
|
+
print(f"{Fore.YELLOW}[*] Proceeding with best-effort scan anyway...{Style.RESET_ALL}\n")
|
|
120
|
+
|
|
121
|
+
# Step 2: Build scan configuration
|
|
122
|
+
configs = ScanConfig(
|
|
123
|
+
allow_loud=args.allow_loud,
|
|
124
|
+
show_all=args.all,
|
|
125
|
+
verbose=args.verbose,
|
|
126
|
+
timeout=args.timeout,
|
|
127
|
+
concurrency=args.concurrency,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Step 3: Run platform scan
|
|
131
|
+
results = run_scan(
|
|
132
|
+
phone_info,
|
|
133
|
+
configs,
|
|
134
|
+
category_filter=args.category,
|
|
135
|
+
module_filter=args.module,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Summary count
|
|
139
|
+
taken_count = sum(1 for r in results if r.is_found())
|
|
140
|
+
print(f"\n{Fore.CYAN}== SUMMARY =={Style.RESET_ALL}")
|
|
141
|
+
print(f"Total Sites Scanned: {len(results)}")
|
|
142
|
+
print(f"Registered / Found: {Fore.GREEN}{taken_count}{Style.RESET_ALL}")
|
|
143
|
+
|
|
144
|
+
# Step 4: Export if requested
|
|
145
|
+
if args.output:
|
|
146
|
+
export_results(results, args.output, args.format)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
main()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import inspect
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from types import ModuleType
|
|
6
|
+
from typing import Any, Callable, Dict, List, Optional, cast
|
|
7
|
+
|
|
8
|
+
# List of modules that could potentially alert the target via SMS or phone call
|
|
9
|
+
LOUD_MODULES: List[str] = []
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class ScanConfig:
|
|
14
|
+
allow_loud: bool = False
|
|
15
|
+
show_all: bool = False
|
|
16
|
+
verbose: bool = False
|
|
17
|
+
timeout: Optional[float] = None
|
|
18
|
+
concurrency: int = 15
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def is_loud(site_name: str) -> bool:
|
|
22
|
+
return site_name.lower() in LOUD_MODULES
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_site_name(module: ModuleType) -> str:
|
|
26
|
+
name = module.__name__.split(".")[-1].replace("_", " ").title()
|
|
27
|
+
return name
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def find_category(module: ModuleType) -> str:
|
|
31
|
+
parts = module.__name__.split(".")
|
|
32
|
+
if len(parts) >= 3 and parts[-3] == "modules":
|
|
33
|
+
return parts[-2].capitalize()
|
|
34
|
+
return "General"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_categories() -> Dict[str, Path]:
|
|
38
|
+
root = Path(__file__).resolve().parent.parent / "modules"
|
|
39
|
+
categories: Dict[str, Path] = {}
|
|
40
|
+
if not root.exists():
|
|
41
|
+
return categories
|
|
42
|
+
|
|
43
|
+
for subfolder in root.iterdir():
|
|
44
|
+
if subfolder.is_dir() and not subfolder.name.startswith("__"):
|
|
45
|
+
categories[subfolder.name] = subfolder.resolve()
|
|
46
|
+
|
|
47
|
+
return categories
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_modules(category_path: Path) -> List[ModuleType]:
|
|
51
|
+
modules = []
|
|
52
|
+
for file in category_path.glob("*.py"):
|
|
53
|
+
if file.name.startswith("__"):
|
|
54
|
+
continue
|
|
55
|
+
spec = importlib.util.spec_from_file_location(file.stem, str(file))
|
|
56
|
+
if spec is None or spec.loader is None:
|
|
57
|
+
continue
|
|
58
|
+
module = importlib.util.module_from_spec(spec)
|
|
59
|
+
try:
|
|
60
|
+
spec.loader.exec_module(module)
|
|
61
|
+
modules.append(module)
|
|
62
|
+
except Exception:
|
|
63
|
+
continue
|
|
64
|
+
return modules
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_scan_func(module: ModuleType) -> Optional[Callable[..., Any]]:
|
|
68
|
+
for attr_name in dir(module):
|
|
69
|
+
if not attr_name.startswith("validate_"):
|
|
70
|
+
continue
|
|
71
|
+
f = getattr(module, attr_name)
|
|
72
|
+
if inspect.isfunction(f) or inspect.iscoroutinefunction(f):
|
|
73
|
+
return cast(Callable[..., Any], f)
|
|
74
|
+
return None
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import threading
|
|
3
|
+
from typing import Literal, Optional, cast
|
|
4
|
+
from curl_cffi import requests as cffi
|
|
5
|
+
|
|
6
|
+
DEFAULT_IMPERSONATE = "chrome"
|
|
7
|
+
DEFAULT_TIMEOUT = 15.0
|
|
8
|
+
|
|
9
|
+
_sessions: dict[tuple, cffi.Session] = {}
|
|
10
|
+
_key_locks: dict[tuple, threading.Lock] = {}
|
|
11
|
+
_warmed: set[tuple] = set()
|
|
12
|
+
_lock = threading.Lock()
|
|
13
|
+
_global_timeout: Optional[float] = None
|
|
14
|
+
_global_proxy: Optional[str] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def set_global_timeout(val: Optional[float]) -> None:
|
|
18
|
+
global _global_timeout
|
|
19
|
+
_global_timeout = val
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_global_timeout() -> float:
|
|
23
|
+
return _global_timeout if _global_timeout is not None else DEFAULT_TIMEOUT
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_global_proxy(val: Optional[str]) -> None:
|
|
27
|
+
global _global_proxy
|
|
28
|
+
_global_proxy = val
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_global_proxy() -> Optional[str]:
|
|
32
|
+
return _global_proxy
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_warm_session(
|
|
36
|
+
impersonate: str,
|
|
37
|
+
proxy: Optional[str],
|
|
38
|
+
warmup_url: Optional[str] = None,
|
|
39
|
+
) -> cffi.Session:
|
|
40
|
+
key = (impersonate, proxy)
|
|
41
|
+
with _lock:
|
|
42
|
+
session = _sessions.get(key)
|
|
43
|
+
if session is None:
|
|
44
|
+
session = cffi.Session(
|
|
45
|
+
impersonate=impersonate, # type: ignore[arg-type]
|
|
46
|
+
proxies={"http": proxy, "https": proxy} if proxy else None,
|
|
47
|
+
)
|
|
48
|
+
_sessions[key] = session
|
|
49
|
+
_key_locks[key] = threading.Lock()
|
|
50
|
+
|
|
51
|
+
key_lock = _key_locks.get(key)
|
|
52
|
+
if key_lock is None:
|
|
53
|
+
key_lock = threading.Lock()
|
|
54
|
+
_key_locks[key] = key_lock
|
|
55
|
+
|
|
56
|
+
if warmup_url and key not in _warmed:
|
|
57
|
+
with key_lock:
|
|
58
|
+
if key not in _warmed:
|
|
59
|
+
try:
|
|
60
|
+
session.get(warmup_url, timeout=get_global_timeout())
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
_warmed.add(key)
|
|
64
|
+
|
|
65
|
+
return session
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def impersonate_request(
|
|
69
|
+
url: str,
|
|
70
|
+
method: Literal["GET", "POST"] = "GET",
|
|
71
|
+
warmup_url: Optional[str] = None,
|
|
72
|
+
impersonate: str = DEFAULT_IMPERSONATE,
|
|
73
|
+
**kwargs,
|
|
74
|
+
) -> cffi.Response:
|
|
75
|
+
"""Issue a request through a cookie-persistent browser-impersonating curl_cffi session."""
|
|
76
|
+
session = _get_warm_session(impersonate, get_global_proxy(), warmup_url)
|
|
77
|
+
kwargs.setdefault("timeout", get_global_timeout())
|
|
78
|
+
kwargs.setdefault("allow_redirects", False)
|
|
79
|
+
return cast(cffi.Response, session.request(method, url, **kwargs))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def impersonate_request_async(
|
|
83
|
+
url: str,
|
|
84
|
+
method: Literal["GET", "POST"] = "GET",
|
|
85
|
+
warmup_url: Optional[str] = None,
|
|
86
|
+
impersonate: str = DEFAULT_IMPERSONATE,
|
|
87
|
+
**kwargs,
|
|
88
|
+
) -> cffi.Response:
|
|
89
|
+
"""Async wrapper around impersonate_request."""
|
|
90
|
+
return await asyncio.to_thread(
|
|
91
|
+
impersonate_request,
|
|
92
|
+
url,
|
|
93
|
+
method=method,
|
|
94
|
+
warmup_url=warmup_url,
|
|
95
|
+
impersonate=impersonate,
|
|
96
|
+
**kwargs,
|
|
97
|
+
)
|