Lucene search
K

📄 Azure APIM 2 Vulnerability Checker

🗓️ 04 Dec 2025 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 146 Views

Azure API Management Vulnerability Checker assesses the developer portal security with PHP.

Code
=============================================================================================================================================
    | # 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" => "[email protected]",
                    "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)|
    ===================================================================================================

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