{"id":539,"date":"2024-02-17T12:45:18","date_gmt":"2024-02-17T11:45:18","guid":{"rendered":"https:\/\/sparrow365.de\/?p=539"},"modified":"2024-11-04T20:20:52","modified_gmt":"2024-11-04T19:20:52","slug":"theater-gegen-overprivilege-pam-edition-praxis","status":"publish","type":"post","link":"https:\/\/sparrow365.de\/index.php\/2024\/02\/17\/theater-gegen-overprivilege-pam-edition-praxis\/","title":{"rendered":"Theater gegen Overprivilege: PAM Edition &#8211; Praxis"},"content":{"rendered":"<blockquote>\n<p>Dieser Artikel ist der Praktische Part zur <a href=\"https:\/\/sparrow365.de\/index.php\/2023\/12\/31\/theater-gegen-overprivilege-pam-edition-theorie\/\">Theorie<\/a> &#8211; f\u00fcr die Antwort auf die Frage <em>&quot;Wozu das ganze?&quot;<\/em> also bitte dort vorbei schauen.<\/p>\n<\/blockquote>\n<p>Damit ich nicht als jemand dastehe der sagt &quot;man m\u00fcsste mal&quot; und dann nichts tut, folgt hier ein (f\u00fcr meine Verh\u00e4ltnisse) <strong>&quot;schneller&quot; Proof of Concept<\/strong> wie man in Realit\u00e4t Passwort Rotation handhaben k\u00f6nnte. Als kurze Erinnerung die Komponenten die wir f\u00fcr eine L\u00f6sung brauchen:<\/p>\n<ol>\n<li>Wir setzen Initialkennw\u00f6rter, die unsere PAM L\u00f6sung kennt<\/li>\n<li>Die PAM L\u00f6sung kann von sich aus Kennw\u00f6rter rotieren wenn notwendig \/ erw\u00fcnscht<\/li>\n<li>Benutzer, wo das Kennwort out of Sync mit der PAM L\u00f6sung ist, k\u00f6nnen wieder aufgegleist werden<\/li>\n<\/ol>\n<p>Ich werde f\u00fcr jeden der einzelnen Punkte ein PoC Snippet in PowerShell bereit stellen &#8211; die individuelle Implementierung h\u00e4ngt von den Feinheiten der PAM L\u00f6sung und den Anforderungen ihrer Organisation ab.<\/p>\n<p>Die bessere L\u00f6sung ist sicherlich, notwendige Rechte auf eine Azure Function auszulagern, die entsprechend nur Passw\u00f6rter Rotiert &#8211; es hat aber nicht jeder Zugang zu Azure Resourcen, die hier beschriebene L\u00f6sung ist rein PowerShell.<\/p>\n<blockquote>\n<p><em>F\u00fcr volle Transparenz, der hier beschriebene Ansatz ist besser als Global Admin \/ Privileged Auth Admin auf einer App Registration, hat aber ein<\/em> <strong>gro\u00dfes Manko<\/strong>:<br \/>\n<em>Die PAM L\u00f6sung w\u00fcrde sich in meinem PoC \u00fcber den<\/em> <strong>weniger sicheren<\/strong> <em><a href=\"https:\/\/learn.microsoft.com\/en-us\/entra\/identity-platform\/v2-oauth-ropc\">OIDC ROPC Auth Flow<\/a> als Benutzer Authentifizeren, um das &quot;eigene&quot; Kennwort zu rotieren. Das ist notwendig, um interaktive Prompts zu vermeiden &#8211; OIDC will aber eigentlich nicht, dass sich Systeme<\/em> <strong>unmittelbar<\/strong> <em>als Benutzer Authentifizieren<\/em><br \/>\nErgebnis:<\/p>\n<ul>\n<li>Das Script Funktioniert nur, wenn Conditional Access <strong>kein MFA oder Compliant Device<\/strong> fordert (In der Regel \u00fcber App + IP Exclusion &#8211; nat\u00fcrlich nicht Optimal)<\/li>\n<li>Alternativ m\u00fcsste man das Script anpassen um einen Interaktiven Flow zu nutzen (<code><code>Connect-MgGraph<\/code><\/code> oder \u00e4hnliches) und Beispielsweise mit Hilfe von AutoIT den Login Prozess navigieren   <\/li>\n<\/ul>\n<p><em>Mit dieser Wand an caveats stellt sich die Frage, warum ich \u00fcberhaupt diesen Weg w\u00e4hle: ganz einfach, weil ich die meisten Snippets schon habe, und diese nur zusammen f\u00fchren muss.<\/em><br \/>\n^ Dieser Satz wurde vor meiner <a href=\"https:\/\/sparrow365.de\/index.php\/2024\/01\/28\/connect-mggraph-mit-benutzername-und-passwort\/\">ROPC Detour<\/a> geschrieben \ud83e\udd21<\/p>\n<\/blockquote>\n<p><br class=\"\"><\/p>\n<h2>Setzen des Benutzerkennworts auf einen bekannten Wert<\/h2>\n<p>Vorne Weg m\u00fcssen wir kl\u00e4ren, wie die PAM L\u00f6sung an das <strong>initiale Kennwort<\/strong> kommt. Ich m\u00f6chte <strong>keine Kennw\u00f6rter \u00fcbertragen<\/strong>, wo keine \u00dcbertragung notwendig ist &#8211; in meinem schnellen Beispiel wird ein ausreichend privilegierter Administrator (oder die Identity Governance L\u00f6sung) <strong>beim Anlegen<\/strong> der Benutzer ein Kennwort setzen, dass auch die <strong>PAM L\u00f6sung <em>errechnen<\/em><\/strong> kann.<\/p>\n<p>Allen Benutzern das selbe Kennwort zu setzen kommt aus hoffentlich bekannten Gr\u00fcnden nicht in Frage. Die mir pers\u00f6nlich offensichtlichsten L\u00f6sungen sind, den Benutzernamen mit einem <strong>geteilten Saltwert<\/strong> zu <strong>Hashen<\/strong> oder mit dem <strong>gleichen Key zu verschl\u00fcsseln<\/strong>.<br \/>\nSo kommen beide Seiten immer <strong>mit gegebenem Benutzer zum selben Wert<\/strong>, den wir entsprechend als Kennwort nutzen k\u00f6nnen. Der Wert ist f\u00fcr jeden Benutzer unterschiedlich und es kann kein System erraten werden, selbst wenn irgenwann ein einzelnes Kennwort abflie\u00dft.<\/p>\n<p>Den geteilten Hash w\u00fcrde ich dann nutzen, wenn ich einen symmetrischen Wert austauschen muss bzw. Abrufe. Es gibt in Windows eingebaute Hashmethoden, die f\u00fcr Kennw\u00f6rter gebaut sind.<br \/>\nVerschl\u00fcsseln w\u00fcrde ich, wenn es eine Schnittstelle gibt, mit der ich Cryptographische Funktionen Auslagern kann &#8211; Beispielsweise ein HSM mit entsprechender API &#8211; so etwas habe ich aber nicht zur Verf\u00fcgung, entsprechend bleibe ich bei Hashing. Das sind aber keine begr\u00fcndeten Erfahrungswerte, sondern einfach ein Gef\u00fchl.<\/p>\n<p><br class=\"\"><\/p>\n<p>Um also unsere Kennw\u00f6rter zu generieren nutzen wir <a href=\"https:\/\/gist.github.com\/dreadsend\/0abecfa2ef687ccebb499e9f297ee633\">folgende Funktion<\/a>:<\/p>\n<blockquote>\n<p><em>Da in unserem Fall der Hash dem Kennwort gleichzusetzen ist, ist die Funktion etwas geh\u00e4rtet &#8211; der Hash und der Salt sind Secure Strings, Klartext wird so schnell wie m\u00f6glich aus dem Speicher gel\u00f6scht &#8211; siehe die <a href=\"https:\/\/get-powershellblog.blogspot.com\/2017\/06\/how-safe-are-your-strings.html\">immens interessante Arbeit von Mark Kraus<\/a><\/em><\/p>\n<\/blockquote>\n<p><br class=\"\"><\/p>\n<pre><code class=\"language-powershell\">function New-Pbkdf2Hash {\n  param (\n    [Parameter(Mandatory = $true, Position = 0)]\n    [ValidateNotNull()]\n    [string]$toHash,\n\n    [Parameter(Mandatory = $true, Position = 1)]\n    [ValidateNotNull()]\n    [Securestring]$salt,\n\n    [Parameter(Mandatory = $false)]\n    [ValidateSet(&quot;SHA256&quot;,&quot;SHA3_256&quot;,&quot;SHA384&quot;,&quot;SHA3_384&quot;,&quot;SHA512&quot;,&quot;SHA3_512&quot;)]\n    [string]$hashAlg = &quot;SHA512&quot;,\n\n    [Parameter(Mandatory = $false)]\n    [ValidateSet(&quot;lower&quot;,&quot;upper&quot;)]\n    [string]$standardCase, # Falls wir aufgrund von H\u00e4ndischen Eintr\u00e4gen \/ uneinheitlicher Datenquelle Case Sensitivity umgehen wollen\n\n    # F\u00fcr Nutzung als Kennwort Speicher ist der Empfohlene Wert 210.000 Iterationen, aber dann dauert die Kalkulation schon ein paar Sekunden \n    # Das Kennwort wird in unserem Fall nicht gespeichert, sondern nur der initiale Wert generiert\n    [Parameter(Mandatory = $false)]\n    [int]$iterations = 1000, \n\n    [Parameter(Mandatory = $false)]\n    [int]$hashLength = 60\n  )\n\n  switch ($standardCase) {\n    &quot;lower&quot; { $toHash = $toHash.ToLower() }\n    &quot;upper&quot; { $toHash = $toHash.ToUpper() }\n  }\n\n  try {\n    # Generieren des Hashes \/ Passwort und Konvertieren zu Securestring aus Byte Array\n    $saltBytes = [Text.Encoding]::UTF8.GetBytes([System.Net.NetworkCredential]::new(&quot;&quot;, $salt).Password)\n    # Die dedizierte Pbkdf2 Methode ist nur im vollen .NET Verf\u00fcgbar, nicht .NET Framework\n    $hashResult = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($toHash, $saltBytes, $iterations, $hashAlg)\n    $hashString =  ConvertTo-Securestring &quot;$( [System.Convert]::ToBase64String($hashResult.GetBytes($hashLength)) )&quot; -AsPlainText -Force\n  }\n  catch {\n    Throw $_.Exception.Message\n  }\n  finally {\n    # Entfernen kritischer Werte aus dem Arbeitsspeicher\n    $sensitiveVars = @(&quot;saltBytes&quot;,&quot;hashResult&quot;)\n    Remove-Variable $sensitiveVars\n    [gc]::Collect()\n  }\n\n  return $hashString\n}<\/code><\/pre>\n<p><br class=\"\"><\/p>\n<p>Jetzt wo wir unsere Funktion zum generieren der Passw\u00f6rter haben, m\u00fcssen wir uns auf die Parameter der Hashfunktion einigen. F\u00fcr meinen PoC gehe ich davon aus, dass <strong>Periodisch der Hash<\/strong> zwischen PAM und Entra ID \/ Identity Governance Admins <strong>abgestimmt wird<\/strong>.<br \/>\nIch speichere den Wert auf den entsprechenden Hosts mit<br \/>\n<code><code>ConvertTo-SecureString &quot;&lt;DiesIstUnserGeteilterWert&gt;&quot; -AsPlainText -Force | ConvertFrom-SecureString | Out-File .\\encryptedHash.txt<\/code><\/code>  <\/p>\n<p>So ist zumindest minimale Sicherheit gegeben. <strong>Achtung &#8211; Denke daran, dass der Export mit dem gleichen Konto erfolgen muss, das sp\u00e4ter bei der Ausf\u00fchrung genutzt wird &#8211; sonst schl\u00e4gt die Entschl\u00fcsselung fehl.<\/strong><\/p>\n<blockquote>\n<p>Der Saltwert sollte idealerweise an einem gemeinsam zug\u00e4nglichen sicheren Ort gespeichert werden und regelm\u00e4\u00dfig rotiert werden &#8211; die meisten PAM L\u00f6sungen haben auch Secret Management APIs, die sich f\u00fcr diese Methode anbieten.<\/p>\n<\/blockquote>\n<p><br class=\"\"><\/p>\n<p>Der Identity Provider \/ Entra Admin sollte jetzt als bei der Anlage von Benutzern das Kennwort wie folgt vorgeben:<\/p>\n<pre><code class=\"language-powershell\">Connect-MgGraph -Scopes &#039;User.ReadWrite.All&#039;\n\n# Importieren des Salt\n$salt = Get-Content .\\encryptedHash.txt | ConvertTo-SecureString\n\n$upn = &quot;max.mustermann@test.de&quot;\n\n# Wir gehen von Handarbeit auf beiden Seiten aus, also einigen wir uns auf Lowercase\n$sharedPassword = New-Pbkdf2Hash -toHash $upn -salt $salt -standardCase lower\n\n# Wir m\u00fcssen sicherstellen, dass der Benutzer das Passwort beim n\u00e4chsten Login nicht \u00e4ndern muss, sonst funktioniert der ROPC Flow nicht\n# Daher forceChangePasswordNextSignIn = $false\n$params = @{\n    displayName       = &quot;Max Mustermann&quot;\n    passwordProfile   = @{ \n        Password                      = [System.Net.NetworkCredential]::new(&quot;&quot;, $sharedPassword).Password \n        forceChangePasswordNextSignIn = $false\n    }\n    accountEnabled    = $true\n    mailNickName      = &quot;max.mustermann&quot;\n    userPrincipalName = $upn\n    #...\n}\n\nNew-Mguser @params\n# Invoke-MgGraphRequest POST &quot;\/v1.0\/users&quot; -body $params\n\nRemove-Variable &quot;params&quot;\n[gc]::collect()<\/code><\/pre>\n<p>Wenn alles geklappt hat, bekommen wir eine Antwort mit dem neu erstellten Benutzerobjekt, aber das interessiert uns gerade nicht so.<\/p>\n<p>Die PAM L\u00f6sung kann jetzt das Passwort Rotieren, insofern ihr der Salt bekannt ist und der Benutzer im Vault angelegt wurde.<\/p>\n<p><br class=\"\"><\/p>\n<h2>Kennwort Rotation<\/h2>\n<h3>Voraussetzungen<\/h3>\n<ol>\n<li>Entra ID App Registration mit <strong>Delegate<\/strong> &quot;Directory.AccessAsUser.All&quot; Rechten und Zugeh\u00f6rig:\n<ol>\n<li>Tenant ID<\/li>\n<li>Client ID<\/li>\n<li>Thumbprint des Zertifikats auf dem PAM Server oder Secret<\/li>\n<li>Falls Anwendbar: Conditional Access Ausnahme f\u00fcr die Enterprise App <em>(Reminder: wenn man keine Public Clients in der Registration aktiviert, kann nur unser Server mit dem Zertifikat die App nutzen)<\/em><\/li>\n<\/ol>\n<\/li>\n<li>Vom PAM Server ausgehende Verbindung zu <a href=\"https:\/\/learn.microsoft.com\/de-de\/microsoft-365\/enterprise\/urls-and-ip-address-ranges?view=o365-worldwide#microsoft-365-common-and-office-online\">Microsoft 365 Adressen<\/a> (mindestens ID 56)<\/li>\n<li><strong>Microsoft.Graph.Authentication<\/strong> PowerShell Modul auf dem PAM Server<\/li>\n<\/ol>\n<blockquote>\n<p><strong><em>Wenn wir uns aber hier als Benutzer Anmelden, und die App erlaubt mit allen Rechten des Benutzers mit der Graph API zu interagieren, warum Benutzen wir dann nicht einfach dieses Konto um Kennw\u00f6rter zu setzen?<\/em><\/strong><br \/>\nDann m\u00fcssten wir vorab pr\u00fcfen \/ wissen, welche Rechte all unsere Konten haben, um dann ein einzelnes Konto als Service Account zu nutzen.<br \/>\nIch bevorzuge es, eine allgemeine L\u00f6sung zu implementieren, die auch Funktioniert, wenn man nicht zuf\u00e4llig ein hochprivilegiertes Konto im Vault hat. Au\u00dferdem sollten diese Benutzer keine stehenden Rechte haben, sondern \u00fcber <a href=\"https:\/\/learn.microsoft.com\/en-us\/entra\/id-governance\/privileged-identity-management\/pim-deployment-plan\">PIM Just in in Time aktivieren<\/a> &#8211; und zwar mit Interaktivem MFA.<\/p>\n<\/blockquote>\n<p><br class=\"\"><\/p>\n<h3>Ausf\u00fchrung<\/h3>\n<p>Achtung: Wir Nutzen die <a href=\"https:\/\/gist.github.com\/dreadsend\/0abecfa2ef687ccebb499e9f297ee633\">Funktion von Oben<\/a> f\u00fcr die Hash bzw. Kennwortgeneration und die <a href=\"https:\/\/gist.github.com\/dreadsend\/fb46410db717ca3e937acbc9fccca754\">Graph ROPC Authentifizierung<\/a><br \/>\nEs werden die selben Variablen weiter genutzt.<\/p>\n<pre><code class=\"language-powershell\">$sharedPassword = New-Pbkdf2Hash -toHash $upn -salt $salt -standardCase lower\n$newPassword = &quot;&lt;Neues_Passwort&gt;&quot; # Wie dieses Kennwort generiert wird h\u00e4ngt von der PAM L\u00f6sung ab\n\n# F\u00fcr den Verbindugnsaufbau ben\u00f6tigen wir Benutzername und Passwort des Users, dem wir das Kennwort rotieren wollen\n[PSCredential]$userCred = New-Object System.Management.Automation.PSCredential ($upn, $sharedPassword)\nConnect-ROPCGraph -userCredentials $userCred -tenantId $conf.tenantID -clientId $conf.clientID -certificateThumbprint $conf.thumb\n\n# Wir Setzen unser neues Passwort\n$params = @{\n    currentPassword = $usercred.GetNetworkCredential().Password\n    newPassword     = $newPassword \n}\n$res = Invoke-MgGraphRequest POST &quot;\/v1.0\/me\/changePassword&quot; -Body $params\n\n# Alternative mit Microsoft.Graph.Users.Actions Modul:\n# Update-MgUserPassword -UserId $userCred.Username -BodyParameter $params\n\nRemove-Variable &quot;params&quot;, &quot;newPassword&quot;\n[gc]::collect()<\/code><\/pre>\n<p>Nach erfolgreicher Ausf\u00fchrung erhalten wir keine Antwort, aber beim n\u00e4chsten Login m\u00fcssen wir das neue Passwort nutzen.<\/p>\n<p><br class=\"\"><\/p>\n<h2>Wiederherstellen des PAM Management (Reconciliation)<\/h2>\n<blockquote>\n<p>Und wieder, wenn man eine dedizierte Azure Function nutzt, w\u00fcrde dies kein Thema sein &#8211; aber ich habe schon zu reinem PowerShell committed. Vielleicht irgendwann mal wenn es jemand interessiert \ud83e\udd37   <\/p>\n<\/blockquote>\n<p>In dieser Variante muss (die Liste) der problematischen Benutzer einem Entra ID Administrator (User Administrator wenn es sich um regul\u00e4re Konten handelt, Privileged Authentication Administrator \/ Global Admin wenn es sich um privilegierte Konten handelt) geliefert werden, der dann folgendes Ausf\u00fchrt:<\/p>\n<pre><code class=\"language-powershell\">$toReconcile = @(&quot;UPN1&quot;, &quot;UPN2&quot;) # Oder aus CSV Laden\nConnect-MgGraph -Scopes &#039;User.ReadWrite.All&#039;\n\n# Wir laden wieder unser &quot;Shared Secret&quot; als Salt Wert\n$salt = Get-Content .\\encryptedHash.txt | ConvertTo-SecureString\n\nforeach ($upn in $toReconcile) {\n    $sharedPassword = New-Pbkdf2Hash -toHash $upn -salt $salt -standardCase lower\n\n    $params = @{\n        passwordProfile = @{ \n            Password                      = [System.Net.NetworkCredential]::new(&quot;&quot;, $sharedPassword).Password \n            forceChangePasswordNextSignIn = $false\n        }\n    }\n\n    Invoke-MgGraphRequest PATCH &quot;\/v1.0\/users\/$upn&quot; -Body $params\n    # Update-MgUser -UserId $upn -PasswordProfile @{forceChangePasswordNextSignIn = $false; password = [System.Net.NetworkCredential]::new(&quot;&quot;, $sharedPassword).Password}\n\n    Remove-Variable &quot;params&quot;, &quot;sharedPassword&quot;\n    [gc]::collect()\n}<\/code><\/pre>\n<p>! Es sollte immer sicher gestellt werden, dass m\u00f6glichst bald nach dem initialen Setzen dass Kennwort durch die PAM L\u00f6sung rotiert wird<\/p>\n<h2>Fazit<\/h2>\n<p>Wenn man wirklich wert auf <strong>least privilege<\/strong> legt, bekommt man oft nicht die einfachste L\u00f6sung. Und ich sage auch ganz offen, dass es nicht allzu realistisch ist, dass dieser PoC in jeder Umgebung eingesetzt wird.<br \/>\nIch hoffe mehr, dass ein PAM-Anbieter oder Berater auf diesen Artikel aufmerksam wird und die M\u00f6glichkeit erkennt, sich der Verbesserung der Applikation anzunehmen. Oder irgend ein anderer Entra ID Administrator fragt sich, ob man wirklich mit <em>Privileged Authentication Administrator<\/em> um sich werfen muss, und kann sich fundiert beschweren.<\/p>\n<p>Und vielleicht bekommen wir irgendwann einfach ein eingebautes Modul, oder einen Wizard, oder was auch immer &#8211; damit wir nicht mehr unn\u00f6tig gro\u00dfe Sicherheitsrisiken in unsere Umgebungen installieren.<\/p>\n<p>Oder wir bekommen granularere Application Permission Scopes von Microsoft.<\/p>\n<p><br class=\"\"><\/p>\n<p>Ich werde keine Kommentare moderieren und m\u00f6chte Ihre E-Mail-Adresse nicht; bitte beteiligen Sie sich an der Diskussion \u00fcber <a href=\"https:\/\/www.linkedin.com\/posts\/julian-sperling-4bba72228_theater-gegen-overprivilege-pam-edition-activity-7164618401200513024-bPNS?utm_source=share&amp;utm_medium=member_desktop\">meinen Zugeh\u00f6rigen LinkedIn Post<\/a>.<\/p>\n<p><br class=\"\"><\/p>\n<p>Wenn sie an den Dingen interessiert sind die ich tue <a href=\"https:\/\/www.linkedin.com\/in\/julian-sperling-4bba72228\/\">folgen sie mir auf LinkedIn<\/a>.   <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dieser Artikel ist der Praktische Part zur Theorie &#8211; f\u00fcr die Antwort auf die Frage &quot;Wozu das ganze?&quot; also bitte dort vorbei schauen. Damit ich nicht als jemand dastehe der sagt &quot;man m\u00fcsste mal&quot; und dann nichts tut, folgt hier ein (f\u00fcr meine Verh\u00e4ltnisse) &quot;schneller&quot; Proof of Concept wie man in Realit\u00e4t Passwort Rotation handhaben&#8230; &raquo; <a class=\"read-more-link\" href=\"https:\/\/sparrow365.de\/index.php\/2024\/02\/17\/theater-gegen-overprivilege-pam-edition-praxis\/\">weiterlesen<\/a><\/p>\n","protected":false},"author":2,"featured_media":542,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24,48],"tags":[72,70,156,162,64,52,56,150,148,107,154],"class_list":["post-539","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-me-id","category-powershell","tag-aad","tag-azure-ad","tag-best-practices","tag-cybersecurity","tag-entra","tag-entra-id","tag-graph-api","tag-overprivilege","tag-pam","tag-passwoerter","tag-privileged-access-management"],"_links":{"self":[{"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/posts\/539","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/comments?post=539"}],"version-history":[{"count":4,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/posts\/539\/revisions"}],"predecessor-version":[{"id":548,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/posts\/539\/revisions\/548"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/media\/542"}],"wp:attachment":[{"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/media?parent=539"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/categories?post=539"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sparrow365.de\/index.php\/wp-json\/wp\/v2\/tags?post=539"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}