Share
## https://sploitus.com/exploit?id=PACKETSTORM:212459
=============================================================================================================================================
    | # Title     : Azure APIM v 2 Vulnerability Checker                                                                                        |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits)                                                            |
    | # Vendor    : https://learn.microsoft.com                                                                                                 |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/212252/
    
    [+] Summary : The PHP script, apim_vuln_checker.php, performs two main types of checks to determine the security posture of an Azure APIM developer portal
    
    [+]  POC :
    
    <?php
    
    const APIM_API_VERSION = "2022-08-01";
    
    // Color codes (ANSI) - PHP equivalent to colorama
    const C_RESET = "\033[0m";
    const C_RED = "\033[0;31m";
    const C_GREEN = "\033[0;32m";
    const C_BLUE = "\033[0;34m";
    const C_YELLOW = "\033[0;33m";
    const C_CYAN = "\033[0;36m";
    
    // ----------------------------------------------------------------------
    // Logging Functions (Equivalent to Python's log_* methods)
    // ----------------------------------------------------------------------
    
    function log_check(string $message): void
    {
        echo C_BLUE . "[?]" . C_RESET . " $message\n";
    }
    
    function log_vuln(string $message): void
    {
        echo C_RED . "[!]" . C_RESET . " $message\n";
    }
    
    function log_safe(string $message): void
    {
        echo C_GREEN . "[โœ“]" . C_RESET . " $message\n";
    }
    
    function log_info(string $message): void
    {
        echo C_YELLOW . "[i]" . C_RESET . " $message\n";
    }
    
    // Function to handle SSL verification skip (if -k flag is used)
    function get_curl_options(bool $verify_ssl): array
    {
        if (!$verify_ssl) {
            // Disabling SSL verification
            return [
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
            ];
        }
        return [];
    }
    
    // ----------------------------------------------------------------------
    // Class AzureRMChecker (Azure Resource Manager Checks)
    // ----------------------------------------------------------------------
    
    class AzureRMChecker
    {
        private string $subscription_id;
        private string $resource_group;
        private string $service_name;
        private bool $verbose;
        private string $base_url;
        private ?string $token; // Azure Access Token
    
        public function __construct(string $subscription_id, string $resource_group, string $service_name, bool $verbose = false)
        {
            $this->subscription_id = $subscription_id;
            $this->resource_group = $resource_group;
            $this->service_name = $service_name;
            $this->verbose = $verbose;
            $this->base_url = "https://management.azure.com/subscriptions/$subscription_id/resourceGroups/$resource_group/providers/Microsoft.ApiManagement/service/$service_name";
            $this->token = null;
        }
    
        /**
         * Attempts to get Azure access token using the Azure CLI.
         * This is the PHP equivalent approach for the 'DefaultAzureCredential' Python method.
         */
        private function get_azure_token(): ?string
        {
            if (empty($this->token)) {
                log_check("Attempting to get Azure token via 'az account get-access-token'...");
                try {
                    // Execute Azure CLI command to get a token for the management endpoint
                    $command = 'az account get-access-token --resource https://management.azure.com/ --query accessToken --output tsv 2>&1';
                    $token = shell_exec($command);
                    
                    // Check for errors (az cli outputting errors might still return a non-empty string)
                    if (strpos($token, 'ERROR:') !== false || strpos($token, 'failed') !== false || empty(trim($token))) {
                        throw new Exception("az CLI returned an error or no token.");
                    }
    
                    $this->token = trim($token);
                    log_safe("Successfully retrieved Azure token.");
                    return $this->token;
                } catch (Exception $e) {
                    log_vuln("Failed to get Azure token: " . $e->getMessage());
                    echo C_RED . "    Make sure you're logged in with: az login" . C_RESET . "\n";
                    return null;
                }
            }
            return $this->token;
        }
    
        /**
         * Make authenticated request to Azure RM API using cURL.
         */
        private function make_request(string $url): ?array
        {
            if (!$this->token) {
                $this->get_azure_token();
                if (!$this->token) {
                    return null;
                }
            }
    
            $ch = curl_init($url);
            
            $headers = [
                "Authorization: Bearer " . $this->token,
                "Content-Type: application/json"
            ];
            
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            
            if ($this->verbose) {
                echo "    GET $url\n";
            }
            
            $response = curl_exec($ch);
            $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
    
            if ($response === false) {
                 if ($this->verbose) {
                    echo "    -> Error: " . curl_error($ch) . "\n";
                }
                return null;
            }
    
            if ($status_code === 200) {
                return json_decode($response, true);
            } elseif ($status_code === 404) {
                return ["_not_found" => true];
            } else {
                 if ($this->verbose) {
                    echo "    -> $status_code: " . substr($response, 0, 200) . "\n";
                }
                return null;
            }
        }
    
        /**
         * Check APIM service properties.
         * @return array{0: ?bool, 1: string, 2: array} Tuple of (is_vulnerable_config, message, properties_dict)
         */
        public function check_apim_properties(): array
        {
            log_check("Checking APIM service properties via Azure RM...");
    
            $url = $this->base_url . "?api-version=" . APIM_API_VERSION;
            $data = $this->make_request($url);
    
            if ($data === null) {
                return [null, "Failed to query APIM properties", []];
            }
    
            if (isset($data['_not_found'])) {
                return [null, "APIM service not found", []];
            }
    
            $properties = $data['properties'] ?? [];
            $sku = $data['sku'] ?? [];
    
            $result = [
                "developerPortalStatus" => $properties['developerPortalStatus'] ?? null,
                "sku_name" => $sku['name'] ?? null,
                "sku_tier" => $sku['tier'] ?? null,
            ];
    
            // Check vulnerable conditions
            $portal_enabled = ($properties['developerPortalStatus'] ?? null) === "Enabled";
            $is_consumption = ($sku['name'] ?? null) === "Consumption";
    
            if ($portal_enabled) {
                log_vuln("properties.developerPortalStatus = 'Enabled'");
            } else {
                log_safe("properties.developerPortalStatus = '" . ($properties['developerPortalStatus'] ?? 'N/A') . "'");
            }
    
            if ($is_consumption) {
                log_safe("sku.name = 'Consumption' (limited portal features)");
            } else {
                log_info("sku.name = '" . ($sku['name'] ?? 'N/A') . "' (full portal features)");
            }
    
            return [$portal_enabled && !$is_consumption, "APIM properties checked", $result];
        }
    
        /**
         * Check if Basic Authentication identity provider exists.
         * @return array{0: ?bool, 1: string} Tuple of (exists, message)
         */
        public function check_basic_identity_provider(): array
        {
            log_check("Checking for Basic Auth identity provider...");
    
            $url = $this->base_url . "/identityProviders/basic?api-version=" . APIM_API_VERSION;
            $data = $this->make_request($url);
    
            if ($data === null) {
                return [null, "Failed to query identity providers"];
            }
    
            if (isset($data['_not_found'])) {
                log_safe("identityProviders/basic does NOT exist");
                return [false, "Basic Auth identity provider not configured"];
            }
    
            log_vuln("identityProviders/basic EXISTS - Basic Auth is configured!");
            return [true, "Basic Auth identity provider is configured"];
        }
    
        /**
         * Check portal signup settings.
         * @return array{0: ?bool, 1: string, 2: ?bool} Tuple of (signup_disabled_in_ui, message, enabled_value)
         */
        public function check_signup_settings(): array
        {
            log_check("Checking portal signup settings...");
    
            $url = $this->base_url . "/portalsettings/signup?api-version=" . APIM_API_VERSION;
            $data = $this->make_request($url);
    
            if ($data === null) {
                return [null, "Failed to query signup settings", null];
            }
    
            if (isset($data['_not_found'])) {
                return [null, "Signup settings not found", null];
            }
    
            $properties = $data['properties'] ?? [];
            $enabled = $properties['enabled'] ?? null;
    
            if ($enabled === true) {
                log_info("portalsettings/signup.properties.enabled = true (signup visible in UI)");
                return [false, "Signup is enabled in UI", true];
            } elseif ($enabled === false) {
                log_vuln("portalsettings/signup.properties.enabled = false (signup hidden but API may still work!)");
                return [true, "Signup is HIDDEN in UI (bypass possible)", false];
            } else {
                return [null, "Signup enabled value: " . ($enabled === null ? 'null' : (string)$enabled), $enabled];
            }
        }
    
        /**
         * Perform comprehensive Azure RM property checks.
         */
        public function check_vulnerability(): array
        {
            $results = [
                "subscription_id" => $this->subscription_id,
                "resource_group" => $this->resource_group,
                "service_name" => $this->service_name,
                "vulnerable" => false,
                "risk_level" => "Unknown",
                "checks" => [],
                "properties" => []
            ];
    
            // Check 1: APIM service properties
            [$props_vuln, $props_msg, $props_data] = $this->check_apim_properties();
            $results["checks"]["apim_properties"] = [
                "status" => $props_vuln,
                "message" => $props_msg
            ];
            $results["properties"] = array_merge($results["properties"], $props_data);
    
            if ($props_vuln === null) {
                $results["risk_level"] = "Error";
                return $results;
            }
    
            // Check 2: Basic Auth identity provider
            [$basic_exists, $basic_msg] = $this->check_basic_identity_provider();
            $results["checks"]["basic_auth_provider"] = [
                "status" => $basic_exists,
                "message" => $basic_msg
            ];
            $results["properties"]["basic_auth_exists"] = $basic_exists;
    
            if ($basic_exists === null) {
                $results["risk_level"] = "Error";
                return $results;
            }
    
            if (!$basic_exists) {
                $results["risk_level"] = "Low";
                $results["checks"]["assessment"] = [
                    "status" => false,
                    "message" => "No Basic Auth configured - not vulnerable"
                ];
                return $results;
            }
    
            // Check 3: Signup settings (only relevant if Basic Auth exists)
            [$signup_hidden, $signup_msg, $signup_enabled] = $this->check_signup_settings();
            $results["checks"]["signup_settings"] = [
                "status" => $signup_hidden,
                "message" => $signup_msg
            ];
            $results["properties"]["signup_enabled"] = $signup_enabled;
    
            // Determine vulnerability
            if ($props_vuln && $basic_exists) {
                if ($signup_hidden) {
                    // Critical: Portal enabled + Basic Auth exists + Signup hidden = VULNERABLE
                    $results["vulnerable"] = true;
                    $results["risk_level"] = "Critical";
                    $results["checks"]["assessment"] = [
                        "status" => true,
                        "message" => "VULNERABLE: Signup hidden in UI but Basic Auth API still accessible"
                    ];
                } else {
                    // Basic Auth enabled and signup visible - attack source
                    $results["risk_level"] = "Attack Source";
                    $results["checks"]["assessment"] = [
                        "status" => true,
                        "message" => "Basic Auth signup enabled - can be used for cross-tenant attacks"
                    ];
                }
            } else {
                $results["risk_level"] = "Low";
                $results["checks"]["assessment"] = [
                    "status" => false,
                    "message" => "Configuration appears safe"
                ];
            }
    
            return $results;
        }
    }
    
    // ----------------------------------------------------------------------
    // Class APIMVulnerabilityChecker (HTTP Probe Checks)
    // ----------------------------------------------------------------------
    
    class APIMVulnerabilityChecker
    {
        private string $url;
        private bool $verbose;
        private bool $verify_ssl;
    
        public function __construct(string $url, bool $verbose = false, bool $verify_ssl = true)
        {
            $this->url = rtrim($url, '/');
            $this->verbose = $verbose;
            $this->verify_ssl = $verify_ssl;
    
            if (!$verify_ssl) {
                 // In PHP, we just set cURL options, no need for urllib3 equivalent
            }
        }
    
        /**
         * Make an HTTP request using cURL.
         */
        private function make_http_request(string $url, string $method = 'GET', ?array $data = null, array $headers = []): ?array
        {
            $ch = curl_init($url);
            
            $curl_options = get_curl_options($this->verify_ssl);
    
            // Standard headers
            $standard_headers = [
                'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            ];
    
            $all_headers = array_merge($standard_headers, $headers);
            
            curl_setopt($ch, CURLOPT_HTTPHEADER, $all_headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 10);
            
            if ($method === 'POST') {
                curl_setopt($ch, CURLOPT_POST, true);
                if ($data !== null) {
                    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
                }
            }
            
            foreach ($curl_options as $option => $value) {
                curl_setopt($ch, $option, $value);
            }
    
            $response = curl_exec($ch);
            $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
    
            if ($response === false) {
                 if ($this->verbose) {
                    echo "    -> Error: " . curl_error($ch) . "\n";
                }
                // Check for SSL error explicitly
                 if (strpos(curl_error($ch), 'SSL') !== false) {
                     return ['ssl_error' => true];
                 }
                return null;
            }
    
            return [
                'status_code' => $status_code,
                'body' => $response
            ];
        }
        
        /**
         * Check if /signup endpoint is accessible (for UI check).
         * @return array{0: ?bool, 1: string} Tuple of (accessible, message)
         */
        public function check_signup_endpoint(): array
        {
            log_check("Checking signup endpoint accessibility...");
            
            $result = $this->make_http_request("{$this->url}/signup", 'GET');
    
            if ($result === null) {
                return [false, "Error accessing signup endpoint: connection failed"];
            }
            
            if (isset($result['ssl_error'])) {
                return [null, "SSL_ERROR: SSL certificate verification failed"];
            }
    
            $status_code = $result['status_code'];
            
            if (in_array($status_code, [200, 302])) {
                return [true, "Signup endpoint is accessible"];
            } else {
                return [false, "Signup endpoint returned $status_code"];
            }
        }
    
        /**
         * Check if Basic Authentication signup API is accessible (bypassing UI).
         * @return array{0: ?bool, 1: string} Tuple of (accessible, message)
         */
        public function check_basic_auth(): array
        {
            log_check("Checking if Basic Auth signup API is accessible...");
            
            $signup_payload = [
                "challenge" => [
                    "testCaptchaRequest" => [
                        "challengeId" => "00000000-0000-0000-0000-000000000000",
                        "inputSolution" => "AAAAAA"
                    ],
                    "azureRegion" => "NorthCentralUS",
                    "challengeType" => "visual"
                ],
                "signupData" => [
                    "email" => "vuln-probe-test@nonexistent-invalid-domain.test",
                    "firstName" => "Probe",
                    "lastName" => "Test",
                    "password" => "VulnProbe123!",
                    "confirmation" => "signup",
                    "appType" => "developerPortal"
                ]
            ];
    
            $headers = [
                'Content-Type: application/json',
                'Accept: */*',
                "Origin: {$this->url}",
                "Referer: {$this->url}/signup"
            ];
            
            $api_url = "{$this->url}/signup";
            if ($this->verbose) {
                echo "    Trying: POST $api_url\n";
            }
            
            $result = $this->make_http_request($api_url, 'POST', $signup_payload, $headers);
            
            if ($result === null) {
                 return [null, "Connection timeout or error"];
            }
            
            if (isset($result['ssl_error'])) {
                return [null, "SSL_ERROR: SSL certificate verification failed"];
            }
    
            $status_code = $result['status_code'];
            $response_text = strtolower($result['body']);
            
            if ($this->verbose) {
                echo "    -> $status_code\n";
                echo "    -> " . substr($response_text, 0, 200) . "...\n";
            }
    
            // 404 = endpoint doesn't exist
            if ($status_code == 404) {
                 if (strpos($response_text, 'html') !== false || strpos($response_text, '<!doctype') !== false) {
                     return [false, "Signup API not found (HTML 404)"];
                 }
                return [false, "Signup API not found (404)"];
            }
            
            // These responses indicate the signup API EXISTS and processes requests
            if ($status_code == 400) {
                if (strpos($response_text, 'captcha') !== false || strpos($response_text, 'challenge') !== false) {
                    return [true, "Basic Auth signup API ACTIVE (captcha validation)"];
                }
                if (strpos($response_text, 'email') !== false || strpos($response_text, 'password') !== false || strpos($response_text, 'invalid') !== false) {
                    return [true, "Basic Auth signup API ACTIVE (input validation)"];
                }
                return [true, "Basic Auth signup API responds (400)"];
            }
            
            if ($status_code == 409) {
                return [true, "Basic Auth signup API ACTIVE (409 conflict)"];
            }
            
            if (in_array($status_code, [200, 201])) {
                return [true, "Basic Auth signup API ACCEPTS requests"];
            }
            
            if (in_array($status_code, [401, 403])) {
                return [true, "Basic Auth signup API responds ($status_code)"];
            }
            
            if ($status_code == 422) {
                return [true, "Basic Auth signup API validates (422)"];
            }
            
            log_info("  /signup returned $status_code");
            return [null, "Signup returned $status_code - manual check recommended"];
        }
    
        /**
         * Check if signup is disabled in UI (but API might still work = vulnerable).
         * @return array{0: ?bool, 1: string} Tuple of (signup_hidden, message)
         */
        public function check_signup_disabled(): array
        {
            log_check("Checking if signup is hidden/disabled in UI...");
            
            $result = $this->make_http_request("{$this->url}/signup", 'GET');
    
            if ($result === null) {
                return [null, "Error checking signup page: connection failed"];
            }
    
            $status_code = $result['status_code'];
            
            // If we get redirected away or 404, signup is hidden in UI
            if ($status_code == 404) {
                return [true, "Signup page returns 404 (hidden in UI)"];
            }
            
            if (in_array($status_code, [301, 302, 303, 307, 308])) {
                return [true, "Signup page redirects away (disabled in UI)"];
            }
            
            if ($status_code == 200) {
                return [false, "Signup page accessible (UI shows signup)"];
            }
            
            return [null, "Signup page returned $status_code"];
        }
    
        /**
         * Perform comprehensive vulnerability check.
         */
        public function check_vulnerability(): array
        {
            $results = [
                'url' => $this->url,
                'vulnerable' => false,
                'attack_source' => false,
                'risk_level' => 'Unknown',
                'checks' => []
            ];
    
            // Check 1: Signup page accessible in UI
            [$signup_ui_accessible, $signup_ui_msg] = $this->check_signup_endpoint();
            $results['checks']['signup_ui'] = [
                'status' => $signup_ui_accessible,
                'message' => $signup_ui_msg
            ];
    
            // Check for SSL error
            if (strpos($signup_ui_msg, "SSL_ERROR:") !== false) {
                log_vuln("SSL certificate verification failed");
                log_info("Try running with -k flag to skip SSL verification");
                $results['risk_level'] = 'SSL Error';
                $results['ssl_error'] = true;
                return $results;
            }
    
            if ($signup_ui_accessible) {
                log_info($signup_ui_msg);
            } else {
                log_info($signup_ui_msg);
            }
    
            // Check 2: Basic Auth API accessible (the real test)
            [$basic_auth_api, $basic_api_msg] = $this->check_basic_auth();
            $results['checks']['basic_auth_api'] = [
                'status' => $basic_auth_api,
                'message' => $basic_api_msg
            ];
    
            if ($basic_auth_api === true) {
                log_vuln($basic_api_msg);
            } elseif ($basic_auth_api === false) {
                log_safe($basic_api_msg);
                $results['risk_level'] = 'Low';
                return $results;
            } else {
                // Check for SSL error from basic_auth check
                if (strpos($basic_api_msg, "SSL_ERROR:") !== false) {
                    log_vuln("SSL certificate verification failed during API check");
                    $results['risk_level'] = 'SSL Error';
                    $results['ssl_error'] = true;
                    return $results;
                }
                log_info($basic_api_msg);
            }
    
            // Check 3: Is signup disabled/hidden in UI?
            [$signup_hidden, $signup_hidden_msg] = $this->check_signup_disabled();
            $results['checks']['signup_ui_hidden'] = [
                'status' => $signup_hidden,
                'message' => $signup_hidden_msg
            ];
    
            if ($signup_hidden) {
                log_info($signup_hidden_msg);
            } else {
                log_info($signup_hidden_msg);
            }
    
            // Determine vulnerability
            if ($basic_auth_api) {
                if ($signup_hidden) {
                    // UI disabled but API works = VULNERABLE TARGET
                    $results['vulnerable'] = true;
                    $results['risk_level'] = 'Critical';
                } else {
                    // UI enabled and API works = ATTACK SOURCE
                    $results['attack_source'] = true;
                    $results['risk_level'] = 'Attack Source';
                }
            } else {
                $results['risk_level'] = 'Low';
            }
    
            return $results;
        }
    }
    
    // ----------------------------------------------------------------------
    // Output Functions (print_azure_rm_results and print_results) - (Not included for brevity, but they would be
    // simple PHP functions using the color constants defined above)
    // ----------------------------------------------------------------------
    // ... (Define print_azure_rm_results and print_results here using the C_ constants) ...
    // The full print functions are omitted here to keep the PHP response focused, 
    // but they would directly translate the Python print statements.
    
    function print_azure_rm_results(array $results): void
    {
        echo C_CYAN . str_repeat('=', 70) . "\n";
        echo "AZURE RESOURCE MANAGER PROPERTY CHECK RESULTS\n";
        echo str_repeat('=', 70) . C_RESET . "\n\n";
    
        echo "Subscription: " . ($results['subscription_id'] ?? 'N/A') . "\n";
        echo "Resource Group: " . ($results['resource_group'] ?? 'N/A') . "\n";
        echo "Service Name: " . ($results['service_name'] ?? 'N/A') . "\n\n";
    
        // Risk level
        $risk = $results['risk_level'] ?? 'Unknown';
        $risk_color = C_GREEN;
        if ($risk === 'Critical' || $risk === 'Error') {
            $risk_color = C_RED;
        } elseif ($risk === 'Attack Source') {
            $risk_color = C_YELLOW;
        }
        echo "Risk Level: {$risk_color}{$risk}" . C_RESET . ($risk === 'Critical' ? ' - VULNERABLE TO SIGNUP BYPASS' : ($risk === 'Attack Source' ? ' - CAN BE USED FOR CROSS-TENANT BYPASS' : ($risk === 'Error' ? ' - COULD NOT COMPLETE CHECKS' : ' - NOT VULNERABLE'))) . "\n";
    
        // Properties
        echo "\n" . C_CYAN . "Resource Properties:" . C_RESET . "\n\n";
        $props = $results['properties'] ?? [];
        foreach ($props as $key => $value) {
            $display_value = is_bool($value) ? ($value ? 'true' : 'false') : ($value ?? 'N/A');
            $prefix = C_GREEN . "[+]" . C_RESET;
    
            if ($key === 'basic_auth_exists' && $value) {
                $prefix = C_RED . "[!]" . C_RESET;
            } elseif ($key === 'signup_enabled' && $value === false) {
                $prefix = C_RED . "[!]" . C_RESET;
            } elseif ($key === 'developerPortalStatus' && $value === 'Enabled') {
                $prefix = C_YELLOW . "[i]" . C_RESET;
            }
    
            echo "  $prefix $key: $display_value\n";
        }
    
        // Assessment
        echo "\n" . C_CYAN . "Vulnerability Assessment:" . C_RESET . "\n\n";
        foreach (($results['checks'] ?? []) as $check_name => $check_data) {
            $status = $check_data['status'] ?? null;
            $message = $check_data['message'] ?? 'N/A';
    
            $prefix = C_YELLOW . "[?]" . C_RESET;
            if ($status === true) {
                $prefix = C_RED . "[!]" . C_RESET;
            } elseif ($status === false) {
                $prefix = C_GREEN . "[+]" . C_RESET;
            }
    
            echo "  $prefix $check_name: $message\n";
        }
        echo "\n" . C_CYAN . str_repeat('=', 70) . C_RESET . "\n\n";
    }
    
    function print_results(array $results): void
    {
        echo "\n" . C_CYAN . str_repeat('=', 70) . "\n";
        echo "VULNERABILITY ASSESSMENT RESULTS\n";
        echo str_repeat('=', 70) . C_RESET . "\n\n";
    
        echo "Target: " . ($results['url'] ?? 'N/A') . "\n\n";
    
        // Risk level
        $risk = $results['risk_level'] ?? 'Unknown';
        $risk_color = C_GREEN;
        $risk_message = '';
        
        switch ($risk) {
            case 'Critical':
                $risk_color = C_RED;
                $risk_message = ' - VULNERABLE TO SIGNUP BYPASS';
                break;
            case 'Attack Source':
                $risk_color = C_YELLOW;
                $risk_message = ' - CAN BE USED FOR CROSS-TENANT BYPASS';
                break;
            case 'SSL Error':
                $risk_color = C_RED;
                $risk_message = ' - SSL ERROR PREVENTED SCAN';
                break;
            case 'High':
                $risk_color = C_RED;
                $risk_message = ' - LIKELY VULNERABLE';
                break;
            case 'Medium':
                $risk_color = C_YELLOW;
                $risk_message = ' - BASIC AUTH ENABLED';
                break;
            default:
                $risk_color = C_GREEN;
                $risk_message = ' - LIKELY NOT VULNERABLE';
                break;
        }
    
        echo "Risk Level: {$risk_color}{$risk}{$risk_message}" . C_RESET . "\n";
    
        echo "\n" . C_CYAN . "Detailed Checks:" . C_RESET . "\n\n";
    
        foreach (($results['checks'] ?? []) as $check_name => $check_data) {
            $status = $check_data['status'] ?? null;
            $message = $check_data['message'] ?? 'N/A';
    
            if (strpos($message, "SSL_ERROR:") !== false) {
                $message = "SSL certificate verification failed";
            }
    
            $prefix = C_YELLOW . "[?]" . C_RESET;
            if ($status === true) {
                $prefix = C_RED . "[!]" . C_RESET;
            } elseif ($status === false) {
                $prefix = C_GREEN . "[+]" . C_RESET;
            }
    
            echo "  $prefix $check_name: $message\n";
        }
    
        // Recommendations (Omitted for brevity, but would use the same logic and C_ constants)
        // ...
        
        echo "\n" . C_CYAN . "Recommendations:" . C_RESET . "\n\n";
        // Simplified Recommendations output:
        if ($results['vulnerable'] ?? false) {
             echo C_RED . "CRITICAL: SIGNUP BYPASS VULNERABILITY CONFIRMED" . C_RESET . "\n";
             echo "Immediate actions: 1. DISABLE Basic Authentication. 2. Audit user accounts.\n";
        } elseif ($results['attack_source'] ?? false) {
             echo C_YELLOW . "ATTACK SOURCE IDENTIFIED" . C_RESET . "\n";
             echo "This instance can be used to attack others. Consider disabling Basic Auth if not needed.\n";
        } elseif ($results['ssl_error'] ?? false) {
             echo C_RED . "SSL CERTIFICATE ERROR" . C_RESET . "\n";
             echo "Could not connect. Try running with -k flag: php apim_vuln_checker.php -k {$results['url']}\n";
        } else {
             echo C_GREEN . "Your instance appears to have reduced risk." . C_RESET . "\n";
        }
        
        echo "\n" . C_CYAN . str_repeat('=', 70) . C_RESET . "\n\n";
    }
    
    
    // ----------------------------------------------------------------------
    // Main Execution Block (Equivalent to Python's main() and argument parsing)
    // ----------------------------------------------------------------------
    
    function main(array $argv): void
    {
        // PHP doesn't have a built-in sophisticated argument parser like Python's argparse,
        // so we'll use a simpler, manual parsing approach or require a library like symfony/console.
        // For simplicity, we'll parse $argv manually.
    
        $args = [
            'url' => null,
            'json' => false,
            'verbose' => false,
            'insecure' => false,
            'azure' => false,
            'subscription' => null,
            'resource-group' => null,
            'name' => null,
        ];
    
        $url_found = false;
        for ($i = 1; $i < count($argv); $i++) {
            $arg = $argv[$i];
            $next_arg = $argv[$i + 1] ?? null;
    
            switch ($arg) {
                case '--json':
                    $args['json'] = true;
                    break;
                case '-v':
                case '--verbose':
                    $args['verbose'] = true;
                    break;
                case '-k':
                case '--insecure':
                    $args['insecure'] = true;
                    break;
                case '--azure':
                    $args['azure'] = true;
                    break;
                case '-s':
                case '--subscription':
                    $args['subscription'] = $next_arg;
                    $i++;
                    break;
                case '-g':
                case '--resource-group':
                    $args['resource-group'] = $next_arg;
                    $i++;
                    break;
                case '-n':
                case '--name':
                    $args['name'] = $next_arg;
                    $i++;
                    break;
                default:
                    if (filter_var($arg, FILTER_VALIDATE_URL) && !$url_found) {
                        $args['url'] = $arg;
                        $url_found = true;
                    } else {
                        // This is a simplistic error handling
                        fwrite(STDERR, "Error: Unknown argument or misplaced URL: $arg\n");
                        exit(1);
                    }
                    break;
            }
        }
    
        // Validate arguments
        if (empty($args['url']) && !$args['azure']) {
            fwrite(STDERR, "Error: Either URL or --azure with -s/-g/-n is required\n");
            fwrite(STDERR, "Usage: php apim_vuln_checker.php [URL] [--azure -s <sub-id> -g <rg-name> -n <apim-name>]\n");
            exit(1);
        }
    
        if ($args['azure'] && (empty($args['subscription']) || empty($args['resource-group']) || empty($args['name']))) {
            fwrite(STDERR, "Error: --azure requires -s/--subscription, -g/--resource-group, and -n/--name\n");
            exit(1);
        }
    
        // Banner (omitted for brevity, but should be included using C_CYAN)
        echo C_CYAN . str_repeat('=', 70) . "\n";
        echo "Azure APIM Vulnerability Checker\n";
        echo "Cross-Tenant Signup Bypass Detection\n";
        echo str_repeat('=', 70) . C_RESET . "\n\n";
    
        $all_results = [];
        $is_vulnerable = false;
    
        // Run HTTP probe checks if URL provided
        if ($args['url']) {
            $checker = new APIMVulnerabilityChecker($args['url'], $args['verbose'], !$args['insecure']);
            $http_results = $checker->check_vulnerability();
            $all_results['http_probe'] = $http_results;
            $is_vulnerable = $is_vulnerable || ($http_results['vulnerable'] ?? false);
    
            if (!$args['json']) {
                print_results($http_results);
            }
        }
    
        // Run Azure RM property checks if enabled
        if ($args['azure']) {
            echo "\n" . C_CYAN . str_repeat('=', 70) . "\n";
            echo "AZURE RESOURCE MANAGER PROPERTY CHECKS\n";
            echo str_repeat('=', 70) . C_RESET . "\n\n";
    
            $azure_checker = new AzureRMChecker(
                $args['subscription'],
                $args['resource-group'],
                $args['name'],
                $args['verbose']
            );
            $azure_results = $azure_checker->check_vulnerability();
            $all_results['azure_rm'] = $azure_results;
            $is_vulnerable = $is_vulnerable || ($azure_results['vulnerable'] ?? false);
    
            if (!$args['json']) {
                print_azure_rm_results($azure_results);
            }
        }
    
        // Output JSON if requested
        if ($args['json']) {
            echo json_encode($all_results, JSON_PRETTY_PRINT) . "\n";
        }
    
        // Exit code based on vulnerability
        exit($is_vulnerable ? 1 : 0);
    }
    
    // Ensure the code is run from the command line and cURL is available
    if (php_sapi_name() !== 'cli' || !extension_loaded('curl')) {
        fwrite(STDERR, "This script must be run from the command line (CLI) and requires the PHP cURL extension.\n");
        exit(1);
    }
    
    // Execute main function
    main($argv);
    
    // End of file
    
    
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
    ===================================================================================================