Lucene search
+L

Google Cloud API Console Listener

🗓️ 29 Jun 2026 19:11:00Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 3 Views

Python script for interacting with Google Cloud API Console using Bearer tokens.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import os
import time
from urllib.parse import quote

import requests
from kittysploit import *


DEFAULT_SCOPES = (
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/datastore",
    "https://www.googleapis.com/auth/devstorage.read_only",
)


class GcpApiConsoleConnection:
    def __init__(self, project_id, token, client_email="", scopes=None, timeout=30):
        self.project_id = project_id
        self.token = token
        self.client_email = client_email
        self.scopes = scopes or list(DEFAULT_SCOPES)
        self.timeout = int(timeout or 30)

    def _headers(self):
        return {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
        }

    def request(self, method, url, payload=None, params=None):
        method = method.upper()
        kwargs = {
            "headers": self._headers(),
            "params": params,
            "timeout": self.timeout,
        }
        if payload is not None:
            kwargs["json"] = payload
        response = requests.request(method, url, **kwargs)
        try:
            body = response.json()
        except Exception:
            body = response.text
        return {
            "status_code": response.status_code,
            "ok": response.ok,
            "body": body,
            "url": response.url,
        }

    def _format_response(self, result):
        body = result.get("body")
        if isinstance(body, (dict, list)):
            rendered = json.dumps(body, indent=2, ensure_ascii=False)
        else:
            rendered = str(body or "")
        status = result.get("status_code")
        url = result.get("url", "")
        return f"HTTP {status} {url}\n{rendered}"

    def _project_url(self, api_path):
        return api_path.format(project_id=quote(self.project_id, safe=""))

    def run_command(self, command):
        command = (command or "").strip()
        if not command:
            return ""
        parts = command.split()
        cmd = parts[0].lower()
        args = parts[1:]

        if cmd in ("help", "?"):
            return self.help_text()
        if cmd == "whoami":
            return json.dumps(
                {
                    "project_id": self.project_id,
                    "client_email": self.client_email,
                    "scopes": self.scopes,
                },
                indent=2,
                ensure_ascii=False,
            )
        if cmd == "get":
            if not args:
                return "Usage: get <absolute_url>"
            return self._format_response(self.request("GET", args[0]))
        if cmd == "post":
            if not args:
                return "Usage: post <absolute_url> [json_payload]"
            payload = json.loads(" ".join(args[1:])) if len(args) > 1 else {}
            return self._format_response(self.request("POST", args[0], payload=payload))
        if cmd == "audit":
            return self._run_audit(args)

        route = self._routes().get(cmd)
        if not route:
            return f"Unknown command: {cmd}\n\n{self.help_text()}"

        method, url, payload, params = route(args)
        return self._format_response(self.request(method, url, payload=payload, params=params))

    def _run_audit(self, args):
        include_heavy = "--heavy" in args
        commands = [
            "project",
            "iam_policy",
            "enabled_services",
            "service_accounts",
            "storage_buckets",
            "compute_firewalls",
            "cloud_run_locations",
            "functions_v1",
            "functions_v2",
            "secrets",
            "artifact_repos",
            "cloud_builds",
            "pubsub_topics",
            "pubsub_subscriptions",
            "sql_instances",
            "bigquery_datasets",
            "firestore_collections",
            "firestore_indexes",
            "firebase_apps",
            "firebase_sites",
            "firebase_rules",
            "remote_config",
            "api_keys",
            "logs",
        ]
        if include_heavy:
            commands.extend(["compute_instances", "gke_clusters"])

        results = []
        for name in commands:
            method, url, payload, params = self._routes()[name]([])
            try:
                result = self.request(method, url, payload=payload, params=params)
                results.append(
                    {
                        "name": name,
                        "status_code": result.get("status_code"),
                        "ok": result.get("ok"),
                        "url": result.get("url"),
                        "body": result.get("body"),
                    }
                )
            except Exception as exc:
                results.append({"name": name, "error": str(exc)})

        summary = {}
        for item in results:
            key = str(item.get("status_code") or "error")
            summary[key] = summary.get(key, 0) + 1
        return json.dumps(
            {
                "project_id": self.project_id,
                "client_email": self.client_email,
                "started_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
                "include_heavy": include_heavy,
                "summary": summary,
                "results": results,
            },
            indent=2,
            ensure_ascii=False,
        )

    def _routes(self):
        p = quote(self.project_id, safe="")
        parent_docs = f"projects/{p}/databases/(default)/documents"

        return {
            "project": lambda _a: (
                "GET",
                f"https://cloudresourcemanager.googleapis.com/v1/projects/{p}",
                None,
                None,
            ),
            "iam_policy": lambda _a: (
                "POST",
                f"https://cloudresourcemanager.googleapis.com/v1/projects/{p}:getIamPolicy",
                {},
                None,
            ),
            "enabled_services": lambda _a: (
                "GET",
                f"https://serviceusage.googleapis.com/v1/projects/{p}/services",
                None,
                {"filter": "state:ENABLED", "pageSize": 200},
            ),
            "service_accounts": lambda _a: (
                "GET",
                f"https://iam.googleapis.com/v1/projects/{p}/serviceAccounts",
                None,
                None,
            ),
            "storage_buckets": lambda _a: (
                "GET",
                f"https://storage.googleapis.com/storage/v1/b?project={p}",
                None,
                None,
            ),
            "compute_instances": lambda _a: (
                "GET",
                f"https://compute.googleapis.com/compute/v1/projects/{p}/aggregated/instances",
                None,
                {"maxResults": 50},
            ),
            "compute_firewalls": lambda _a: (
                "GET",
                f"https://compute.googleapis.com/compute/v1/projects/{p}/global/firewalls",
                None,
                {"maxResults": 50},
            ),
            "gke_clusters": lambda _a: (
                "GET",
                f"https://container.googleapis.com/v1/projects/{p}/aggregated/clusters",
                None,
                {"maxResults": 30},
            ),
            "cloud_run_locations": lambda _a: (
                "GET",
                f"https://run.googleapis.com/v1/projects/{p}/locations",
                None,
                None,
            ),
            "cloud_run_jobs": lambda _a: (
                "GET",
                f"https://run.googleapis.com/v2/projects/{p}/locations/-/jobs",
                None,
                None,
            ),
            "functions_v1": lambda _a: (
                "GET",
                f"https://cloudfunctions.googleapis.com/v1/projects/{p}/locations/-/functions",
                None,
                None,
            ),
            "functions_v2": lambda _a: (
                "GET",
                f"https://cloudfunctions.googleapis.com/v2/projects/{p}/locations/-/functions",
                None,
                None,
            ),
            "secrets": lambda _a: (
                "GET",
                f"https://secretmanager.googleapis.com/v1/projects/{p}/secrets",
                None,
                None,
            ),
            "artifact_repos": lambda _a: (
                "GET",
                f"https://artifactregistry.googleapis.com/v1/projects/{p}/locations/-/repositories",
                None,
                None,
            ),
            "cloud_builds": lambda _a: (
                "GET",
                f"https://cloudbuild.googleapis.com/v1/projects/{p}/builds",
                None,
                {"pageSize": 20},
            ),
            "pubsub_topics": lambda _a: (
                "GET",
                f"https://pubsub.googleapis.com/v1/projects/{p}/topics",
                None,
                None,
            ),
            "pubsub_subscriptions": lambda _a: (
                "GET",
                f"https://pubsub.googleapis.com/v1/projects/{p}/subscriptions",
                None,
                None,
            ),
            "sql_instances": lambda _a: (
                "GET",
                f"https://sqladmin.googleapis.com/v1/projects/{p}/instances",
                None,
                None,
            ),
            "bigquery_datasets": lambda _a: (
                "GET",
                f"https://bigquery.googleapis.com/v2/projects/{p}/datasets",
                None,
                None,
            ),
            "firestore_collections": lambda _a: (
                "POST",
                f"https://firestore.googleapis.com/v1/projects/{p}/databases/(default)/documents:listCollectionIds",
                {"parent": parent_docs},
                None,
            ),
            "firestore_indexes": lambda _a: (
                "GET",
                f"https://firestore.googleapis.com/v1/projects/{p}/databases/(default)/collectionGroups/-/indexes",
                None,
                None,
            ),
            "firebase_apps": lambda _a: (
                "GET",
                f"https://firebase.googleapis.com/v1beta1/projects/{p}/webApps",
                None,
                None,
            ),
            "firebase_sites": lambda _a: (
                "GET",
                f"https://firebasehosting.googleapis.com/v1beta1/projects/{p}/sites",
                None,
                None,
            ),
            "firebase_rules": lambda _a: (
                "GET",
                f"https://firebaserules.googleapis.com/v1/projects/{p}/releases",
                None,
                None,
            ),
            "remote_config": lambda _a: (
                "GET",
                f"https://firebaseremoteconfig.googleapis.com/v1/projects/{p}/remoteConfig",
                None,
                None,
            ),
            "api_keys": lambda _a: (
                "GET",
                f"https://apikeys.googleapis.com/v2/projects/{p}/locations/global/keys",
                None,
                None,
            ),
            "logs": lambda _a: (
                "GET",
                f"https://logging.googleapis.com/v2/projects/{p}/logs",
                None,
                {"pageSize": 50},
            ),
        }

    def help_text(self):
        commands = [
            "whoami",
            "audit [--heavy]",
            "project",
            "iam_policy",
            "enabled_services",
            "service_accounts",
            "storage_buckets",
            "compute_instances",
            "compute_firewalls",
            "gke_clusters",
            "cloud_run_locations",
            "cloud_run_jobs",
            "functions_v1",
            "functions_v2",
            "secrets",
            "artifact_repos",
            "cloud_builds",
            "pubsub_topics",
            "pubsub_subscriptions",
            "sql_instances",
            "bigquery_datasets",
            "firestore_collections",
            "firestore_indexes",
            "firebase_apps",
            "firebase_sites",
            "firebase_rules",
            "remote_config",
            "api_keys",
            "logs",
            "get <url>",
            "post <url> [json_payload]",
        ]
        return "Available GCP API commands:\n" + "\n".join(f"  {c}" for c in commands)


class Module(Listener):
    __info__ = {
        "name": "Google Cloud API Console Listener",
        "description": "Creates an interactive GCP/Firebase API console from a token or service account.",
        "author": "KittySploit Team",
        "version": "1.0.0",
        "handler": Handler.BIND,
        "session_type": "gcp_api",
        "protocol": "gcp_api",
        "dependencies": [],
        "optional_dependencies": ["google-auth"],
    }

    project_id = OptString("", "Google Cloud project ID; inferred from service account when empty", False)
    service_account_file = OptString("service-account.json", "Google service account JSON key file", False)
    service_account_json = OptString("", "Raw Google service account JSON key", False, advanced=True)
    access_token = OptString("", "Raw OAuth2 access token", False, advanced=True)
    access_token_file = OptString("", "File containing an OAuth2 access token", False, advanced=True)
    auth_scopes = OptString(",".join(DEFAULT_SCOPES), "Comma-separated OAuth scopes", False, advanced=True)
    test_command = OptString("whoami", "Command used to verify the API console", False)
    timeout = OptInteger(30, "HTTP/API timeout in seconds", False, advanced=True)

    @staticmethod
    def _as_str(value):
        raw = getattr(value, "value", value)
        return str(raw or "").strip()

    @staticmethod
    def _normalize_service_account_info(info):
        if not isinstance(info, dict):
            return info
        private_key = info.get("private_key")
        if isinstance(private_key, str) and "\\n" in private_key:
            info = dict(info)
            info["private_key"] = private_key.replace("\\n", "\n")
        return info

    def _scopes(self):
        scopes = [scope.strip() for scope in self._as_str(self.auth_scopes).split(",") if scope.strip()]
        return scopes or list(DEFAULT_SCOPES)

    def _load_service_account(self):
        raw_json = self._as_str(self.service_account_json)
        if raw_json:
            return self._normalize_service_account_info(json.loads(raw_json))
        path = self._as_str(self.service_account_file)
        if path and os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as handle:
                return self._normalize_service_account_info(json.load(handle))
        return None

    def _token_from_service_account(self, info):
        try:
            from google.auth.transport.requests import Request
            from google.oauth2 import service_account
        except ImportError:
            print_error("google-auth is required for service account authentication")
            return "", ""
        credentials = service_account.Credentials.from_service_account_info(info, scopes=self._scopes())
        credentials.refresh(Request())
        return credentials.token or "", info.get("client_email", "")

    def _resolve_token(self):
        token = self._as_str(self.access_token)
        if token:
            return token, "", self._as_str(self.project_id)
        token_file = self._as_str(self.access_token_file)
        if token_file:
            with open(token_file, "r", encoding="utf-8") as handle:
                return handle.read().strip(), "", self._as_str(self.project_id)
        info = self._load_service_account()
        if info:
            token, email = self._token_from_service_account(info)
            project_id = self._as_str(self.project_id) or str(info.get("project_id") or "")
            return token, email, project_id
        print_error("Set access_token, access_token_file, service_account_file, or service_account_json")
        return "", "", self._as_str(self.project_id)

    def run(self, background=False):
        token, client_email, project_id = self._resolve_token()
        if not token:
            return False
        if not project_id:
            print_error("project_id is required when it cannot be inferred from service account JSON")
            return False

        conn = GcpApiConsoleConnection(
            project_id=project_id,
            token=token,
            client_email=client_email,
            scopes=self._scopes(),
            timeout=int(self.timeout),
        )
        try:
            print_status(f"Testing GCP API console for project {project_id}...")
            output = conn.run_command(self._as_str(self.test_command) or "whoami")
            if output:
                print_info(output[:4000])
            print_success("GCP API console session ready")
            return (
                conn,
                f"gcp-api-{project_id}",
                0,
                {
                    "project_id": project_id,
                    "client_email": client_email,
                    "scopes": self._scopes(),
                    "session_type": "gcp_api",
                    "protocol": "gcp_api",
                },
            )
        except Exception as e:
            print_error(f"GCP API console failed: {e}")
            return False

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 Jun 2026 19:11Current
5.8Medium risk
Vulners AI Score5.8
3