[add] redirectUriCheckController
This commit is contained in:
parent
d35af82aae
commit
9c4b53a6bc
27 changed files with 1235 additions and 161 deletions
145
packages/backend/dist/index.js
vendored
145
packages/backend/dist/index.js
vendored
|
|
@ -1,20 +1,110 @@
|
|||
// packages/backend/src/index.ts
|
||||
// packages/backend/src/controller/PKCECheck.ts
|
||||
import { RequestSpec } from "caido:utils";
|
||||
var PKCECheck = class {
|
||||
async test(sdk, req) {
|
||||
const method = req.getMethod();
|
||||
if (method !== "GET") {
|
||||
sdk.console.log("[PKCEDowngradeCheck] Not a GET request. Skipping.");
|
||||
return false;
|
||||
}
|
||||
const query = req.getQuery();
|
||||
const searchParams = new URLSearchParams(query);
|
||||
const requiredKeys = ["client_id", "response_type", "code_challenge", "code_challenge_method"];
|
||||
if (!requiredKeys.every((key) => searchParams.has(key))) {
|
||||
sdk.console.log("[PKCEDowngradeCheck] Required PKCE parameters missing. Skipping.");
|
||||
return false;
|
||||
}
|
||||
const url = req.getUrl();
|
||||
const isOpenID = searchParams.get("scope")?.includes("openid") || url.includes("id_token");
|
||||
const methodVal = searchParams.get("code_challenge_method");
|
||||
const challengeVal = searchParams.get("code_challenge");
|
||||
if (!methodVal || !challengeVal) {
|
||||
sdk.console.log("[PKCEDowngradeCheck] code_challenge or method missing. Skipping.");
|
||||
await sdk.findings.create({
|
||||
title: isOpenID ? "[WARN] OpenID Flow PKCE Parameters Missing" : "[WARN] OAuth2 Flow PKCE Parameters Missing",
|
||||
description: `PKCE parameters are missing or incomplete for ${url}. This may indicate a misconfiguration.`,
|
||||
request: req,
|
||||
reporter: "PKCE Checker"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (methodVal === "plain") {
|
||||
sdk.console.log("[PKCEDowngradeCheck] code_challenge_method is 'plain'. Skipping.");
|
||||
await sdk.findings.create({
|
||||
title: isOpenID ? "[WARN] OpenID Flow PKCE Method is 'plain'" : "[WARN] OAuth2 Flow PKCE Method is 'plain'",
|
||||
description: `PKCE method is set to 'plain' for ${url}. This may indicate a downgrade vulnerability.`,
|
||||
request: req,
|
||||
reporter: "PKCE Checker"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
searchParams.delete("code_challenge");
|
||||
searchParams.delete("code_challenge_method");
|
||||
const downgradedQuery = searchParams.toString();
|
||||
const scheme = req.getUrl().startsWith("https") ? "https" : "http";
|
||||
const downgradedUrl = `${scheme}://${req.getHost()}:${req.getPort()}${req.getPath()}?${downgradedQuery}`;
|
||||
sdk.console.log(`${req.getHost()} Original URL: ` + url);
|
||||
sdk.console.log(`${req.getHost()} Downgraded URL: ` + downgradedUrl);
|
||||
try {
|
||||
const spec = new RequestSpec(downgradedUrl);
|
||||
spec.setBody(req.getBody());
|
||||
for (const [key, value] of Object.entries(req.getHeaders())) {
|
||||
if (Array.isArray(value)) {
|
||||
spec.setHeader(key, value.join(", "));
|
||||
} else {
|
||||
spec.setHeader(key, value);
|
||||
}
|
||||
}
|
||||
spec.setHost(req.getHost());
|
||||
spec.setMethod(req.getMethod());
|
||||
spec.setPath(req.getPath());
|
||||
spec.setQuery(downgradedQuery);
|
||||
spec.setTls(req.getTls());
|
||||
spec.setPort(req.getPort());
|
||||
let sendDowngradedRequest = await sdk.requests.send(spec);
|
||||
if (sendDowngradedRequest.response) {
|
||||
let domain = spec.getHost();
|
||||
let port = spec.getPort();
|
||||
let path2 = spec.getPath();
|
||||
let query2 = spec.getQuery();
|
||||
let id = sendDowngradedRequest.response.getId();
|
||||
let code = sendDowngradedRequest.response.getCode();
|
||||
sdk.console.log(`REQ ${id}: ${domain}:${port}${path2}${query2} received a status code of ${code}`);
|
||||
}
|
||||
if (sendDowngradedRequest.response?.getCode() === 302) {
|
||||
await sdk.findings.create({
|
||||
title: isOpenID ? "[CRITICAL] OpenID Flow PKCE Downgrade Vulnerability" : "[CRITICAL] OAuth2 Flow PKCE Downgrade Vulnerability",
|
||||
description: `The request to ${url} is vulnerable to a PKCE downgrade attack. This may indicate a configuration error.`,
|
||||
request: req,
|
||||
reporter: "PKCE Checker"
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
sdk.console.error(`PKCE downgrade check failed for ${url}: ${String(err)}`);
|
||||
}
|
||||
sdk.console.log("[PKCEDowngradeCheck] No PKCE downgrade detected.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// packages/backend/src/controller/redirectUriCheck.ts
|
||||
import { promises as fs } from "fs";
|
||||
import * as path from "path";
|
||||
import os from "os";
|
||||
var requestMap = /* @__PURE__ */ new Map();
|
||||
function init(sdk) {
|
||||
sdk.events.onInterceptRequest(async (sdk2, req) => {
|
||||
var redirectUriCheck = class {
|
||||
requestMap = /* @__PURE__ */ new Map();
|
||||
// constructor(private sdk: SDK<any>) {}
|
||||
async onRequest(sdk, req) {
|
||||
try {
|
||||
const urlString = req.getUrl();
|
||||
const url = new URL(urlString);
|
||||
sdk2.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
||||
sdk.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
|
||||
if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
|
||||
const params = new URLSearchParams(url.search);
|
||||
const redirectUri = params.get("redirect_uri");
|
||||
if (!redirectUri) return;
|
||||
const reqId = req.getId();
|
||||
requestMap.set(reqId, redirectUri);
|
||||
this.requestMap.set(reqId, redirectUri);
|
||||
const clientId = params.get("client_id") ?? "(missing)";
|
||||
const responseType = params.get("response_type") ?? "(missing)";
|
||||
const isScan = params.has("scan");
|
||||
|
|
@ -29,14 +119,14 @@ function init(sdk) {
|
|||
const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
|
||||
await fs.writeFile(filePath, JSON.stringify(output, null, 2));
|
||||
} catch (err) {
|
||||
await sdk2.findings.create({
|
||||
await sdk.findings.create({
|
||||
title: "[fs] Write Failed",
|
||||
description: `Could not write to file: ${err}`,
|
||||
request: req,
|
||||
reporter: "oauth-open-redirect-detector"
|
||||
});
|
||||
}
|
||||
await sdk2.findings.create({
|
||||
await sdk.findings.create({
|
||||
title: "[ ] OAuth2 Authorization Request Collected",
|
||||
description: `client_id: ${clientId}
|
||||
redirect_uri: ${redirectUri}
|
||||
|
|
@ -45,10 +135,10 @@ response_type: ${responseType}`,
|
|||
reporter: "oauth-open-redirect-detector"
|
||||
});
|
||||
} catch (err) {
|
||||
sdk2.console.error(`Error in onInterceptRequest: ${err}`);
|
||||
sdk.console.error(`Error in onRequest: ${err}`);
|
||||
}
|
||||
});
|
||||
sdk.events.onInterceptResponse(async (sdk2, req, resp) => {
|
||||
}
|
||||
async onResponse(sdk, req, resp) {
|
||||
try {
|
||||
const reqId = req.getId();
|
||||
const url = new URL(req.getUrl());
|
||||
|
|
@ -57,26 +147,43 @@ response_type: ${responseType}`,
|
|||
const params = new URLSearchParams(url.search);
|
||||
const isScan = params.has("scan");
|
||||
if (!isScan) {
|
||||
requestMap.delete(reqId);
|
||||
this.requestMap.delete(reqId);
|
||||
return;
|
||||
}
|
||||
if (status >= 300 && status < 400 && location) {
|
||||
const redirectUri = requestMap.get(reqId) ?? "(unknown)";
|
||||
await sdk2.findings.create({
|
||||
const redirectUri = this.requestMap.get(reqId) ?? "(unknown)";
|
||||
await sdk.findings.create({
|
||||
title: "[+] Redirect URI Misconfiguration Detected",
|
||||
description: `Status: ${status}
|
||||
Location: ${location}
|
||||
Original Redirect URI: ${redirectUri}
|
||||
Request URL: ${url.href}`,
|
||||
Request URL: ${url.href}
|
||||
Redirect URI: ${redirectUri}`,
|
||||
request: req,
|
||||
reporter: "oauth-open-redirect-detector"
|
||||
});
|
||||
}
|
||||
requestMap.delete(reqId);
|
||||
this.requestMap.delete(reqId);
|
||||
} catch (err) {
|
||||
sdk2.console.error(`Error in onInterceptResponse: ${err}`);
|
||||
sdk.console.error(`Error in onResponse: ${err}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// packages/backend/src/index.ts
|
||||
var pkceCheckController = new PKCECheck();
|
||||
var redirectUriCheckController = new redirectUriCheck();
|
||||
function init(sdk) {
|
||||
sdk.events.onInterceptRequest(
|
||||
async (sdk2, req) => {
|
||||
await redirectUriCheckController.onRequest(sdk2, req);
|
||||
}
|
||||
);
|
||||
sdk.events.onInterceptResponse(
|
||||
async (sdk2, req, resp) => {
|
||||
await pkceCheckController.test(sdk2, req);
|
||||
await redirectUriCheckController.onResponse(sdk2, req, resp);
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
init
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue