Lab 2 - Authentication Attacks and Defense
In this Lab, you will explore common authentication vulnerabilities. APIs often use “API Keys” or various “Authentication Tokens” to authenticate the user. API keys are static secrets that are often difficult to change if they should leak. More sophisticated authentication tokens have a limited lifetime and must be renewed periodically. This rotation reduces the risk of the token being leaked. In our case, we use “OIDC” (Open ID Connect). OIDC uses a digitally signed token issued by an authentication server.
We use Keycloak, an open-source authentication server supporting several protocols and token formats. Our API gateway, Kong, is used to verify the token. The API gateway only allows authenticated users to access the API but does not restrict which API the user may access.
This exercise consists of three parts:
-
You will explore how Keycloak and Kong interoperate. In this exercise, you focus on what should happen in a properly configured application.
-
The second part will use a buggy API gateway. This API gateway suffers from “Algorithm Confusion”, a vulnerability attackers may use to create fake authentication tokens.
-
Finally, you will explore API keys and how they could be leaked.
Time allocation
20 minutes
Access to the environment
Please launch your browser and visit the URL http://www.sansapi.com/. You should use the full hostname, including the “www” prefix. Browsers do not always display the hostname's first label ('www').
Part 1: Explore the JWT Token
You have already registered an account in Lab 1. If not, please consult the instructions for Lab 1. Please login to the website using the credential you registered earlier if prompted.
Info
If you cannot remember your credentials from Lab 1, please register another user.
-
Copy the token displayed in the textbox. Make sure to copy the entire token. The token should start with the letters
ey
. Your token will look slightly different. Be extra diligent about this copy operation, make sure you are copying all of the text in the box. -
Open https://jwt.io in a new browser window or tab.
-
Paste the token into the text area labeled
Encoded
on the left side of the jwt.io debugger. You want to replace all existing text pre-populated in the text area. -
On the right-hand side, you will see the decoded token. The token consists of three parts: Header, Payload, and Signature.
The exact decoded token will look different. Each token is unique. Identify the following parts of the token:
Header
- “alg”: The algorithm used to sign the token. The algorithm should be “RS256”, a shorthand for “RSA using SHA256”. A SHA256 hash of the token was calculated and encrypted using a private key. The encrypted hash is used as a signature. To verify it, we need to know the public key, use it to decrypt the hash, and verify the hash.
- “typ”: The type of the token is “JWT” or “JSON Web Token”. The type defines the overall format of the token.
- “kid”: The key ID is used to identify the right key to verify the token.
Body
- “exp”: The expiration time as the number of seconds since January 1st, 1970, the Unix “epoch.” Hover your cursor above the timestamp to see the human-readable version of the date.
- “iat”: The issued time (issued at).
- “auth_time”: When the user authenticated to the authentication server.
- “jti”: A unique ID. This ID is used to prevent replaying tokens.
- “iss”: The issuer’s URL.
- “aud”: The “Audience”. The application must verify that the token was created for this application (=audience).
- “sub”: A unique “Subscriber ID.” Our authentication server assigns a UUID to each user.
- “typ”: This should be “Bearer.” We will use the token as a “Bearer Token.” Bearer tokens are transmitted using the authentication header.
- “azp”: The client ID assigned to the client.
- “nonce”: A random string to prevent reusing the signature.
- “session_state”: A session ID tracked by Keycloak.
- “acr”: Authentication Context Class Reference. We are not using this here. It can be used to certify which authentication method was used. For example, to identify users with multi-factor authentication.
- “allowed-origins”: Identifies which origins (websites) may use this token.
- “realm_access”: These are different roles associated with the user for this particular realm.
- “resource_access”: What resources does the user have access to.
- “scope”: The scope of the token.
The remainder of the token contains information about the user, like email address and name.
Summary
RFC 7519 defines JSON Web Tokens (JWT) as "a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted." (https://datatracker.ietf.org/doc/html/rfc7519)
Part 2: Create and use a fake token
-
The advantage of an asymmetric encryption scheme like RSA is that the key used to create the token can be kept secret. However, note how the algorithm is part of the header, and the user may change it. Change the algorithm to
HS256
(HMAC SHA256). HS256 is a symmetric encryption algorithm. The same key is used to create and verify the signature. -
We will use the public RSA key to create the signature. Unlike Kong, our “Bad Gateway” at badgw.sansapi.com is vulnerable to algorithm confusion. It will see the symmetric algorithm and use the key for RSA to decrypt it. Finding the key is a bit tricky. Start by opening in another browser tab https://auth.sansapi.com/realms/Animalshelter/protocol/openid-connect/certs. The “Set JWT” page will have a link to make it easier. Firefox will automatically "Pretty Print" the output.
In Chrome, you have a "Pretty-print" option to make it easier to read the output. You should see the different parameters defining the keys used by this instance of Keycloak:
* kid: The Key ID
* kty: The Key Type (RSA in this case)
* alg: The algorithm the key should be used for. In our case, we do have two options. First, "RSA-OAEP" and second, "RS256".
* use: Allowed uses for the key. In our case, this is "sig" for signature for the RS256 key and "enc" for encryption for the "RSA-OAEP" key.
* n: The key "modulus". We will use this as our symmetric encryption key for the buggy proxy.
* e: the public exponent.
* x5c: The X.509 certificate chain.
* x5t: The certificate SHA1 thumbprint.
* x5t#S256: The SHA256 thumbprint.
Chrome browser below,
Firefox browser below,
-
The page contains information about the keys used by the authentication server, including the public key used for RSA SHA256 signatures. Find the string
“alg”:”RS256”,”use”:”sig”
This identifies the Key used to create RSA SHA256 signatures. Following this string, you will seen
followed by the key. For example:"n":"xG30....Mkw"
Copy the key (everything from "xG30" to "Mkw" in the example above). The key will likely span at least two or more lines depending on the width of your screen and font size. Please double check you are NOT copying the beginning and ending double quotes. -
Go back to https://jwt.io. Paste the key into the
your-256-bit-secret
field in the Signature block; at the bottom of the selection screen. This will update the signature. -
Optional: Increase the
exp
(Expiration) value of the token. We are now able to alter the token at will. Extending the expiration time will make it easier to complete the exercise before the token expires. - Paste the new token into in the text area at https://www.sansapi.com/setjwt.html and click
Update JWT
. - Click on
Test Token
at the top right hand corner of the webpage, which will direct you to https://www.sansapi.com/faketokentest.html. The text area on the page should display the output of the/animal
API, proofing that the token worked to authenticate.
Summary
We've exploited the algorithm confusion vulnerability in OIDC authentication tokens. By using an existing token as a template, we created a modified version that allowed us to impersonate other users. This tampering could enable unauthorized access to restricted parts of the API.
Feel free to experiment. Using the Edit and Resend
feature of Firefox or Postman, you can adjust the token and send the new token to various API endpoints. Copy the request from the browser and try to change the API. Maybe experiment with altering other fields in the token, and use it against the https://badgw.sansapi.com/api/user/1
URL to retrieve data from other users. See if you can impersonate other users. Note that only the “bad” proxy at badgw.sansapi.om will accept the fake tokens. The default proxy, at api.sansapi.com, is not vulnerable and will not accept the bad token (feel free to experiment with it).
Part 3: API Keys (Optional)
Next, we will investigate API keys. API keys are much simpler than tokens. They are opaque secrets. Often, they are issued without expiration time, and they are verified using a common database. In our case, each user is issued a unique API key. This API key can be used to access any data the user is authorized to access.
-
Go to the “User Info” https://www.sansapi.com/userinfo.html page. This page will display the account information of the user currently logged in.
-
The default “User Info” page uses the OIDC token to authenticate the user. You may want to experiment with the “fake token” from the prior exercise.
-
The “User Info” page lists the user’s API key. We kept it simple and short. It should be just six letters and or numbers. Note the API key.
-
The API key is stored in the browser’s Local Storage. Select the “Application” tab in the Browser’s developer tools, and inspect the API key. If Developer Tools are not already launched, use
Ctrl + Shift + I
orF12
on Windows and Linux. On macOS, useCmd + Opt + I
orF12
.Chrome browser below,
Firefox browser below,
-
Click on the “API Key” link at the top of the page. This will direct you to https://www.sansapi.com/userinfoapikey.html. The borders of this page are red.
-
In the Developer Tools, select the “Network” tab and inspect the last request. The URL in the “Name” column (Chrome)/"File" column in Firefox should consist of a number, followed by a question mark and apikey=[your API key].
-
Right-click on the URL, and “Open in new tab”. This will display the raw JSON data returned from the API. (Firefox menu shows the "Open in new tab" option at a lower spot)
-
Our web server leaks some log data. Goto https://bypass.sansapi.com/logs. This URL returns JSON formatted logs like:
[{"accesstime":null,"url":"https://bypass.sansapi.com/user-apikey/14?apikey=abc123"}
You should be able to extract other user’s API keys from the logs and reuse them. You may also "social engineer" your neighbors or nicely ask them for their API keys for this experiment. -
The logs may also show the abuse of API keys. Try to identify users who are using the wrong API key. The user ID in the URL should match the user ID associated with the API key. Can you find attackers?
https://bypass.sansapi.com/user-apikey/[userid]?apikey=[apikey]
- Knowing how an attack may be detected, send a request that can not be detected by matching the user ID and API key.