@@ -837,6 +837,317 @@ Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt
837837{{# endtab }}
838838{{#endtabs }}
839839
840+ ## Azure WireServer & GoalState
841+
842+ 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**.
843+
844+ ---
845+
846+ ### Wire Server (Azure Fabric Endpoint)
847+
848+ The **Azure WireServer** is an internal Azure IP ( ` 168.63.129.16` ) used by the platform to communicate with the VM.
849+
850+ It is responsible for:
851+
852+ - Communication with the ** VM Agent**
853+ - Delivering:
854+ - ** GoalState**
855+ - ** ExtensionsConfig**
856+ - Internal VM configuration (including identities)
857+ - DHCP & DNS services
858+ - Health monitoring
859+
860+ ---
861+
862+ # ## GoalState & ExtensionsConfig
863+
864+ The ** GoalState** represents the ** desired configuration of the VM** as defined by Azure. It may include:
865+
866+ - Extensions configuration
867+ - Managed identities
868+ - Provisioning state
869+ - Agent instructions
870+
871+ The ** ExtensionsConfig** contains detailed configuration of VM extensions and may include:
872+
873+ - ** User Assigned Managed Identities**
874+ - Extension settings
875+ - Secrets (depending on extension)
876+
877+ These endpoints are typically accessed via:
878+
879+ ` ` ` bash
880+ curl -H " x-ms-version: 2012-11-30" http://168.63.129.16/? comp=goalstate
881+ ` ` `
882+
883+ # ## Access Restrictions
884+
885+ Although the endpoint is reachable from the VM network, ** it is not equally accessible from all contexts** .
886+
887+ ** Accessible from** :
888+
889+ - Azure ** VM Agent**
890+ - Azure ** Run Command**
891+ - ** VM Extensions**
892+
893+ ** Not reliably accessible from** :
894+
895+ - Interactive SSH sessions (e.g., ` azureuser` )
896+ - Unprivileged processes inside the VM
897+
898+ This is because:
899+
900+ - The WireServer is designed for ** platform-agent communication**
901+ - Requests may require ** specific headers, timing, or context**
902+ - Some responses are only available to the ** VM Agent execution environment**
903+
904+ ---
905+
906+ # ## Run Command vs SSH Context
907+
908+ Azure provides multiple ways to execute commands inside a VM, but ** they do not run in the same context** .
909+
910+ ---
911+
912+ # ### Run Command
913+
914+ Run Command is an Azure feature that executes scripts via the ** VM Agent** .
915+
916+ - Uses: ` Microsoft.Compute/virtualMachines/runCommand/action`
917+ - Runs with ** agent-level privileges**
918+ - Has access to:
919+ - WireServer
920+ - GoalState
921+ - ExtensionsConfig
922+
923+ Example:
924+
925+ ` ` ` bash
926+ az vm run-command invoke \
927+ --resource-group < rsc-group> \
928+ --name < vm-name> \
929+ --command-id RunShellScript \
930+ --scripts @script.sh
931+ ` ` `
932+
933+ # ### SSH Session
934+
935+ When connecting via SSH:
936+
937+ - Runs as a ** regular OS user**
938+ - Uses standard network stack
939+ - Does ** NOT have agent-level access**
940+
941+ As a result:
942+
943+ - Requests to ` 168.63.129.16` may fail or return incomplete data
944+ - GoalState may not be accessible
945+
946+ ** Script Examples:**
947+
948+ {{# tabs }}
949+ {{#tab name="Linux" }}
950+
951+ ` ` ` bash
952+ #! /usr/bin/env bash
953+ set -euo pipefail
954+
955+ ws=" http://168.63.129.16"
956+
957+ echo " [*] Getting Goal State..."
958+
959+ goal_urls=(
960+ " $ws /?comp=goalstate"
961+ " $ws /machine?comp=goalstate"
962+ " $ws /machine/?comp=goalstate"
963+ )
964+
965+ goal_xml=" "
966+ for url in " ${goal_urls[@]} " ; do
967+ if goal_xml=" $( curl -fsS -H " x-ms-version: 2012-11-30" " $url " 2> /dev/null) " ; then
968+ echo " [+] GoalState OK via $url "
969+ break
970+ fi
971+ done
972+
973+ if [[ -z " $goal_xml " ]]; then
974+ echo " [-] No GoalState endpoint responded"
975+ exit 1
976+ fi
977+
978+ ext_url=" $(
979+ GOAL_XML=" $goal_xml " python3 - << 'PY '
980+ import os
981+ import xml.etree.ElementTree as ET
982+
983+ xml = os.environ["GOAL_XML"].strip()
984+ root = ET.fromstring(xml)
985+
986+ def lname(tag):
987+ return tag.rsplit("}", 1)[-1]
988+
989+ for el in root.iter():
990+ if lname(el.tag) == "ExtensionsConfig" and (el.text or "").strip():
991+ print(el.text.strip())
992+ break
993+ PY
994+ ) "
995+
996+ if [[ -z " $ext_url " ]]; then
997+ echo " [-] No ExtensionsConfig URL found in GoalState"
998+ echo " [*] Identity-like nodes seen in GoalState:"
999+ GOAL_XML=" $goal_xml " python3 - << 'PY '
1000+ import os
1001+ import xml.etree.ElementTree as ET
1002+
1003+ xml = os.environ["GOAL_XML"].strip()
1004+ root = ET.fromstring(xml)
1005+
1006+ def lname(tag):
1007+ return tag.rsplit("}", 1)[-1]
1008+
1009+ found = False
1010+ for el in root.iter():
1011+ name = lname(el.tag)
1012+ if "Identity" in name:
1013+ found = True
1014+ text = (el.text or "").strip()
1015+ print(f"<{name}>{text}</{name}>")
1016+
1017+ if not found:
1018+ print(" (none)")
1019+ PY
1020+ exit 0
1021+ fi
1022+
1023+ echo " [*] Getting ExtensionsConfig..."
1024+ ext_xml=" $( curl -fsS -H " x-ms-version: 2012-11-30" " $ext_url " ) "
1025+
1026+ EXT_XML=" $ext_xml " python3 - << 'PY '
1027+ import os
1028+ import xml.etree.ElementTree as ET
1029+
1030+ xml = os.environ["EXT_XML"].strip()
1031+ root = ET.fromstring(xml)
1032+
1033+ def lname(tag):
1034+ return tag.rsplit("}", 1)[-1]
1035+
1036+ ids = [el for el in root.iter() if lname(el.tag) == "UserAssignedIdentity"]
1037+
1038+ if not ids:
1039+ print("[-] No UserAssignedIdentity nodes found")
1040+ print("[*] Identity-like nodes present in ExtensionsConfig:")
1041+ shown = False
1042+ for el in root.iter():
1043+ name = lname(el.tag)
1044+ if "Identity" in name:
1045+ shown = True
1046+ text = (el.text or "").strip()
1047+ attrs = " ".join(f'{k}="{v}"' for k, v in el.attrib.items())
1048+ if attrs:
1049+ print(f" <{name} {attrs}>{text}</{name}>")
1050+ else:
1051+ print(f" <{name}>{text}</{name}>")
1052+ if not shown:
1053+ print(" (none)")
1054+ raise SystemExit(0)
1055+
1056+ for idnode in ids:
1057+ client_id = ""
1058+ object_id = ""
1059+ resource_id = ""
1060+
1061+ for child in idnode.iter():
1062+ name = lname(child.tag)
1063+ text = (child.text or "").strip()
1064+ if name == "IdentityClientId":
1065+ client_id = text
1066+ elif name == "IdentityObjectId":
1067+ object_id = text
1068+ elif name == "IdentityResourceId":
1069+ resource_id = text
1070+
1071+ print()
1072+ print("[+] Managed Identity:")
1073+ print(f" ClientId : {client_id}")
1074+ print(f" ObjectId : {object_id}")
1075+ print(f" ResourceId : {resource_id}")
1076+ PY
1077+ ` ` `
1078+
1079+ {{# endtab }}
1080+
1081+ {{#tab name="Windows" }}
1082+
1083+ ` ` ` bash
1084+ $ws = " http://168.63.129.16"
1085+ $h = @{
1086+ " x-ms-version" = " 2012-11-30"
1087+ }
1088+
1089+ Write-Host " [*] Getting Goal State..." -ForegroundColor Cyan
1090+
1091+ $goalUrls = @ (
1092+ " $ws /?comp=goalstate" ,
1093+ " $ws /machine?comp=goalstate" ,
1094+ " $ws /machine/?comp=goalstate"
1095+ )
1096+
1097+ $gs = $null
1098+
1099+ foreach ($url in $goalUrls ) {
1100+ try {
1101+ $gs = Invoke-WebRequest -Uri $url -Headers $h -UseBasicParsing -ErrorAction Stop
1102+ Write-Host " [+] GoalState OK via $url " -ForegroundColor Green
1103+ break
1104+ } catch {}
1105+ }
1106+
1107+ if (-not $gs ) {
1108+ Write-Host " [-] No GoalState endpoint responded" -ForegroundColor Red
1109+ return
1110+ }
1111+
1112+ [xml]$xml = $gs .Content
1113+ $cfg = $xml .GoalState.Container.RoleInstanceList.RoleInstance.Configuration
1114+
1115+ $extUrl = $cfg .ExtensionsConfig
1116+
1117+ Write-Host " [*] Getting ExtensionsConfig..." -ForegroundColor Cyan
1118+
1119+ try {
1120+ $ext = Invoke-WebRequest -Uri $extUrl -Headers $h -UseBasicParsing -ErrorAction Stop
1121+ [xml]$extXml = $ext .Content
1122+ } catch {
1123+ Write-Host " [-] Error getting ExtensionsConfig" -ForegroundColor Red
1124+ return
1125+ }
1126+
1127+ # Extract Managed Identity info
1128+ $ids = $extXml .SelectNodes(" //UserAssignedIdentity" )
1129+
1130+ if (! $ids ) {
1131+ Write-Host " [-] No User Assigned Identities found" -ForegroundColor Red
1132+ return
1133+ }
1134+
1135+ foreach ($id in $ids ) {
1136+ $clientId = $id .IdentityClientId
1137+ $objectId = $id .IdentityObjectId
1138+ $resourceId = $id .IdentityResourceId
1139+
1140+ Write-Host " ` n[+] Managed Identity:" -ForegroundColor Green
1141+ Write-Host " ClientId : $clientId "
1142+ Write-Host " ObjectId : $objectId "
1143+ Write-Host " ResourceId : $resourceId "
1144+ }
1145+ ` ` `
1146+
1147+ {{# endtab }}
1148+ {{#endtabs }}
1149+
1150+
8401151## Privilege Escalation
8411152
8421153{{#ref}}
@@ -866,6 +1177,9 @@ Invoke-AzureRmVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt
8661177- [https://learn.microsoft.com/en-us/azure/virtual-machines/overview](https://learn.microsoft.com/en-us/azure/virtual-machines/overview)
8671178- [https://hausec.com/2022/05/04/azure-virtual-machine-execution-techniques/](https://hausec.com/2022/05/04/azure-virtual-machine-execution-techniques/)
8681179- [https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service](https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service)
1180+ - [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)
1181+ - [https://learn.microsoft.com/en-us/azure/virtual-machines/run-command](https://learn.microsoft.com/en-us/azure/virtual-machines/run-command)
1182+ - [https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux](https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux)
8691183
8701184{{#include ../../../../banners/hacktricks-training.md}}
8711185
0 commit comments