Update open_redirect_check.py

This commit is contained in:
gyuu04 2025-07-17 13:59:05 +09:00
commit 7d378fa91f

View file

@ -1058,7 +1058,7 @@ class OpenRedirectChecker:
self.session = None self.session = None
""" 우회된 URL에 GET 요청을 보내고, 서버 응답 정보 반환 """ """ 우회된 URL에 GET 요청을 보내고, 서버 응답 정보 반환 """
async def _send_request(self, url, headers=None): async def _send_request(self, url, headers=None, record_to_limiter=True):
try: try:
session = await self._get_session() # 세션 준비 session = await self._get_session() # 세션 준비
request_headers = headers or {} # 요청 헤더 설정 - headers가 주어지지 않았다면 빈 딕셔너리 사용 request_headers = headers or {} # 요청 헤더 설정 - headers가 주어지지 않았다면 빈 딕셔너리 사용
@ -1072,10 +1072,11 @@ class OpenRedirectChecker:
} }
# 성공/실패 기록 (rate limiter용) # 성공/실패 기록 (rate limiter용)
if response.status in [200, 301, 302, 303, 307, 308]: if record_to_limiter:
redirect_limiter.record_success() if response.status in [200, 301, 302, 303, 307, 308]:
else: redirect_limiter.record_success()
redirect_limiter.record_failure(f"HTTP {response.status}") else:
redirect_limiter.record_failure(f"HTTP {response.status}")
return result return result
@ -1116,6 +1117,9 @@ class OpenRedirectChecker:
'/oauth/callback', '/auth/callback', '/login/callback', '/oauth/callback', '/auth/callback', '/login/callback',
'/oauth/redirect', '/auth/return', '/sso/callback', '/oauth/redirect', '/auth/return', '/sso/callback',
'/signup', '/register', '/signup', '/register',
'/api/auth/v3/social/callback', # velog.io
'/social/callback', # 일반화
'/auth/social/callback', # 변형
] ]
path = parsed.path.lower() path = parsed.path.lower()
@ -1134,7 +1138,11 @@ class OpenRedirectChecker:
import re import re
# 패턴 1: 간단한 소셜 식별자 # 패턴 1: 간단한 소셜 식별자
simple_patterns = ['from=fb', 'from=facebook', 'from=google', 'from=github'] simple_patterns = [
'from=fb', 'from=facebook', 'from=google', 'from=github',
'provider=google', 'provider=facebook',
'callback/google', 'callback/github', 'callback/facebook' # ← URL 경로 패턴
]
has_simple = any(pattern in query_string.lower() for pattern in simple_patterns) has_simple = any(pattern in query_string.lower() for pattern in simple_patterns)
# 패턴 2: URL 형태 # 패턴 2: URL 형태
@ -1151,7 +1159,7 @@ class OpenRedirectChecker:
'next', 'return_to', 'continue', 'redirect_uri', 'redirect_url', 'next', 'return_to', 'continue', 'redirect_uri', 'redirect_url',
'destination', 'success_url', 'callback_url', 'goto', 'forward_to', 'destination', 'success_url', 'callback_url', 'goto', 'forward_to',
'redirectUrl', 'redirectURL', 'redirect_to', 'returnUrl', 'returnURL', 'redirectUrl', 'redirectURL', 'redirect_to', 'returnUrl', 'returnURL',
'from', 'target', 'targetUrl', 'targetURL' # 일반적인 변형들 추가 'from', 'target', 'targetUrl', 'targetURL',
] ]
has_redirect_param = any(param in query for param in client_redirect_params) has_redirect_param = any(param in query for param in client_redirect_params)
@ -1203,7 +1211,11 @@ class OpenRedirectChecker:
'/oauth2/v1/authorize', '/oauth2/v1/authorize',
'/authorize', # 일반적인 authorize 추가 '/authorize', # 일반적인 authorize 추가
'/auth/realms', # Keycloak 패턴 '/auth/realms', # Keycloak 패턴
'/connect/authorize' # IdentityServer 패턴 '/connect/authorize', # IdentityServer 패턴
'/idp/', # ← Identity Provider 패턴
'/signin', # ← Sign-in 패턴
'/auth/signin', # ← Auth Sign-in 패턴
'/identity/', # ← Identity 패턴
] ]
path = parsed.path.lower() path = parsed.path.lower()
@ -1263,6 +1275,9 @@ class OpenRedirectChecker:
if 'state' in query: if 'state' in query:
await self._test_state_parameter_manipulation(url, parsed, query) await self._test_state_parameter_manipulation(url, parsed, query)
# 일반 리다이렉트 파라미터 테스트를 위해 Rate Limiter 리셋
redirect_limiter.reset_for_new_target()
# 2. 테스트할 파라미터들 찾기 # 2. 테스트할 파라미터들 찾기
test_params = [] test_params = []
@ -1289,8 +1304,7 @@ class OpenRedirectChecker:
print(f"[OAUTH] 📍 발견된 파라미터들: {test_params}") print(f"[OAUTH] 📍 발견된 파라미터들: {test_params}")
print(f"[OPEN_REDIRECT] 총 우회 패턴: {len(self.bypass_payloads)}") print(f"[OPEN_REDIRECT] 총 우회 패턴: {len(self.bypass_payloads)}")
print("-" * 50) print("-" * 50)
redirect_limiter.reset_for_new_target()
success_count = 0 success_count = 0
# 3. 각 파라미터별로 모든 우회 패턴 테스트 # 3. 각 파라미터별로 모든 우회 패턴 테스트
@ -1368,7 +1382,7 @@ class OpenRedirectChecker:
test_url = urlunparse(parsed._replace(query=new_query_string)) test_url = urlunparse(parsed._replace(query=new_query_string))
# 요청 전송 # 요청 전송
response = await self._send_request(test_url) response = await self._send_request(test_url, record_to_limiter=False)
# State 전용 취약점 분석 # State 전용 취약점 분석
if await self._analyze_state_vulnerability(url, test_url, malicious_state, response, attack_name): if await self._analyze_state_vulnerability(url, test_url, malicious_state, response, attack_name):
@ -1377,8 +1391,7 @@ class OpenRedirectChecker:
else: else:
print(f"[STATE_TEST] ✓ {attack_name}") print(f"[STATE_TEST] ✓ {attack_name}")
# 레이트 리미팅 await asyncio.sleep(1) # 1초 대기
await redirect_limiter.wait_if_needed(f"state_{attack_name}")
print(f"[STATE_TEST] ✅ State 테스트 완료: {success_count}/{len(state_attacks)} 취약점 발견") print(f"[STATE_TEST] ✅ State 테스트 완료: {success_count}/{len(state_attacks)} 취약점 발견")