forked from Gerenios/AADInternals
-
Notifications
You must be signed in to change notification settings - Fork 1
/
CBA.ps1
276 lines (229 loc) · 9.93 KB
/
CBA.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Get's tokens using CBA
# May 24th 2022
function Get-AdminPortalAccessTokenUsingCBA
{
<#
.SYNOPSIS
Gets Access Tokens using CBA
.DESCRIPTION
Gets Access Tokens using Certificate Based Authentication (CBA). Returns tokens for Portal and Business Store.
Assumes that CN of the given certificate contains upn with domain name.
.Parameter PfxFileName
Name of the certificate file to be used
.Parameter PfxPassword
Password of the certificate file to be used
.Example
Get-AADIntAccessTokenForAADGraph
.Example
PS C:\>$tokens = Get-AADIntAdminPortalAccessTokenUsingCBA -PfxFileName .\my_cert.pfx -PfxPassword "my supersecret password"
Logged in as [email protected]
PS C:\>Read-AADIntAccesstoken $tokens[0] | Select aud,iss,appid,amr | fl
aud : https://portal.office.com/
iss : https://sts.windows.net/25dc721a-d37f-44ec-b8dc-cc5783e9ec56/
appid : 00000006-0000-0ff1-ce00-000000000000
amr : {rsa, mfa}
#>
[cmdletbinding()]
Param(
[Parameter(ParameterSetName="File",Mandatory=$True)]
[string]$PfxFileName,
[Parameter(ParameterSetName="File",Mandatory=$False)]
[string]$PfxPassword,
[Parameter(ParameterSetName="Certificate",Mandatory=$True)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
)
Process
{
if($Certificate -eq $null)
{
$Certificate = Load-Certificate -FileName $PfxFileName -Password $PfxPassword -Exportable
}
$TenantId = Get-TenantID -Domain $certificate.SubjectName.Name.Split("@")[1]
# Create a web session
$webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$webSession.Cookies.Add((New-Object System.Net.Cookie("x-portal-routekey", "wuk", "/", "admin.microsoft.com")))
# Invoke the first request to get redirect url
$response = Invoke-WebRequest -UseBasicParsing -Uri "https://admin.microsoft.com/login?ru=%2FAdminportal%2FHome%3F%26source%3Dapplauncher" -Method Get -WebSession $webSession -ErrorAction SilentlyContinue -MaximumRedirection 0
$url = $response.Headers.'Location'
# Get the login parameters and cookies with the certificate
$loginInfo = Get-LoginParametersUsingCBA -Url $url -TenantId $TenantId -Certificate $certificate -WebSession $webSession
# Send parameters to redirect_url
$response2 = Invoke-RestMethod -UseBasicParsing -Uri "https://admin.microsoft.com/landing" -Method Post -Body $loginInfo.parameters -MaximumRedirection 0 -ErrorAction SilentlyContinue -WebSession $webSession
# Return an array of access tokens
$retVal = @()
$retVal += Get-AccessTokenUsingAdminAPI -TokenType PortalAT -WebSession $webSession
$retVal += Get-AccessTokenUsingAdminAPI -TokenType BusinessStoreAT -WebSession $webSession
return $retVal
}
}
# Get's tokens using CBA
# May 24th 2022
function Get-PortalAccessTokenUsingCBA
{
<#
.SYNOPSIS
Gets Access Tokens using CBA
.DESCRIPTION
Gets Access Tokens using Certificate Based Authentication (CBA).
Returns tokens for Graph, Office search, Substrate, Loki, and Portal
Assumes that CN of the given certificate contains upn with domain name.
.Parameter PfxFileName
Name of the certificate file to be used
.Parameter PfxPassword
Password of the certificate file to be used
.Example
Get-AADIntAccessTokenForAADGraph
.Example
PS C:\>$tokens = Get-AADIntPortalAccessTokenUsingCBA -PfxFileName .\my_cert.pfx -PfxPassword "my supersecret password"
Logged in as [email protected]
PS C:\>Read-AADIntAccesstoken $tokens[0] | Select aud,iss,appid,amr | fl
aud : https://graph.microsoft.com
iss : https://sts.windows.net/25dc721a-d37f-44ec-b8dc-cc5783e9ec56/
appid : 4765445b-32c6-49b0-83e6-1d93765276ca
amr : {rsa, mfa}
#>
[cmdletbinding()]
Param(
[Parameter(ParameterSetName="File",Mandatory=$True)]
[string]$PfxFileName,
[Parameter(ParameterSetName="File",Mandatory=$False)]
[string]$PfxPassword,
[Parameter(ParameterSetName="Certificate",Mandatory=$True)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
)
Process
{
if($Certificate -eq $null)
{
$Certificate = Load-Certificate -FileName $PfxFileName -Password $PfxPassword -Exportable
}
$TenantId = Get-TenantID -Domain $certificate.SubjectName.Name.Split("@")[1]
# Create a web session
$webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
# Invoke the first request to get redirect url
$response = Invoke-WebRequest -UseBasicParsing -Uri "https://www.office.com/login?ru=%2F%3Ffrom%3DPortalHome" -Method Get -WebSession $webSession -ErrorAction SilentlyContinue -MaximumRedirection 0
$url = $response.Headers.'Location'
# Get the login parameters and cookies with the certificate
$loginInfo = Get-LoginParametersUsingCBA -Url $url -TenantId $TenantId -Certificate $certificate -WebSession $webSession
# Send parameters to redirect_url
$response2 = Invoke-RestMethod -UseBasicParsing -Uri "https://www.office.com/landingv2" -Method Post -Body $loginInfo.parameters -MaximumRedirection 1 -ErrorAction SilentlyContinue -WebSession $webSession
# Parse tokens from the html
$tokens = (Get-Substring -String $response2 -Start '<div id="primaryTokensInfo" style="display: none;">' -End "</div>").replace('"','"') | ConvertFrom-Json
# Return an array of access tokens
$retVal = @()
foreach($token in ($tokens | Get-Member -MemberType NoteProperty))
{
$value = $tokens | Select -ExpandProperty $token.Name
$retVal += $value.TokenValue
}
return $retVal
}
}
function Get-LoginParametersUsingCBA
{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$True)]
[String]$Url,
[Parameter(Mandatory=$True)]
[String]$TenantId,
[Parameter(Mandatory=$True)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
[Parameter(Mandatory=$True)]
[Microsoft.PowerShell.Commands.WebRequestSession]$WebSession
)
Process
{
function Get-Config
{
Param(
[Parameter(Mandatory=$True)]
[String]$Content
)
Process
{
$strConfig = Get-Substring -String $Content -Start '$Config=' -End ";`n"
if([string]::IsNullOrEmpty($strConfig))
{
Throw "Could not parse config"
}
try
{
return $strConfig | ConvertFrom-Json
}
catch
{
Throw "Coud not parse config"
}
}
}
$nonce = (New-Guid).ToString()
# Make an initial request to login.microsoftonline.com to get required tokens and config
$response1 = Invoke-WebRequest -UseBasicParsing -Uri $Url -Method Get -WebSession $WebSession -ErrorAction SilentlyContinue
# Extract the config
$config = Get-Config -Content $response1.Content
# Make request to https://certauth.login.microsoftonline.com/<tenantid>/certauth
$body = @{
"ctx"=$config.sCtx
"flowToken"=$config.sFT
}
$response2 = Invoke-RestMethod -UseBasicParsing -Uri "https://certauth.login.microsoftonline.com/$TenantId/certauth" -Method Post -Body $body -Certificate $Certificate
# Parse the hidden form fields
$parameters = @{}
foreach($e in $response2.html.body.form.input)
{
$parameters[$e.name]=$e.value
}
# Make the final request to login.microsoftonline.com to get cookies
$response3 = Invoke-WebRequest -UseBasicParsing -Uri "https://login.microsoftonline.com/common/login" -Method Post -Headers @{"Referer"="https://certauth.login.microsoftonline.com/"} -Body $parameters
# Parse the config
$config = Get-Config -Content $response3.content
if(-not [string]::IsNullOrEmpty($config.strMainMessage))
{
Throw $config.strServiceExceptionMessage
}
# Get the cookies
foreach($cookie in $response3.Headers.'Set-Cookie'.Split(","))
{
$parts = $cookie.Split(";")[0].Split("=")
switch($parts[0])
{
"ESTSAUTH" { $estsauth = $parts[1] }
"ESTSAUTHLIGHT" { $estsauthlight = $parts[1] }
}
}
Write-Host "Logged in as $($config.sPOST_Username)" -ForegroundColor Green
# Make a request to login.microsoftonline.com/kmsi to get code and id_token
$body = @{
"LoginOptions" = "3"
"type" = "28"
"ctx" = $config.sCtx
"hpgrequestid" = $config.sessionId
"flowToken" = $config.sFT
"canary" = $config.canary
"i19" = "2326"
}
$response4 = Invoke-RestMethod -UseBasicParsing -Uri "https://login.microsoftonline.com/kmsi" -Method Post -WebSession $WebSession -Body $body
# Parse the hidden form fields
$parameters = @{}
foreach($e in $response4.html.body.form.input)
{
$parameters[$e.name]=$e.value
}
if(-not $parameters.ContainsKey("code"))
{
$config = Get-Config -Content $response4
if(-not [string]::IsNullOrEmpty($config.strMainMessage))
{
Throw $config.strServiceExceptionMessage
}
Throw "Could not get authorization code!"
}
# Return
return New-Object psobject -Property @{
"parameters" = $parameters
"ESTSAUTH" = $estsauth
}
}
}