Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: initstring/linkedin2username
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.14
Choose a base ref
...
head repository: initstring/linkedin2username
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Loading
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: initstring

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Please provide the command as you ran it, omitting the target company name if necessary and of course your username and password.

**Verify Expected Results**
Have you verified that you can access the company page via the web UI, and that the expected amount of contacts are visible using your login?

**Additional context**
Do you have any type of special setup? Web proxy, two-factor LI authentication, etc.
39 changes: 39 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python application

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
47 changes: 47 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,48 @@
# custom
li2u-output
.vscode
geckodriver.log

# MacOS
.DS_Store

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
*.manifest
*.spec
pip-log.txt
pip-delete-this-directory.txt
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
115 changes: 74 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -3,70 +3,103 @@ OSINT Tool: Generate username lists from companies on LinkedIn.

This is a pure web-scraper, no API key required. You use your valid LinkedIn username and password to login, it will create several lists of possible username formats for all employees of a company you point it at.

Use an account with a lot of connections, otherwise you'll get crappy results. Adding a couple connections at the target company should help - this tool will work up to third degree connections. Note that [LinkedIn will cap search results](https://www.linkedin.com/help/linkedin/answer/129/what-you-get-when-you-search-on-linkedin?lang=en) to 1000 employees max. You can use the features '--geoblast' or '--keywords' to bypass this limit. Look at help below for more details.

Here's what you get:
- first.last.txt: Usernames like Joe.Schmoe
- f.last.txt: Usernames like J.Schmoe
- flast.txt: Usernames like JSchmoe
- firstl.txt: Usernames like JoeS
- first.txt Usernames like Joe
- lastf.txt Usernames like SchmoeJ
- rawnames.txt: Full name like Joe Schmoe
- metadata.txt CSV file which is full_name,occupation

Optionally, the tool will append @domain.xxx to the usernames.

# Example
You'll need to provide the tool with LinkedIn's company name. You can find that by looking at the URL for the company's page. It should look something like `https://linkedin.com/company/uber-com`. It may or may not be as simple as the exact name of the company.
![](drawing.jpeg)

Here's an example to pull all employees of Uber:
```
$ python linkedin2username.py myname@email.com uber-com
```
## Warnings

Here's an example to pull a shorter list and append the domain name @uber.com to them:
```
$ python linkedin2username.py myname@email.com uber-com -d 5 -n 'uber.com'
```
Do not blame me if your LinkedIn account gets rate limited, or even banned. This is a security research tool - use it only after reading the code and fully understanding what it is doing.

I have not heard of any account bans since the tool was written, but rate limiting does occasionally kick in when the "commercial search limit" is hit. That has been temporary so far (measured monthly).

## Using the tool

# Full Help
### Pre-requisites

Install the Python dependencies with `pip3 install -r ./requirements.txt`.

You'll also need Chrome, Chromium, or Firefox installed in typical paths that can be discovered by Selenium. A web browser will be spawned temporarily to handle the login.

### Full usage
```
usage: linkedin2username.py [-h] [-p PASSWORD] [-n DOMAIN] [-d DEPTH]
[-s SLEEP]
username company
usage: linkedin2username.py [-h] -c COMPANY [-n DOMAIN] [-d DEPTH]
[-s SLEEP] [-x PROXY] [-k KEYWORDS] [-g] [-o OUTPUT]
positional arguments:
username A valid LinkedIn username.
company Company name.
OSINT tool to generate lists of probable usernames from a given company's LinkedIn page.
This tool may break when LinkedIn changes their site.
Please open issues on GitHub to report any inconsistencies.
optional arguments:
-h, --help show this help message and exit
-p PASSWORD, --password PASSWORD
Specify your password on in clear-text on the command
line. If not specified, will prompt and not display on
screen.
-c COMPANY, --company COMPANY
Company name exactly as typed in the company linkedin profile page URL.
-n DOMAIN, --domain DOMAIN
Append a domain name to username output. [example: '-n
uber.com' would ouput jschmoe@uber.com]
Append a domain name to username output.
[example: "-n targetco.com" would output jschmoe@targetco.com]
-d DEPTH, --depth DEPTH
Search depth. If unset, will try to grab them all.
Search depth (how many loops of 25). If unset, will try to grab them
all.
-s SLEEP, --sleep SLEEP
Seconds to sleep between pages. defaults to 3.
Seconds to sleep between search loops. Defaults to 0.
-x PROXY, --proxy PROXY
HTTPS proxy server to use. Example: "-p
https://localhost:8080" WARNING: WILL DISABLE SSL
VERIFICATION.
Proxy server to use. WARNING: WILL DISABLE SSL VERIFICATION.
[example: "-p https://localhost:8080"]
-k KEYWORDS, --keywords KEYWORDS
Filter results by a a list of command separated
keywords. Will do a separate loop for each keyword,
potentially bypassing the 1,000 record limit.
[example: "-k 'sales,human resources,information
technology']
-g, --geoblast Attempts to bypass the 1,000 record search limit by
running multiple searches split across geographic
regions.
Filter results by a a list of command separated keywords.
Will do a separate loop for each keyword,
potentially bypassing the 1,000 record limit.
[example: "-k 'sales,human resources,information technology']
-g, --geoblast Attempts to bypass the 1,000 record search limit by running
multiple searches split across geographic regions.
-o OUTPUT, --output OUTPUT
Output Directory, defaults to li2u-output
```


### Examples
You'll need to provide the tool with LinkedIn's company name. You can find that by looking at the URL for the company's page. It should look something like `https://linkedin.com/company/targetco`. It may or may not be as simple as the exact name of the company.

Here's an example to pull all employees of targetco:

```
$ python linkedin2username.py -c targetco
```

Here's an example to pull a shorter list and append the domain name @targetco.com to them:

```
$ python linkedin2username.py -c targetco -d 5 -n 'targetco.com'
```

### Tips

Use an account with a lot of connections, otherwise you'll get crappy results. Adding a couple connections at the target company should help - this tool will work up to third degree connections. Note that [LinkedIn will cap search results](https://www.linkedin.com/help/linkedin/answer/129/what-you-get-when-you-search-on-linkedin?lang=en) to 1000 employees max. You can use the features '--geoblast' or '--keywords' to bypass this limit. Look at help below for more details.

## Toubleshooting

When LinkedIn changes things, the tool may break. The API used here is not documented, and it may take some fiddling around to get it working again. Please open issues if you notice something weird.

You can verify Selenium works on your machine like this:

```
$ python3
from selenium import webdriver
driver = webdriver.Firefox() # or webdriver.Chrome()
driver.get("https://linkedin.com/login")
```

# Toubleshooting
Sometimes LinkedIn does weird stuff or returns weird results. Sometimes it doesn't like you logging in from new locations. If something looks off, run the tool once or twice more. If it still isn't working, please open an issue.
You can try the `--proxy` flag to inspect traffic with Burp. Right now, it is not inspecting the logins from the Selenium browser as you can see pretty clearly what is happening there.

*This is a security research tool. Use only where granted explicit permission from the network owner.*
Binary file added drawing.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading