Microsoft Graph eXploitation toolkit. Abusing the Microsoft Graph SDK to search and harvest SharePoint files, Outlook mail, Teams messages and lot of M365 things.
Prefer using uv, a fast Python package manager that installs tools in isolated environments. Alternatively, pipx or pip work as well.
With uv
uv tool install persistently installs the tool and adds it to your PATH, similar to pipx:
From GitHub (latest):
uv tool install git+https://github.com/n3rada/msgraphx.gitAfter installation, msgraphx is available directly:
msgraphx --helpTo upgrade later:
uv tool upgrade msgraphxTip
You can also run msgraphx without installing it using uvx, which creates a temporary isolated environment on the fly:
uvx --from git+https://github.com/n3rada/msgraphx.git msgraphx --helppipx install 'git+https://github.com/n3rada/msgraphx.git'pip install 'git+https://github.com/n3rada/msgraphx.git'msgraphx supports two authentication modes.
Required for any module that acts on behalf of a user (e.g., Outlook, SharePoint search as yourself).
The easiest way to get a valid token is msauth-browser, which drives a real Chromium browser through the full OAuth PKCE flow and handles MFA, Conditional Access, and CAPTCHAs transparently:
# authenticate as Graph Explorer and save tokens in .roadtools_auth
msauth-browser --save roadtoolsThen run msgraphx from the same directory - it will pick up .roadtools_auth automatically:
msgraphx outlook contactsAlternatively, pass the token directly or via environment variable:
# via flag
msgraphx --access-token <JWT> outlook contacts
# via env var
export ACCESS_TOKEN=<JWT>
msgraphx outlook contactsFor app-only flows (service principals with client credentials):
msgraphx --tenant-id <tid> --client-id <cid> --client-secret <secret> sp search "password"Or via environment variables:
export TENANT_ID=<tid>
export CLIENT_ID=<cid>
export CLIENT_SECRET=<secret>
msgraphx sp search "password"Note
App-only tokens cannot use the /me endpoint. Modules that require a user context (Outlook) only work with delegated auth.
Note
All modules default to the last year (--after 1y). Pass --all to remove the time bound, or --after/--before to set a custom range. These are global flags available to every subcommand.
Build a full communication graph from your mailbox. By default analyses both sent and received mail across four ranked tables:
- π€ Sent β To β who you direct-email most
- π€ Sent β CC β who you copy most
- π₯ Received β as To β who emails you directly most
- π₯ Received β as CC β who copies you most
msgraphx outlook contacts
# or
msgraphx mail contactsRestrict to sent or received only:
msgraphx mail contacts --only sent
msgraphx mail contacts --only receivedFetch everything with no time bound:
msgraphx mail contacts --allLimit to the last 90 days and show top 50:
msgraphx mail contacts --after 90d --top 50Save the full ranked list as JSON:
msgraphx mail contacts --save /tmp/contacts.jsonSearch your mailbox using KQL. Streams up to 1 000 results (Exchange API cap):
msgraphx outlook search "password"
msgraphx outlook search --from alice@corp.com
msgraphx outlook search --subject "VPN" --has-attachments
msgraphx outlook search "credentials" --after 90dResults are cached locally (~/.local/share/msgraphx/last_mail.json).
Download specific emails as .eml files from the last search:
# First, search
msgraphx outlook search "password"
# 1. RE: VPN config alice@corp.com 2025-03-12
# 2. FW: passwords bob@corp.com 2025-01-08
# Then, download by index
msgraphx outlook download 1
msgraphx outlook download 1,2
msgraphx outlook download 1-5 --save /tmp/loot/.eml files open natively in most mail clients.
Search for anything across SharePoint. Defaults to the last year:
msgraphx sp search "password"Filter by filetype:
msgraphx sp search --filetype pdf
msgraphx sp search -f docx "confidential"Use predefined hunt queries:
msgraphx sp search --hunt credentials
msgraphx sp search --hunt ssh --all
msgraphx sp search --hunt office --after 90dSearch only within your own Microsoft 365 groups:
msgraphx sp search "password" --my-groupsSave all results to disk:
msgraphx sp search --hunt credentials --save /tmp/loot/Download specific files from the last search by index:
# First, search
msgraphx sp search "Itron" --filetype pdf
# 1. Itron_Report_2024.pdf jsmith 2.1 MB 2024-11-03
# 2. Itron_Specs.pdf jdoe 840 KB 2024-09-15
# 3. Itron_Invoice.pdf admin 120 KB 2025-01-20
# Then, download by index
msgraphx sp download 2
msgraphx sp download 1,3
msgraphx sp download 1-3Each search caches results locally (~/.local/share/msgraphx/last_sharepoint.json). A new search always replaces the previous cache.
Download to a specific directory:
msgraphx sp download 1-3 --save /tmp/loot/Full drive dump (requires --drive-id):
msgraphx --drive-id <drive-id> sp download --save /tmp/loot/Resume interrupted downloads (default behaviour, skips files already present with matching size):
msgraphx --drive-id <drive-id> sp download --save /tmp/loot/Force re-download everything:
msgraphx --drive-id <drive-id> sp download --no-resume --save /tmp/loot/Requires delegated auth. Both subcommands use POST /search/query (EntityType.ChatMessage) and need ChannelMessage.Read.All (admin consent) in addition to Chat.Read.
Search 1:1 DMs and group chats:
# keyword search
msgraphx teams chat "password"
msgraphx teams chat "vpn credentials"
# sender filter (client-side)
msgraphx teams chat "budget" --from alice
# date range
msgraphx teams chat "aws key" --after 90d
msgraphx teams chat "deploy" --after 2024-01-01 --before 2024-06-01
# wildcard β return everything
msgraphx teams chatResults are cached locally (~/.local/share/msgraphx/last_teams.json).
Search messages across all Teams channels you have access to:
msgraphx teams channel "password"
msgraphx teams channel "from:alice@corp.com"
msgraphx teams channel "incident" --after 30dKQL is passed directly to the Search API, so any valid KQL expression works:
msgraphx teams channel "subject:deployment AND azure"Show context around a cached result, or browse a named chat directly. The argument auto-detects the mode: a number/range opens the cache, any other string finds a chat.
# cached result (index from last search)
msgraphx teams show 3
msgraphx teams show 1-5
msgraphx teams show 2 --context 8
# browse a named chat (topic or member name match, last 20 by default)
msgraphx teams show alice
msgraphx teams show "project phoenix" --last 50
msgraphx teams show alice --last 5Graph Explorer is Microsoft's interactive API sandbox. It lets you run live queries against the Graph API, inspect raw responses, and generate ready-to-paste Python SDK code snippets for any request via the Code snippets tab.
Use it to prototype a query before implementing it as a module, or to understand exactly what fields a response contains.