From b0cd651c8d4a376e6d007cc5548e405f21c14d18 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 5 May 2026 14:25:14 +0200 Subject: [PATCH] Add WireServer & GoalState --- .../azure-security/az-services/vms/README.md | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) diff --git a/src/pentesting-cloud/azure-security/az-services/vms/README.md b/src/pentesting-cloud/azure-security/az-services/vms/README.md index 248ce6bd8..c667f9ce7 100644 --- a/src/pentesting-cloud/azure-security/az-services/vms/README.md +++ b/src/pentesting-cloud/azure-security/az-services/vms/README.md @@ -837,6 +837,317 @@ Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt {{#endtab }} {{#endtabs }} +## Azure WireServer & GoalState + +Azure VMs expose **internal platform endpoints** that are used for configuration, metadata retrieval and identity management. Understanding the difference between them is critical for **enumeration, privilege escalation and post-exploitation**. + +--- + +### Wire Server (Azure Fabric Endpoint) + +The **Azure WireServer** is an internal Azure IP (`168.63.129.16`) used by the platform to communicate with the VM. + +It is responsible for: + +- Communication with the **VM Agent** +- Delivering: + - **GoalState** + - **ExtensionsConfig** + - Internal VM configuration (including identities) +- DHCP & DNS services +- Health monitoring + +--- + +### GoalState & ExtensionsConfig + +The **GoalState** represents the **desired configuration of the VM** as defined by Azure. It may include: + +- Extensions configuration +- Managed identities +- Provisioning state +- Agent instructions + +The **ExtensionsConfig** contains detailed configuration of VM extensions and may include: + +- **User Assigned Managed Identities** +- Extension settings +- Secrets (depending on extension) + +These endpoints are typically accessed via: + +```bash +curl -H "x-ms-version: 2012-11-30" http://168.63.129.16/?comp=goalstate +``` + +### Access Restrictions + +Although the endpoint is reachable from the VM network, **it is not equally accessible from all contexts**. + +**Accessible from**: + +- Azure **VM Agent** +- Azure **Run Command** +- **VM Extensions** + +**Not reliably accessible from**: + +- Interactive SSH sessions (e.g., `azureuser`) +- Unprivileged processes inside the VM + +This is because: + +- The WireServer is designed for **platform-agent communication** +- Requests may require **specific headers, timing, or context** +- Some responses are only available to the **VM Agent execution environment** + +--- + +### Run Command vs SSH Context + +Azure provides multiple ways to execute commands inside a VM, but **they do not run in the same context**. + +--- + +#### Run Command + +Run Command is an Azure feature that executes scripts via the **VM Agent**. + +- Uses: `Microsoft.Compute/virtualMachines/runCommand/action` +- Runs with **agent-level privileges** +- Has access to: + - WireServer + - GoalState + - ExtensionsConfig + +Example: + +```bash +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunShellScript \ + --scripts @script.sh +``` + +#### SSH Session + +When connecting via SSH: + +- Runs as a **regular OS user** +- Uses standard network stack +- Does **NOT have agent-level access** + +As a result: + +- Requests to `168.63.129.16` may fail or return incomplete data +- GoalState may not be accessible + +**Script Examples:** + +{{#tabs }} +{{#tab name="Linux" }} + +```bash +#!/usr/bin/env bash +set -euo pipefail + +ws="http://168.63.129.16" + +echo "[*] Getting Goal State..." + +goal_urls=( + "$ws/?comp=goalstate" + "$ws/machine?comp=goalstate" + "$ws/machine/?comp=goalstate" +) + +goal_xml="" +for url in "${goal_urls[@]}"; do + if goal_xml="$(curl -fsS -H "x-ms-version: 2012-11-30" "$url" 2>/dev/null)"; then + echo "[+] GoalState OK via $url" + break + fi +done + +if [[ -z "$goal_xml" ]]; then + echo "[-] No GoalState endpoint responded" + exit 1 +fi + +ext_url="$( + GOAL_XML="$goal_xml" python3 - <<'PY' +import os +import xml.etree.ElementTree as ET + +xml = os.environ["GOAL_XML"].strip() +root = ET.fromstring(xml) + +def lname(tag): + return tag.rsplit("}", 1)[-1] + +for el in root.iter(): + if lname(el.tag) == "ExtensionsConfig" and (el.text or "").strip(): + print(el.text.strip()) + break +PY +)" + +if [[ -z "$ext_url" ]]; then + echo "[-] No ExtensionsConfig URL found in GoalState" + echo "[*] Identity-like nodes seen in GoalState:" + GOAL_XML="$goal_xml" python3 - <<'PY' +import os +import xml.etree.ElementTree as ET + +xml = os.environ["GOAL_XML"].strip() +root = ET.fromstring(xml) + +def lname(tag): + return tag.rsplit("}", 1)[-1] + +found = False +for el in root.iter(): + name = lname(el.tag) + if "Identity" in name: + found = True + text = (el.text or "").strip() + print(f"<{name}>{text}") + +if not found: + print(" (none)") +PY + exit 0 +fi + +echo "[*] Getting ExtensionsConfig..." +ext_xml="$(curl -fsS -H "x-ms-version: 2012-11-30" "$ext_url")" + +EXT_XML="$ext_xml" python3 - <<'PY' +import os +import xml.etree.ElementTree as ET + +xml = os.environ["EXT_XML"].strip() +root = ET.fromstring(xml) + +def lname(tag): + return tag.rsplit("}", 1)[-1] + +ids = [el for el in root.iter() if lname(el.tag) == "UserAssignedIdentity"] + +if not ids: + print("[-] No UserAssignedIdentity nodes found") + print("[*] Identity-like nodes present in ExtensionsConfig:") + shown = False + for el in root.iter(): + name = lname(el.tag) + if "Identity" in name: + shown = True + text = (el.text or "").strip() + attrs = " ".join(f'{k}="{v}"' for k, v in el.attrib.items()) + if attrs: + print(f" <{name} {attrs}>{text}") + else: + print(f" <{name}>{text}") + if not shown: + print(" (none)") + raise SystemExit(0) + +for idnode in ids: + client_id = "" + object_id = "" + resource_id = "" + + for child in idnode.iter(): + name = lname(child.tag) + text = (child.text or "").strip() + if name == "IdentityClientId": + client_id = text + elif name == "IdentityObjectId": + object_id = text + elif name == "IdentityResourceId": + resource_id = text + + print() + print("[+] Managed Identity:") + print(f" ClientId : {client_id}") + print(f" ObjectId : {object_id}") + print(f" ResourceId : {resource_id}") +PY +``` + +{{#endtab }} + +{{#tab name="Windows" }} + +```bash +$ws = "http://168.63.129.16" +$h = @{ + "x-ms-version" = "2012-11-30" +} + +Write-Host "[*] Getting Goal State..." -ForegroundColor Cyan + +$goalUrls = @( + "$ws/?comp=goalstate", + "$ws/machine?comp=goalstate", + "$ws/machine/?comp=goalstate" +) + +$gs = $null + +foreach ($url in $goalUrls) { + try { + $gs = Invoke-WebRequest -Uri $url -Headers $h -UseBasicParsing -ErrorAction Stop + Write-Host "[+] GoalState OK via $url" -ForegroundColor Green + break + } catch {} +} + +if (-not $gs) { + Write-Host "[-] No GoalState endpoint responded" -ForegroundColor Red + return +} + +[xml]$xml = $gs.Content +$cfg = $xml.GoalState.Container.RoleInstanceList.RoleInstance.Configuration + +$extUrl = $cfg.ExtensionsConfig + +Write-Host "[*] Getting ExtensionsConfig..." -ForegroundColor Cyan + +try { + $ext = Invoke-WebRequest -Uri $extUrl -Headers $h -UseBasicParsing -ErrorAction Stop + [xml]$extXml = $ext.Content +} catch { + Write-Host "[-] Error getting ExtensionsConfig" -ForegroundColor Red + return +} + +# Extract Managed Identity info +$ids = $extXml.SelectNodes("//UserAssignedIdentity") + +if (!$ids) { + Write-Host "[-] No User Assigned Identities found" -ForegroundColor Red + return +} + +foreach ($id in $ids) { + $clientId = $id.IdentityClientId + $objectId = $id.IdentityObjectId + $resourceId = $id.IdentityResourceId + + Write-Host "`n[+] Managed Identity:" -ForegroundColor Green + Write-Host " ClientId : $clientId" + Write-Host " ObjectId : $objectId" + Write-Host " ResourceId : $resourceId" +} +``` + +{{#endtab }} +{{#endtabs }} + + ## Privilege Escalation {{#ref}} @@ -866,6 +1177,9 @@ Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt - [https://learn.microsoft.com/en-us/azure/virtual-machines/overview](https://learn.microsoft.com/en-us/azure/virtual-machines/overview) - [https://hausec.com/2022/05/04/azure-virtual-machine-execution-techniques/](https://hausec.com/2022/05/04/azure-virtual-machine-execution-techniques/) - [https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service](https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service) +- [https://learn.microsoft.com/en-us/azure/virtual-network/what-is-ip-address-168-63-129-16](https://learn.microsoft.com/en-us/azure/virtual-network/what-is-ip-address-168-63-129-16) +- [https://learn.microsoft.com/en-us/azure/virtual-machines/run-command](https://learn.microsoft.com/en-us/azure/virtual-machines/run-command) +- [https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux](https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux) {{#include ../../../../banners/hacktricks-training.md}}