How Holehe Uses the Forgotten Password Function for Account Detection
Holehe determines whether an email address is registered on a service by simulating password-recovery ("forgotten password") requests and analyzing the server's response for account-existence indicators.
Holehe is an open-source OSINT (Open Source Intelligence) tool maintained by megadose that verifies email registrations across hundreds of web services. Rather than attempting to breach accounts, Holehe exploits the information leakage inherent in password-recovery workflows to infer whether an email exists in a service's user database. This article examines the technical implementation of how Holehe leverages forgotten-password endpoints to perform non-intrusive account detection.
Module Architecture for Password Recovery Detection
Every service module in Holehe implements a standardized asynchronous function signature designed to support the password-recovery detection method. Each module defines an async def <service>(email, client, out) function where the method variable is explicitly set to the string "password recovery".
This architectural pattern appears consistently across the codebase. For example, in holehe/modules/software/adobe.py and holehe/modules/mails/mail_ru.py, the function begins by declaring method = "password recovery". This flag serves two purposes: it documents the detection mechanism for that module, and it enables the core engine to filter modules based on user preferences.
The out parameter is a shared list that collects results from all concurrent modules. Each function appends a standardized dictionary containing keys such as name, domain, method, exists, emailrecovery, and rateLimit.
HTTP Request Simulation Techniques
Holehe constructs requests that mirror legitimate user behavior when requesting a password reset. The tool uses a shared httpx.AsyncClient instance created in holehe/core.py (lines 151–154) to manage persistent connections and timeouts across all modules.
When implementing the forgotten-password check, modules typically perform the following steps:
- Gather prerequisites – Some services require an initial GET request to harvest CSRF tokens or session cookies before submitting the recovery form.
- Craft headers – Modules set realistic
User-Agent,Referer, andContent-Typeheaders to avoid bot detection. The User-Agent is often randomized from a pool of browser signatures. - Submit the target email – The email address under investigation is placed in the request body (usually as form-data or JSON) or query string, depending on the service's API design.
The request targets the same endpoint that a browser would hit when a legitimate user clicks "Forgot Password."
Response Analysis and Existence Signatures
After submitting the password-recovery request, Holehe analyzes the HTTP response to determine account status. The parsing logic looks for specific string patterns or JSON fields that indicate whether the email is registered.
In holehe/modules/mails/google.py, the module inspects the response for specific markers in the returned JavaScript or JSON:
- The presence of
'"gf.wuar",2'indicates the email exists and has a valid account. - The strings
'"gf.wuar",1'or"EmailInvalid"indicate the email is not found or invalid.
Modules apply similar pattern matching across different services:
- Existence confirmed: Phrases like
"Email already in use","Account found", or service-specific success messages. - Non-existence: Responses containing
"Email not found","No account associated", or similar negative indicators. - Rate limiting: If the response indicates throttling (HTTP 429, CAPTCHA challenges, or specific error messages), the module sets
"rateLimit": Trueand"exists": Falseto avoid false positives.
CLI Control: Disabling Password Recovery Checks
Holehe provides users with granular control over which detection methods to employ. The CLI accepts the -NP or --no-password-recovery flag, defined in holehe/core.py (lines 190–191).
When this flag is present, the get_functions utility in the core filters the module list, excluding any function where method == "password recovery". This allows security researchers to run Holehe against registration endpoints only, avoiding the potential side effect of triggering password-reset emails to the target address.
To skip password-recovery checks entirely:
holehe user@example.com --no-password-recovery
To include all detection methods (default behavior):
holehe user@example.com
Practical Implementation Example
The following simplified example from holehe/modules/software/adobe.py demonstrates the forgotten-password detection pattern:
async def adobe(email, client, out):
method = "password recovery"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0",
"Referer": "https://account.adobe.com/",
"Content-Type": "application/x-www-form-urlencoded"
}
# Initial request to gather CSRF token (if required)
# ... token extraction logic ...
payload = {
"email": email,
"action": "forgotPassword"
}
resp = await client.post(
"https://account.adobe.com/realm-password-recovery",
headers=headers,
data=payload
)
if "account exists" in resp.text.lower() or "password reset" in resp.text.lower():
out.append({
"name": "adobe",
"domain": "adobe.com",
"method": method,
"exists": True,
"rateLimit": False,
"emailrecovery": None,
"phoneNumber": None
})
else:
out.append({
"name": "adobe",
"domain": "adobe.com",
"method": method,
"exists": False,
"rateLimit": False,
"emailrecovery": None,
"phoneNumber": None
})
Summary
- Method Identification: Each password-recovery module sets
method = "password recovery"to flag its detection technique. - Request Simulation: Modules use
httpx.AsyncClientto send forged password-reset requests with authentic headers and proper CSRF handling. - Response Parsing: Holehe detects account existence by searching for specific JSON flags or HTML strings in the recovery endpoint's response, as seen in
holehe/modules/mails/google.py. - User Control: The
--no-password-recoveryflag inholehe/core.pyallows users to skip these checks entirely, filtering modules via theget_functionslogic. - Non-Intrusive: This technique only queries public password-recovery APIs without attempting authentication or account access.
Frequently Asked Questions
How does Holehe determine if an email exists without logging in?
Holehe exploits the information leakage present in password-recovery workflows. When a user submits an email to a "Forgot Password" form, the server typically responds with different messages depending on whether the account exists. Holehe programmatically submits the target email to these endpoints and parses the response for existence indicators like "account found" or specific JSON codes, allowing it to infer registration status without credentials.
Can I run Holehe without triggering password reset emails?
Yes. Use the --no-password-recovery (or -NP) flag when running Holehe from the command line. According to holehe/core.py (lines 190–191), this argument filters out all modules where method is set to "password recovery", ensuring Holehe only uses registration-based checks and avoids sending password-reset emails to the target address.
What happens if a service rate-limits the password recovery check?
When Holehe detects rate-limiting (HTTP 429 status codes, CAPTCHA pages, or specific error messages in the response), the affected module appends a result with "rateLimit": True and "exists": False to the output list. This prevents false positives while flagging the service as potentially blocking automated checks, allowing users to retry with different network conditions or timing.
Which file contains the logic to skip password recovery modules?
The filtering logic resides in holehe/core.py within the get_functions utility (around lines 190–191 for argument parsing). This function inspects each module's method attribute and excludes those marked as "password recovery" when the user supplies the --no-password-recovery flag, dynamically building the list of checks to execute.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →