It works by analyzing various aspects of an APIM instance, including its policies, network configurations, access controls, and subscription settings, against a defined set of security rules and known exploit patterns. This includes checking for overly permissive policies, exposed backend details, weak authentication schemes, or outdated configurations.
By utilizing this checker, administrators and security teams can gain valuable insights into their APIM's security posture, remediate identified weaknesses, and reduce the attack surface. It's an essential component for maintaining a robust and secure API gateway environment, preventing unauthorized access and data breaches.
=============================================================================================================================================
| # 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" => "
"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)|
===================================================================================================