
An OSINT tool that helps detect members of a company with leaked credentials
EmploLeaks is an OSINT (Open Source Intelligence) tool with a CLI interface, designed to discover and correlate information about employees of a target company. It allows collecting LinkedIn profiles, generating potential corporate emails, searching for leaked credentials in leak databases (ClickHouse internally), verifying known breaches through HaveIBeenPwned, discovering company infrastructure, and profiling employees on social networks. All information is stored locally in SQLite for later analysis.
emploleaks/ ├── emploleaks.py # Script principal (CLI interactiva con cmd2) ├── telegram_sync.py # Daemon userbot de Telegram (Telethon, standalone) ├── requirements.txt # Dependencias de Python ├── README.md ├── .gitignore ├── plugins/ │ ├── linkedin.py # Plugin de LinkedIn (scraping de empleados) │ ├── github.py # Plugin de GitHub (repos, stalk, secrets) │ └── hibp.py # Plugin de HaveIBeenPwned (brechas) ├── utils/ │ ├── logging_format.py # Configuración de logging con colores │ ├── ai_classifier.py # Clasificación de roles con IA (OpenAI/Ollama) │ ├── leak_parser.py # Parser de leaks 100% agéntico (loop de IA, sin regex) │ ├── email_lookup.py # Búsqueda de emails en redes sociales (Holehe) │ ├── profile_lookup.py # Búsqueda de usernames en redes sociales (Maigret) │ └── discovery.py # Enumeración de subdominios (assetfinder + SecurityTrails opcional) ├── clickhouse-docker/ # Docker Compose para levantar ClickHouse │ ├── docker-compose.yml │ └── config/ │ └── users.xml ├── leaks_data/ # Carpeta para archivos de leaks a importar (no en git) ├── config/ # Configuración (autogenerado) │ └── tokens.ini # Tokens y credenciales de plugins (no en git) ├── data/ # Base de datos local (autogenerado) │ └── emploleaks.db # SQLite con toda la información recopilada ├── webapp/ # Webapp administrativa │ ├── backend/ # FastAPI (Python) │ └── frontend/ # Next.js (React/TypeScript) └── logs/ # Archivos de log (autogenerado) └── log.txt
## Requirements
- Python 3.10+
- pip
- Internet connection
- **Optional:** Docker and Docker Compose (to run ClickHouse locally)
- **Optional:** [gitleaks](https://github.com/gitleaks/gitleaks) (for scanning secrets in repos)
- **Optional:** [HaveIBeenPwned](https://haveibeenpwned.com/API/Key) API key
- LinkedIn session cookies (`JSESSIONID` and `li_at`) for the LinkedIn plugin
## Installation
1. Clone the repository:```bash
git clone https://github.com/yourusername/emploleaks.git
cd emploleaks
3. (Optional) Launch ClickHouse with Docker for the leak database:```bash
cd clickhouse-docker
docker compose up -d
cd ..
Run the tool:```bash python emploleaks.py
With debug mode:```bash
python emploleaks.py -d
| Command | Description |
|---|---|
help | Shows general help |
help <command> | Shows help for a specific command |
quit | Exits the application |
| Command | Description |
|---|---|
add_company --name <name> | Adds a new company |
select_company --name <name> |
The ClickHouse connection is configured in config/tokens.ini:```ini
[clickhouse]
host = localhost
port = 9000
user = default
passwd =
dbname = credentials_db
If ClickHouse is configured in `tokens.ini`, the connection is established automatically on startup.
### Credential and Breach Search
| Command | Description |
|---------|-------------|
| `find_passwords <mode>` | Searches for credentials in ClickHouse + [ProxyNova COMB](https://www.proxynova.com/tools/comb/) (3.2B credentials). Modes: `find_all`, `only_usernames`, `only_emails` |
| `find_passwords <mode> --no-proxynova` | Searches only in local ClickHouse |
| `find_passwords <mode> --no-clickhouse` | Searches only in ProxyNova COMB (requires no ClickHouse) |
| `find_passwords <mode> --email <email>` | Searches for credentials for a specific email |
| `find_breaches` | Searches for breaches in HIBP for all company emails (requires plugin `hibp` active) |
**ProxyNova COMB** is a public database with 3.2 billion leaked credentials (Combination Of Many Breaches). It requires no API key and is queried automatically on each search. Use `--no-proxynova` to disable it.
### Infrastructure Discovery
| Command | Description |
|---------|-------------|
| `add_domain <domain>` | Associates a domain with the selected company (e.g., `add_domain faradaysec.com`) |
| `discover` | Runs subdomain enumeration against all company domains using `assetfinder` (+ optional SecurityTrails), resolving DNS |
| `print --data domains` | Shows registered domains and the number of subdomains found |
| `print --data subdomains` | Shows all subdomains with their IP, source, and discovery date |
The Discovery module delegates passive enumeration to [`assetfinder`](https://github.com/tomnomnom/assetfinder), which internally aggregates results from crt.sh, HackerTarget, BufferOver, and other sources without an API key. The binary is expected to be in `$PATH` (installation: `go install github.com/tomnomnom/assetfinder@latest`).
Optionally, if you configure a SecurityTrails API key, their subdomains are merged with those from assetfinder:```ini
[discovery]
# securitytrails_key = your_key_here
Holehe (email lookup): uses the "forgot password" technique to determine if an email is registered on each platform, without alerting the owner. The platforms are configured in tokens.ini:```ini
[holehe]
platforms = google, discord, github, instagram, twitter, spotify, ...
**Maigret** (username lookup): searches if a username exists on 500+ social media platforms. Usernames are added manually by employees from the webapp. Platforms are optionally filtered in `tokens.ini`:```ini
[maigret]
# platforms = instagram, twitter, facebook, tiktok, reddit, github
| Command | Description |
|---|---|
set_ai --endpoint <url> --key <key> --model <modelo> | Configures the AI provider |
classify | Classifies employees into departments using AI (saved in the DB) |
classify --force | Re-classifies even if they already have a department assigned |
AI classification analyzes employees' titles/roles and groups them by department (Engineering, Security, Sales, etc.). Departments are persisted in SQLite and are visible in the HTML report and in the webapp.
Supported providers (any OpenAI-compatible API):
set_ai --endpoint http://localhost:11434/v1 --model llama3set_ai --endpoint https://api.openai.com/v1 --key sk-... --model gpt-4o-miniStandalone daemon that uses a personal Telegram account (via Telethon) to join approved channels/groups, download .txt/.csv/.dat/.zip/.gz files and leave them in leaks_data/telegram/<chat>/. The subsequent import_leaks ingests them into ClickHouse using the agentic parser. Decoupled from the CLI — runs as a separate process, so it can be 24/7 without the CLI being open.
Initial setup (one-time):
api_id and api_hash.config/tokens.ini: ```ini
[telegram]
api_id = 12345678
api_hash = abcd1234...
session = config/telegram.session
download_root = leaks_data/telegram
allowed_extensions = txt, csv, dat, zip, gz
default_backfill_limit = 10
Subcommands:
New tables in data/emploleaks.db:
| Table | Description |
|---|---|
telegram_groups | Snapshot of chats (chat_id, title, username, approved, backfill_limit, last_sync_at) |
telegram_files | Downloaded files with dedup by (chat_id, message_id) and by SHA-256 of the content |
Daemon in background:```bash nohup python3.13 telegram_sync.py run --watch >> logs/telegram.log 2>&1 & echo $! > /tmp/telegram_daemon.pid
### 100% Agentic Leak Parser
The `utils/leak_parser.py` module uses **only an AI agent** to parse each file. There are no hardcoded regexes (they were removed because each new format introduced subtle extraction bugs).
**Agent pipeline:**```
1. Auto-detectar encoding (BOM sniffing): utf-8 / utf-8-sig / utf-16 / utf-32.
2. Sampling inteligente: tomar las primeras 80 líneas que parezcan credenciales
(con separadores típicos, alfanumérico >50%) — skipea banners ASCII art.
3. La IA recibe 50 líneas + system prompt con familias comunes de formato y
propone {separator, fields, skip_lines}.
4. Aplicamos el schema a la muestra SIN filtros → list of tuples.
5. Validamos con _is_clean_credential → score = % rows válidas.
6. Si score ≥ 0.8 y ≥ 5 rows → aplicar al archivo completo.
7. Si no → mandar al agente la (source_line ↔ extracción mala) en pares,
más checklist de errores típicos. Goto 3.
8. Hasta 7 iteraciones. Si no converge a 0.8: fallback al mejor schema si
alcanzó ≥ 0.7. Bajo eso → skip con warning (nunca ingerimos basura).
_is_clean_credential — cross-validation:
/ : \ space, does not start with http/android/ftp//./, maximum one :, no whitespace, does not start with URL prefix.Determinism: temperature=0 in all calls + in-memory caching within the same run. The same file produces the same schema between runs → import_leaks is idempotent.
Typical cost: 1-3 API calls per file, ~$0.0001-$0.0005 with gpt-4o-mini. For 12 files: ~$0.005 = half a cent.
| Command | Description |
|---|---|
python emploleaks.py --webapp | Launches the webapp (backend on :8421, frontend on :3421) |
The webapp allows:
Allows searching for employees of a company on LinkedIn using browser session cookies, and generating potential corporate emails. Automatically captures the company logo.
Options:
| Option | Description |
|---|---|
JSESSIONID | LinkedIn session cookie JSESSIONID |
li-at | LinkedIn session cookie li_at |
hide | Hide the JSESSIONID value when displaying it (default: yes) |
Available commands within the plugin:
| Command | Description |
|---|---|
run impersonate | Authenticate using the configured cookies |
run find <company_linkedin> <domain_email> [--email-format FORMAT] | Search for employees and generate emails |
Available email formats:
f_last (e.g., [email protected]) and f.last (e.g., [email protected])--email-format: use placeholders {n} (first name initial), {s} (last name), {name} (full name), {l} (last name initial)
--email-format {n}.{s} generates [email protected]Complete example:``` emploleaks> add_company --name miempresa emploleaks> select_company --name miempresa emploleaks(miempresa)> use --plugin linkedin emploleaks(miempresa)(linkedin)> setopt JSESSIONID JSESSIONID: emploleaks(miempresa)(linkedin)> setopt li-at li-at: emploleaks(miempresa)(linkedin)> run impersonate emploleaks(miempresa)(linkedin)> run find miempresa miempresa.com --email-format {n}.{s}
### GitHub
Allows obtaining information from GitHub profiles, listing repositories, and scanning secrets in repositories with gitleaks.
**Options:**
| Option | Description |
|--------|-------------|
| `token` | Personal GitHub access token |
| `blur` | Obfuscate the token when displaying it |
| `gitleaks_path` | Path to gitleaks binary (default: `gitleaks` in PATH) |
| `max_repo_size` | Maximum repository size to analyze in MB (default: `15`) |
**Available commands within the plugin:**
| Command | Description |
|---------|-------------|
| `run stalk <username>` | Get email from a GitHub account |
| `run get_repos <username>` | List public repositories of a user |
| `run find_secrets` | Scan secrets in company employee repositories |
| `run find_secrets --download-all` | Same but without size limit |
**Complete example:**```
emploleaks(miempresa)> use --plugin github
emploleaks(miempresa)(github)> setopt token ghp_xxxxxxxxxxxx
emploleaks(miempresa)(github)> run find_secrets
emploleaks(miempresa)(github)> print --data secrets
Checks if the company's emails appear in known breaches using the HIBP API.
Options:
| Option | Description |
|---|---|
apikey | HaveIBeenPwned API key |
rate_limit | Delay between API calls in seconds (default: 2.0) |
Available commands within the plugin:
| Command | Description |
|---|---|
run find_breaches | Search breaches for all company emails |
find_breaches | Direct command (equivalent, requires active hibp plugin) |
Complete example:``` emploleaks(miempresa)> use --plugin hibp emploleaks(miempresa)(hibp)> setopt apikey <tu_api_key> emploleaks(miempresa)(hibp)> find_breaches emploleaks(miempresa)(hibp)> print --data breaches
## Leaks Database (ClickHouse)
EmploLeaks can connect to its own ClickHouse database with filtered credentials to search for passwords associated with the emails/usernames of discovered employees.
### Starting ClickHouse with Docker```bash
cd clickhouse-docker
docker compose up -d
This exposes ClickHouse on:
9000 (native TCP protocol)8123 (HTTP interface)The leaks_data/ folder is mounted as a read-only volume inside the container.
.txt, .csv, .dat, .zip, .gz) in the leaks_data/ folder.The parser automatically detects the most common formats:
- `email:password`
- `email:password:url`
- `email;password`
- `email|password`
- `url,email,password`
For unknown formats, it uses the configured AI (OpenAI/Ollama) to analyze a sample of the file and determine how to parse it. Use `--no-ai` to skip AI detection.
`.zip` and `.gz` files are decompressed automatically before parsing.
### Search credentials```
emploleaks(miempresa)> find_passwords only_emails
emploleaks(miempresa)> find_passwords find_all
emploleaks(miempresa)> print --data passwords
If ClickHouse is configured in tokens.ini, the connection is automatic when the tool starts.
credentials table in ClickHouse## Disclaimer
This tool is designed solely for educational purposes, security research, and authorized penetration testing. The use of this tool for malicious or unauthorized activities is strictly prohibited. Users are responsible for complying with all applicable laws and project terms of service.
| Selects a company to work with |
list_companies | Shows all companies |
delete_company --name <name> | Deletes a company and all its data |
| Command | Description |
|---|
use --plugin <name> | Activates a plugin (linkedin, github, hibp) |
deactivate | Deactivates the current plugin |
show options | Shows the active plugin's options |
setopt <option> [value] | Sets a plugin option (if no value is provided, it will be prompted as hidden input) |
autosave --enable / --disable | Enables/disables automatic saving of configuration to config/tokens.ini |
autoload --enable / --disable | Enables/disables automatic loading of configuration from config/tokens.ini |
| Command | Description |
|---|
connect_leaks | Connects to ClickHouse using the saved configuration in tokens.ini |
connect_leaks --host <host> --port <port> --save | Connects with specific parameters and saves them for future sessions |
disconnect_leaks | Disconnects from the ClickHouse database |
import_leaks [directory] | Imports credential files to ClickHouse (default: leaks_data/) |
import_leaks --no-ai | Imports only files with a known format, without using AI |
create_db --user <user> --passwd <pass> --dbname <db> [--import-data <dir>] | Creates the ClickHouse database manually (legacy) |
| Command | Description |
|---|
lookup_emails | Searches for confirmed emails on ~120 platforms with Holehe |
lookup_emails --include-potential | Also includes generated emails |
lookup_emails --email [email protected] | Searches for a specific email |
lookup_emails --all | Searches on the 120+ platforms (not just the configured ones) |
lookup_emails --list-platforms | Lists all available platforms |
lookup_profiles | Searches profiles by username with Maigret (requires loaded usernames) |
lookup_profiles --employee "Juan" | Searches only for a specific employee |
| Command | Description |
|---|
print --data emails | Shows confirmed and potential emails |
print --data passwords | Shows found credentials |
print --data breaches | Shows HIBP breaches |
print --data gits | Shows GitHub accounts |
print --data twitters | Shows Twitter/X accounts |
print --data phones | Shows phone numbers |
print --data websites | Shows websites |
print --data secrets | Shows secrets found in repositories |
print --data domains | Shows registered domains |
print --data subdomains | Shows discovered subdomains |
print --data all | Shows everything consolidated by employee |
print --data all --html | Generates an interactive HTML report with photos |
print --data all --html --ai | Generates HTML report grouping employees by department using AI |
print --data <tipo> --export | Exports the data to a CSV file with timestamp |
| Command | Description |
|---|
login | Initial authentication. Persists session in config/telegram.session |
list_groups | Lists all chats/channels where your account is, and snapshots them in SQLite |
approve <chat_id> [--limit N] | Marks a chat as approved and runs backfill of the last N messages |
unapprove <chat_id> | Removes approval |
list_approved | Table of approved chats with count of downloaded files |
backfill <chat_id> [--limit N] | Re-downloads the last N messages from an approved chat |
run [--watch] | Backfill all approved; with --watch it listens for NewMessage events |
status | Summary: approved / downloaded files / disk used |
| Field | Type | Description |
|---|
mail_username | String | Part of the email username |
mail_domain | String | Email domain (without TLD) |
mail_tld | String | Email TLD |
password | String | Leaked password |
uri_subdomain | String | Subdomain of the site where it was leaked |
uri_domain | String | Site domain |
uri_tld | String | Site TLD |
| Table | Description |
|---|
companies | Registered companies (name, logo) |
employees | Discovered employees (name, title, photo, company, department) |
emails | Confirmed emails (obtained from LinkedIn contact_info) |
potential_emails | Potential emails generated by format |
passwords | Found passwords associated with emails |
username_passwords | Passwords found by username |
breaches | HIBP breaches associated with emails |
githubs | GitHub profile/repo URLs |
twitters | Twitter/X profile URLs |
phones | Phone numbers |
websites | Personal websites |
secrets_repos | Secrets found in repositories with gitleaks |
social_profiles | Social media profiles (Holehe + Maigret) |
domains | Company domains for discovery |
subdomains | Discovered subdomains (IP, source, date) |
usernames | Usernames associated with employees for Maigret |