diff --git a/addon/cleintsecret_check.py b/addon/cleintsecret_check.py new file mode 100644 index 0000000..9f431ab --- /dev/null +++ b/addon/cleintsecret_check.py @@ -0,0 +1,64 @@ +# client_secret_check.py +from mitmproxy import http, ctx +from urllib.parse import urlparse, parse_qs +from typing import Optional, List +import lib.target as target +from lib.report import save_report + + +class ClientSecretChecker: + def is_oauth_uri(self, uri: str) -> bool: + qs = parse_qs(urlparse(uri).query) + keys = qs.keys() + return "client_id" in keys and ("client_secret" in keys or "client_secret" in uri) + + def has_client_secret_in_uri(self, uri: str) -> bool: + qs = parse_qs(urlparse(uri).query) + return "client_secret" in qs + + def is_post_with_client_secret(self, flow: http.HTTPFlow) -> bool: + if flow.request.method != "POST": + return False + content_type = flow.request.headers.get("Content-Type", "") + if "application/x-www-form-urlencoded" in content_type: + body = parse_qs(flow.request.get_text()) + return "client_secret" in body + return False + + def is_exposed_in_referer(self, flow: http.HTTPFlow) -> bool: + referer = flow.request.headers.get("Referer", "") + return "client_secret" in referer + + def check_client_secret_leak(self, flow: http.HTTPFlow) -> List[str]: + messages = [] + + if self.has_client_secret_in_uri(flow.request.url): + messages.append("client_secret found in URL query string") + + if self.is_post_with_client_secret(flow): + messages.append("client_secret found in POST body") + + if self.is_exposed_in_referer(flow): + messages.append("client_secret exposed in Referer header") + + return messages + + def response(self, flow: http.HTTPFlow) -> None: + try: + if not self.is_oauth_uri(flow.request.url): + return + + issues = self.check_client_secret_leak(flow) + if issues: + desc = " | ".join(issues) + report_data = [{ + 'target': target.load(), + 'status': "HIGH", + 'title': "OAuth Client Secret Exposure", + 'description': desc, + 'uri': flow.request.url, + }] + save_report(report_data) + print(f"[INFO] Client Secret Check: {desc}") + except Exception as e: + print(f"[ERROR] Client Secret Check failed: {e}") diff --git a/addon/init.py b/addon/init.py index 1ef743d..29e7027 100644 --- a/addon/init.py +++ b/addon/init.py @@ -4,6 +4,7 @@ from pkce_check import PKCEDowngradeChecker from ScopeDetection import ScopeDetection from csrf_check import CsrfChecker from nonce_check import NonceChecker +from cleintsecret_check import ClientSecretChecker class PKCEAddon: def __init__(self): @@ -60,5 +61,16 @@ class NonceAddon: except Exception as e: print(f"[ERROR] NonceAddon failed: {e}") pass + +class ClientSecretAddon: + def __init__(self): + self.checker = ClientSecretChecker() -addons = [PKCEAddon(), ScopeAddon(), CsrfAddon(), NonceAddon()] + async def request(self, flow: http.HTTPFlow): + try: + self.checker.response(flow) + except Exception as e: + print(f"[ERROR] ClientSecretAddon failed: {e}") + pass + +addons = [PKCEAddon(), ScopeAddon(), CsrfAddon(), NonceAddon(), ClientSecretAddon()]