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)|
===================================================================================================