Today I come to you with a new idea. What if we could bring XSS payloads into third-party applications without attacking them directly? Would be a nice approach, right? Let’s call it XSS over IdP Federation.
Oh gosh, what exactly are we doing here? In today’s cloud world, many services are federated with an Identity Provider (IdP). This means that applications pass authentication to an IdP, such as Azure AD, and trust it. If we take a registration process as an example, the target application receives a token from the IdP after the user has successfully logged in with his IdP. From this token, the application can read, for example, the email address, first and last name, or display name and use this data to create an account on its own system, if necessary. And it is this foundation of trust that we are now attacking π But first, let’s get started by looking at the following diagram.
As you can see, we are going to create a user in our local Active Directory. This user will have the name “Johnny Attack” and the display name will be the XSS payload. This will be synchronized in a first step in our Azure Active Directory and in a second and third step the payload will be passed through the federation in the registration process to the target application. Let’s get started right away.
Active Directory
The first thing we need is our user. In principle, this step could be skipped, as the user could be created directly in Azure AD. However, we want to depict a real-world scenario, and Azure AD Connect is active in most environments. We already fill the First name, Last name and Display name with our XSS polyglot. We use the polyglot from “www.openbugbounty.org“. Of course you can use your own here, this is just for demonstration purposes.
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
Azure AD
To avoid having to wait for the synchronization, we initiate it with the following PowerShell command and then see in Azure AD that the user has been created. We also see that the Azure portal is not vulnerable to our payload, that would have been the jackpot π
$syncstate= (Get-ADSyncScheduler).SyncCycleInProgress
if ($syncstate-like "false") {
Start-ADSyncSyncCycle -PolicyType Delta
}
Target Service
Now that we have everything in place, we are ready to attack the service. To do this, however, we need to take a quick digression into how OAuth 2.0 and its implicit grant flow works. More details can be found on the Microsoft website. I will only touch on the topic superficially, referring to our use case. First, when the user clicks the register button on the service website, he/she is redirected to the “authorize” endpoint from Microsoft. The URL must be specially prepared and the absolute minimum looks like this:
https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/authorize?
client_id={appID}
&response_type=id_token
&redirect_uri=https%3A%2F%2Fplayground.collfuse.com%2Fidpxss%2F
&scope=openid%20profile
&nonce=idpxss
&response_mode=form_post
- client_id The application ID of the azure app
- response_type What kind of token we want
- redirect_uri Where you want to be redirected
- scope For ID tokens, this must be openid
- nonce A value contained in the token for identification
- response_mode How the token is transmitted (POST)
For our example, as you can see, we are redirecting to https://playground.collfuse.com/idpxss/, which is our test service.
https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/authorize?client_id={appID}&response_type=id_token&redirect_uri=https%3A%2F%2Fplayground.collfuse.com%2Fidpxss%2F&scope=openid%20profile&nonce=idpxss&response_mode=form_post
When a user calls or is redirected to this URL, they must authenticate to Azure AD. Typically with username and password, but also with MFA if necessary. Once this is successful, the IdP makes a POST request with the token to the redirect URL provided. If we look at this token, how to do that is written here “what about: JWT“, we see that the XSS payload is being transmitted in the claim “name” inside the token.
Next we build our application (https://playground.collfuse.com/idpxss/) so, that it reads the values from the token and displays them on the website. In this example, it acts as a service that blindly trusts the IdP. Of course, it would still need to verify the token, but we will leave that out completely as it is not part of this blog.
<?php
if (!isset($_POST['id_token'])) {
header("Location: {redirect_url}");
die();
}
$jwt = ( explode( '.',($_POST['id_token'])));
$jwt_payload = json_decode(base64_decode($jwt[1]), true);
$lastname = $jwt_payload['family_name'];
$firstname = $jwt_payload['given_name'];
$dispayname = $jwt_payload['name'];
?>
<html>
<head> </head>
<body>
<div> Hello <?php echo $firstname; ?> <?php echo $lastname; ?> </div>
<div> XSS Payload <?php echo $dispayname; ?> </div>
</body>
</html>
Yes, I know the application is quick and dirty, but it should help only for demonstration purposes, so please no bad words. Now if we play through this registration process, you know what? see for yourself π
As you can see our beautiful payload triggers, gorgeous!
Thatβs it so far, stay tuned and see you soon!
** midjourney string βsecure data connectionβ