From 979dda299a720d9ac4bdaa6284eceb7895cf47fc Mon Sep 17 00:00:00 2001 From: gyuu04 Date: Tue, 3 Jun 2025 14:44:09 +0900 Subject: [PATCH] Update redirect_uriBypass.ts --- .../src/controller/redirect_uriBypass.ts | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/controller/redirect_uriBypass.ts b/packages/backend/src/controller/redirect_uriBypass.ts index f77b324..ce521cb 100644 --- a/packages/backend/src/controller/redirect_uriBypass.ts +++ b/packages/backend/src/controller/redirect_uriBypass.ts @@ -2,42 +2,55 @@ import type { Request, Response } from "caido:utils"; import type { SDK } from "caido:plugin"; export class RedirectBypassController { - isRedirectUri(req: Request): boolean { + // redirect_uri를 확인하는 함수 + isRedirectUri(req: Request): { detected: boolean; redirectUri?: string } { + // ? 뒤에 오는 파라미터 모두 가져오고, 정규표현식으로 redirect_uri= 이후 주소만 뽑음(없으면 null) const query = req.getQuery(); - - // redirect_uri 파라미터 정규식으로 추출 const redirectUriMatch = query.match(/redirect_uri=([^&]+)/i); - if (!redirectUriMatch) return false; - // redirect_uri 파라미터의 URL 문자열을 디코딩 + // redirectUriMatch[1]은 ()로 감싼 부분 + // redirect_uri 파라미터가 없거나 있어도 주소가 문자열이 아니면 false + if (!redirectUriMatch || typeof redirectUriMatch[1] !== "string") { + return { detected: false }; + } + + // 인코딩된 주소를 원래대로 바꿈 (ex. https://~~) const redirectUri = decodeURIComponent(redirectUriMatch[1]); - // 우회 키워드 const bypassPatterns = [ - "%ff@", "/", "%2f@", "%0a@", "%0d@", "\\", ".evil.com", "@", "%2f..%2f" + "%ff@", "/", "%2f@", "%0a@", "%0d@", "\\", ".evil.com", "@", "%2f..%2f", ]; - - return bypassPatterns.some(pattern => redirectUri.includes(pattern)); + + // 위 패턴에 일치하는 게 있으면 true랑 redirectUri 반환 (false일 땐 undefined) + const detected = bypassPatterns.some(pattern => redirectUri.includes(pattern)); + return { detected, redirectUri: detected ? redirectUri : undefined }; } + // 응답에 인가 코드가 포함되어 있는지 확인하는 함수 isCodeIssued(res: Response): boolean { const location = res.getHeader("Location") || ""; return location.includes("code="); } - test(req: Request, res: Response): string | false { - if (this.isRedirectUri(req) && this.isCodeIssued(res)) { - return "redirect_uri bypass detected"; + // 위의 두 함수 모두 만족하면 true, 문제의 주소를 반환하는 함수 + test(req: Request, res: Response): { detected: boolean; redirectUri?: string } { + const redirectCheck = this.isRedirectUri(req); + const codeIssued = this.isCodeIssued(res); + + if (redirectCheck.detected && codeIssued) { + return { detected: true, redirectUri: redirectCheck.redirectUri }; } - return false; + + return { detected: false }; } - async testAsync(sdk: SDK, req: Request, res: Response) { + // 탐지된 결과 저장하는 함수 + async testAsync(sdk: SDK, req: Request, res: Response): Promise { const result = this.test(req, res); - if (result) { + if (result.detected) { await sdk.findings.create({ title: "Redirect URI Bypass Detected", - description: result, + description: `redirect_uri 우회 발견\nRedirect URI: ${result.redirectUri}`, request: req, reporter: "gyu", });