Skip to content

Security questions

Security questions (“knowledge-based authentication”) are a weak second factor that survives in account-recovery flows. Two attack paths:

# Path A - answer is OSINT-recoverable
"What is your mother's maiden name?" → LinkedIn family history, public records
"What city were you born in?" → Facebook About section, employee bio
"What was the name of your first pet?" → Instagram, family posts
"What high school did you attend?" → LinkedIn education
"What is your favorite color?" → ~12 candidates, brute-forceable
# Path B - brute-force the answer space
Color: red, blue, green, yellow, ... → ~30 attempts max
Year: 1950-2010 → ~60 attempts
Sports team: top 50 in the country → ~50 attempts

Success indicator: reset flow completes without legitimate identity verification - application accepts the OSINT-derived or brute-forced answer and lets the operator set a new password.

Security questions are popular with developers and product teams because they’re cheap and avoid the complexity of email/SMS-based recovery. They survive in:

  • Self-service account recovery on consumer apps
  • Call-center identity verification (operator reads the question to the caller)
  • Pre-MFA enterprise apps that haven’t been retrofitted
  • Banking applications, especially during account-opening recovery flows

NIST 800-63B explicitly recommends against security questions as a factor. They show up in audit findings regularly but compliance pressure varies - many applications keep them as a fallback path even when their primary flow has been hardened.

The question themes cluster heavily - and the themes happen to map to information people post publicly.

Question themePublic sources
Mother’s maiden nameGenealogy sites (FamilySearch, Ancestry leaks), Facebook family tags
City of birthLinkedIn About, Facebook About, employee bios on company sites
High schoolLinkedIn education, alumni groups
CollegeLinkedIn education
First petInstagram, Facebook (especially memorial posts)
Father’s nameGenealogy, obituaries, Facebook
Spouse’s nameFacebook relationship status, wedding announcements
Childhood streetPublic-records databases, voter rolls
First carLong-form interviews, blog posts

The attacker’s job is to assemble this from existing public sources. A typical real engagement:

  1. LinkedIn for college, high school, current and previous employer locations
  2. Facebook for family relationships and birthplace
  3. Instagram for pet names (often in handle or bio)
  4. Local-newspaper archive search for obituaries (which list extended family - mother’s maiden name in particular)

When the answer is recoverable, the attack is one-shot - submit the answer, get the reset.

Path B - Brute-forcing low-entropy answers

Section titled “Path B - Brute-forcing low-entropy answers”

Some answers have small natural spaces:

# Favorite color
red, orange, yellow, green, blue, purple, pink, black, white, gray, brown, ~10 more
# Total: ~15-25 plausible answers
# Year of birth (for adults)
1940-2010 → 71 candidates
# Favorite season
spring, summer, autumn, fall, winter → 5
# Favorite number
1-100 covers most users → 100 candidates
# Mother's first name (US English, common names)
top-100 first names by decade of birth → ~200 candidates depending on target age
# High school mascot
top sports team names + state-specific patterns → ~500 candidates

When the application has no rate limit on security-question attempts, brute-forcing these spaces is fast.

Terminal window
# Submit a few wrong answers in quick succession, observe response changes
for guess in red blue green yellow purple; do
curl -s -X POST -d "answer=$guess" https://<TARGET>/recover/step2
echo "---"
done

If responses are identical for 5 wrong guesses, the endpoint isn’t rate-limiting. Run a real wordlist:

Terminal window
ffuf -w colors.txt:ANSWER \
-u https://<TARGET>/recover/step2 \
-X POST -d "answer=ANSWER" \
-fr "Incorrect"

Better-designed applications present a different question on each failed attempt:

Attempt 1: What is your mother's maiden name? → wrong → "let's try another question"
Attempt 2: What was the name of your first pet?
Attempt 3: In what city were you born?

The intent: an attacker who knows the answer to one question doesn’t get to keep trying it; they have to know multiple answers. In practice, the attacker collects the rotation, identifies the easiest brute-force target, then engineers attempts to land on that question.

import requests
questions_seen = set()
for attempt in range(20):
# Trigger a fresh reset (often requires re-submitting the username)
requests.post(url, data={"user": "victim"})
# Get the recovery page, extract the question
page = requests.get(url + "/recover").text
# Parse the question (selector depends on app - example below)
import re
match = re.search(r'<label class="question">([^<]+)</label>', page)
if match:
questions_seen.add(match.group(1))
# Submit a deliberate wrong answer to rotate
requests.post(url + "/recover", data={"answer": "garbage"})
print(f"Found {len(questions_seen)} unique questions:")
for q in questions_seen:
print(f" - {q}")

After 20-50 cycles you usually have the entire question pool. Pick the easiest target (lowest entropy, easiest OSINT) and re-cycle until that question appears.

The application has to track which question it asked between requests. The state lives in:

  • The session cookie (most common) - drop and re-acquire the cookie to reset
  • A hidden form field (sometimes) - submit only when the desired question is presented
  • Server-side per-IP state - header smuggling resets the state

If the state is in a hidden form field, the attacker can fix it: submit only the answer-form variant that includes the desired question’s identifier, forcing the validation to check the answer against that specific question.

A subtle but common bug: the application rotates the visible question on failed attempts, but always validates against the first question regardless. The attacker can:

  1. Trigger a reset, see the first question, fail it deliberately
  2. See the second question (rotation), submit the answer to the first question
  3. If validation accepts → the application is checking the answer against the first question, not the displayed one
  4. Cycle until the easiest-to-answer first question appears

Trigger a reset on your own account. Note the first question. Submit the wrong answer. When the second question appears, submit the first question’s correct answer - if the reset succeeds, the rotation is cosmetic.

Probing without committing to a real attempt:

Terminal window
# Identify whether the reset flow has security questions at all
curl -s https://<TARGET>/forgot-password | grep -iE '(question|security|verify)'
# See whether attempts are rate-limited
for i in 1 2 3 4 5; do
curl -s -X POST -d "answer=test$i" https://<TARGET>/recover
done

For target users you’ve identified, a quick OSINT pass:

Terminal window
# Search engine basics
"<Full Name>" site:linkedin.com
"<Full Name>" site:facebook.com
"<Full Name>" site:instagram.com
# Specific high-value searches
"<Full Name>" "born in"
"<Full Name>" mother
"<Full Name>" obituary
# Username-style searches if you only have a handle
"<username>" mother OR father OR family
# Email-derived
# If you have an email, search for it on Have I Been Pwned-style services
# and the leaked databases that often accompany pwned-account dumps

A practiced operator can usually answer 2-3 of the canonical questions for a given target in 15-30 minutes of OSINT.

  • Some applications have moved to “give us a random fact about yourself.” Free-form security questions chosen by the user are theoretically stronger but practically weaker - users pick easily-guessable facts, and the attacker doesn’t have to research what questions exist.
  • Call-center attacks use the same answers. When the application’s documented security questions are weak, social engineering against the call center using the same answers is often easier than the web-form attack. Out of web-app scope but worth noting.
  • MFA mostly kills this. When a real second factor is required (TOTP, FIDO2), security questions either go away or become a non-blocking step. The attack surface here is concentrated in applications that haven’t deployed MFA, or apps that allow security questions to bypass MFA in recovery scenarios - that last pattern is the worst and worth checking specifically.
  • The application might disclose which questions are wrong vs. which questions are unknown. “That’s not the answer to your first question” is informational; “your answer is incorrect” is generic. Verbose responses tell you whether you’re hitting valid questions.