Don’t try this at home (or how to enable Core Server Remote Management for AD FS GUI)

I’ve been running AD FS on Core servers for some time now, mostly because I like the smaller footprint and centralized management experience.

The smaller footprint also guarantees:

  • That there are less consumed resources
  • That the there is less potential attack surface

But I want my GUI…

ADFSs

The lovely GUI Icon

Sometimes I’ve felt the temptation to just peek into AD FS GUI from remote administration host… only to remember that it’s not possible due to the fact that there is no RSAT for managing AD FS.

Yes this, this is crazy… I am doing it just for the kick of it

CoreSRVR

Scheme

This the part that you definitely shouldn’t do in production, or even in staging if you value your deployment – Nonetheless, I had the temptation to see if I can crack this nut:

  • Install and configure remote management host temporarily as AD FS slave node
  • Disable and stop AD FS service on the remote management node, because you won’t really be needing the service itself, you still need the installation to do management of the primary node

MGMT2

AD FS -> Nobody here, go away!

  • Do a crazy portproxy with NETSH to send port 1500 to primary AD FS node

MGMT3

TCP 1500 has now new destination

  • Enjoy remote management (and maybe some crazy side effects…)

MGMT

Welcome to AD FS management on Core Server!

#Disable and stop the AD FS service on management computer
Get-Service adfssrv | Set-Service -StartupType Disabled; Stop-Service adfssrv
#Do a crazy binding to port 1500
netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=1500connectaddress=yourremotehost connectport=1500
#End

Speculation if this was really plausible approach:

Q: Would it just be smarter to have the primary node on GUI enabled server?

A: Pretty much yes

(in this crazy demonstrated approach the only difference, is that you don’t have to have AD FS service running on the remote management host)

Br, Joosua!

Azure AD Application Proxy – SSO and Authorization notes from the field

This blog attempts to capture some of the Single Sign-On and Authorization scenarios I’ve dealt with during my extensive tenure with Azure AD Application proxy deployments.

To keep this blog short there is no description of what Azure AD Application Proxy is generally. More information can be found here

  • AAD SSO Column in the table indicates whether Single Sign-On setting on Azure AD Enterprise Application is enabled or disabled
  • Native Clients in the following table assumes scenario where browser-like features (web view etc) are only used to perform subset of functions, such as authenticating user to Azure AD. All other calls are authorized to Azure AD Application via the usage of Access Tokens passed in Authorization Header
    • For example: The native app may not use cookies to hold session persistence, but rather refresh token stored in the device
    • Some native clients rely completely on running browser inside the app, or open external browser (payment views etc). In these scenarios the application may support similar option as described in the second column (2 Browser)


Back-end Protocol
AAD SSO1 Native Client 2 Browser
SAML – WS/FED (SSO)*Enabled:SAML / or Disabled*No – At least when the most common binding: (Redirect -> POST) is used
Yes 1.Further reading 2.Source
Header Based Authentication against back-end APIDisabledYes With Azure API management using JWT_Bearer Grant Yes With Azure API management using JWT_Bearer Grant (Note this solution is not for rendering browser views. To render browser view from API response additional client side rendering is required
OAuth2 / OIDC Disabled No, unless the application is using web-view that maintains the session to Azure AD (source) Yes – While existing Azure AD session is maintained within browser. Fetch/XHR, or redirect based client can make requests to the back-end app, which can then validate tokens in Authorization Bearer scenario (source)
Header Based Authentication using PingAccessEnabled:Header-BasedNot tested – Probably? (Refer to the documentation) Not tested – Probably? (Refer to the documentation)
Windows integrated Authentication / SPNEGO * * Enabled:Windows Integrated Authentication
Yes – Kerberos Constrained Delegation by the App Proxy Agent is transparent to the client
Yes – Kerberos Constrained Delegation by the App Proxy Agent is transparent to the client
Basic / NTLM Authentication  * * * Disabled No – Authorization header is reserved for Bearer Tokens, which App Proxy Consumes Yes – While existing Azure AD session is maintained within browser, Basic Authentication can be used. NTLM can be used as well, applies also to WIA scenario when WIA fallbacks to NTLM
SOAP API (U/P)Disabled
Yes – When credentials are passed as part of body, and authorization header contains the Bearer Token
Yes – When credentials are passed as part of body, and authorization header contains the Bearer Token
Client CertificateNANo No

Clarifications

  • * Existing IDP Session
    • You could also have specified another Azure AD application separate from the App Proxy Enterprise Application. The SSO works regardless of the SSO setting, when the application uses sames IDP which the user has existing session on.
  • * * About SPNEGO [Source]
    • ”If you configure a connector machine for SPNEGO, make sure that all other connectors in that Connector group are also configured with SPNEGO. Applications expecting standard KCD should be routed through other connectors that are not configured for SPNEGO ”
  • * * * Compound Authorization (in some cases Double authentication)
    • First Authorization: Azure AD Consumes the Authorization header
      • While Sending additional Bearer token in other than Authorization header, or in body payload
      • Or While Sending additional Credentials in other than Authorization header, or in body payload
      • Sequentially (after) challenged by the back-end app running in browser session

Good to know

  • Native API clients using AAD Pre-authentication
    • With Native Clients Forwarding JWT tokens to back-end service doesn’t work unless the Native client is calling Azure AD App Proxy from webview like representation or pure browser
  • Only user identities can passed through application having Azure AD pre-authentication enabled
    • For example Application identity (Client Credentials Flow) doesn’t work with Azure AD App Proxy Applications, unless Compound Authorization is used
    • Apps can still work in chain (On-Behalf-of-Flow for example) as long as the identity being delegated is user identity
  • If you have Windows Authentication enabled in the Application, ensure the deployment scenario supports Kerberos Constrained Delegation, and the Azure AD App Proxy agent can act as delegating party to the SPN.
  • While Mutual TLS authentication isn’t supported features such as require device compliance from and other Client recognizing options are available when combined with Conditional Access.
  • There are also other scenarios which I may cover in another blog post
    • Using a mix of pre-authenticated and pass-trough Azure AD Applications
    • Linked applications
    • Password-based

Further reading

https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/application-proxy-configure-single-sign-on-with-kcd

https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/application-proxy-configure-for-claims-aware-applications

If you find any discrepancies (errors etc) with the information on the table just dm me at @santasalojoosua, or post question to this blog, and I shall correct it /explain it.

Experimental testing: Azure AD Application Proxy With Azure Application Gateway WAF

Disclaimer: This configuration example is only for experimental testing. I’d advise against using it in any kind of serious scenario as the configuration has no official support …and is based on-whim testing 🙂

I was recently browsing Feedback for Azure AD Application Proxy, and noticed that I am not the only one who would like to see WAF functionality enabled for AAD App Proxy.

The comment for ”Under Review” raised my curiosity ” We are reviewing options for creating smoother integration and providing documentation on how to layer the two. ”
https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/31964980-allow-azure-ad-app-proxy-apps-to-use-the-azure-web

While its fairly easy to retrofit WAF API -scenario with Azure AD App Proxy and API management, it’s another thing to also make it render web pages in a browser without a custom front end. https://securecloud.blog/2019/06/01/concept-publish-on-prem-api-using-aad-app-proxy-and-api-management-with-azure-ad-jwt-bearer-grant/

Test configuration

Application Proxy Configuration

Application Gateway Configuration

  1. Create Listener binding the cert for App Proxy Apps FQDN

2. Add the IP of Azure AD App Proxy as back-end target

  • The logic: Point the DNS to Application Gateway instead to App Proxy Application, and point the application gateway to that CNAME, and override the naming bind in the listener of Application Gateway
Use the name AppProxy DNS should be pointed at

3. Override the host name to the same name that is in the DNS (this would create loop, unless we hadn’t different name in the back-end pool)

Now watch the back end for traffic originating through WAF + AppProxy

Back-end application receiving WAF forwarded traffic, with both App Proxy and Application Gateway headers
  • Obvious problem is that the attacker can bypass WAF by ”gatewaying” itself with custom DNS directly to the AppProxy.
    • Obviously there is no public reference anywhere, what is the IP for Azure AD App Proxy app, or whats the name of the app, as the communication goes through App GW, and DNS points to App GW. Depending on the back-end app, the attacker might figure out a simple way, to get the app ”echoing” back the route (For example headers…)
  • Sub optimal mitigations would be (if back-end app is configurable, and you want to check in back end that did the request come from WAF)
      • that the only calls that have last X-Forwarded-For IP as Application Gateway would be authorized.
      • Or to set an ”secret” header in Application Gateway URL rewrite rules, and check the presence of that header in the back-end app for authorization
  • If I had to do this in production today, I would place WAF in the internal network before the back-end app
AppGW WAF headers
The app is the consent extractor, which i just used as placeholder app (has no context meaning in this scenario)

I will stay tuned to see if this feature gets actually implemented!

Br Joosua!

Reddit Thread Answer: Azure AD – Autologon endpoint

This is an endpoint which is not used independently, but rather as a sub-feature of another feature in Azure AD. The feature is Azure AD Seamless SSO, which you can find out in general more by clicking the link in the picture

Seamless Single Sign On - Web app flow
https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-sso-how-it-works#how-does-sign-in-on-a-native-client-with-seamless-sso-work

Autologon endpoint is used to facilitate particular flows of Azure AD Seamless SSO. One of the flows provides WS-Trust endpoint via metadata query. This endpoint is available for managed account types on all global Azure AD tenants. This is regardless if Seamless SSO is enabled or not.

AFAIK the flow isn’t meant to be used outside of the Seamless SSO scenarios. ROPC (Resource Owner Password Flow) would be the recommended flow if you absolutely need to transport username and password outside of the browsers input flow (passive ”profiles”)

Example: Get token from UserNameMixed endpoint, and exchange the SAML Token (DesktopSSO) for OAuth2 Access Token

[string]$username= Read-Host -Prompt "Enter UserName"
$securedValue = Read-Host -AsSecureString -Prompt "uurPass"
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securedValue)
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
$requestid = [System.Guid]::NewGuid().guid
$domain = ($username -split "@")[1]
Invoke-RestMethod -Method Get -UseBasicParsing ("https://login.microsoftonline.com/common/userrealm/$username" + "?api-version=1.0") -UserAgent $userAgent
$headers = @{
"client-request-id"=$requestid
"return-client-request-id"="true"
}
$uri2 = "https://autologon.microsoftazuread-sso.com/$domain/winauth/trust/2005/usernamemixed?client-request-id=$requestid"
[xml]$data = '<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
    <a:MessageID>urn:uuid:36a6762f-40a9-4279-b4e6-b01c944b5698</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://autologon.microsoftazuread-sso.com/dewi.onmicrosoft.com/winauth/trust/2005/usernamemixed?client-request-id=30cad7ca-797c-4dba-81f6-8b01f6371013</a:To>
    <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
      <u:Timestamp u:Id="_0">
        <u:Created>2019-01-02T14:30:02.068Z</u:Created>
        <u:Expires>2019-01-02T14:40:02.068Z</u:Expires>
      </u:Timestamp>
      <o:UsernameToken u:Id="uuid-ec4527b8-bbb0-4cbb-88cf-abe27fe60977">
        <o:Username>DefinedLater</o:Username>
        <o:Password>DefinedLater</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <trust:RequestSecurityToken xmlns:trust="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
        <a:EndpointReference>
          <a:Address>urn:federation:MicrosoftOnline</a:Address>
        </a:EndpointReference>
      </wsp:AppliesTo>
      <trust:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</trust:KeyType>
      <trust:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</trust:RequestType>
    </trust:RequestSecurityToken>
  </s:Body>
</s:Envelope>
'
[string]$UsernameToken  = [System.Guid]::NewGuid().guid
[string]$messageId = "urn:uuid:" + ([System.Guid]::NewGuid().guid)
 $data.Envelope.Header.Security.UsernameToken.Id =$UsernameToken
 $data.Envelope.Header.Security.UsernameToken.Username = $username
 $data.Envelope.Header.Security.UsernameToken.Password = $password
 $data.Envelope.Header.MessageID = $messageId
 $data.Envelope.Header.To.'#text'= $uri2
$req = Invoke-RestMethod -UseBasicParsing -Uri $uri2 -Method Post -Headers $headers -Body  $data -ContentType "application/soap+xml; charset=utf-8" -UserAgent $userAgent 
$samltoken = $req.Envelope.Body.RequestSecurityTokenResponse.RequestedSecurityToken.Assertion.DesktopSsoToken
$token ='<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"><DesktopSsoToken>SAMLSSO</DesktopSsoToken></saml:Assertion>' -replace "SAMLSSO", $samltoken
$bytes = [System.Text.Encoding]::ASCII.GetBytes($token)
$base64 = [System.Convert]::ToBase64String($bytes);$base64
$uri3 = "https://login.microsoftonline.com/common/oauth2/token"
$body =@{
    client_id="cb1056e2-e479-49de-ae31-7812af012ed8"
    resource="https://graph.microsoft.com"
    grant_type="urn:ietf:params:oauth:grant-type:saml1_1-bearer"
    assertion=$base64
    }
    
$req = Invoke-RestMethod -UseBasicParsing -Uri $uri3 -Method Post -Headers $headers -ContentType "application/x-www-form-urlencoded" -Body $body
$headers = @{
    "Authorization" = ($req.token_type) +" "+ ($req.access_token)
    }
    
    $me = Invoke-RestMethod -Uri ($body.resource + "/v1.0/me") -Method Get -Headers $headers; $me
    

Azure AD App Proxy|Forward incoming JWT token to backend service: What are my choices?

Currently there is feature request in feedback.azure.com for getting JWT Tokens forwarded into the back end.

https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/32386468-forward-incoming-jwt-token-to-backend-service

There are at least two scenarios for such request. I am taking some shortcuts here, and assuming that in most scenarios this is an access token, similar to the one that is issued and sent with Native Clients in Authorization: Bearer … header

  • Browser clients using XHR / Fetch
    • This seems to work ”out-of-the-box” as the browser session is ”authenticated” with session data held in the session persisting cookies
    • I am using an example where the back-end service supplies the client per client side chained fetch() requests with any Access Token. This token is then sent back to back-end and displayed in the back end. This is to prove, that Azure AD application Proxy doesn’t strip the bearer token form Authorization header
function getToken () {
    fetch('/refreshToken').then((response) => {
        response.json().then( (data) => {
        console.log(data['access_token'])
        var token = data['access_token']
        fetch('/caller',{
            headers: {
                'Authorization': 'Bearer ' + token
                // 'Content-Type': 'application/x-www-form-urlencoded',
              },
        }).then( (response) => {
        response.json().then((data2) => {
            console.log(data2)
                })
            })
        })
    })
}
Authorization header is contained, and can thus be received in the back end
  • Native clients outside of web view sending the Access Token destined for AppProxy itself
    • I am excluding native client using web view like scenario where a browser is ”conjured” in the app. In which case I’d assume that web view would behave similarly as the browser example (mostly?) and successfully send the token to the back-end
    • This doesn’t work (And per explanation in the feature request, that’s by design), but alternative ways are available, which I’ve previously explored in another post
There is no Authorization Header. I’ve added extra header for (Authorization2) for illustrative purposes