221 - Attacking OAuth, Citrix, and some P2O Drama
What happens when you don’t properly validate OAuth access tokens? Account takeovers.
Fundamentally, Salt Security found the same issue across three separate login implementations making use of OAuth2.0 implicit grant flows. These applications would accept the
access_token from the Identity Provider and imediately use that to determine the user/email associated with the token, the problem being that the applications would not verify who the token was issues to. By not ensuring that the tokens were issued to their own application an attacker able to obtain access tokens from another application could reuse them on these applications to take over that user’s account on one of the vulnerable applications.
Obtaining those access tokens is a bit of a stretch, but not impossibly so. The post documents the idea of simply using a legitimate website, having users register and then just reusing those tokens. This could also be a route to leverage a compromise of one website into a compromise of accounts on another application.
Of the three applications documented in this post, the first two Vidio and Bukalapak used the implicit grant by default so, but the third application Grammarly used an Authorization Code flow which would not be vulnerable by default as the received code has to be exchanged using the Client ID and Secret. However by bruteforcing the parameters of the page that received the token they found that providing the
access_token parameter instead of a
code parameter would result in the application using the
access_token as if it was an implicit flow.
The authors here focus on crafting the exploit for an already discovered vulnerability in Orthanc’s DICOM server. The vulnerability is a natural consequence of two features:
- Import of DICOM files.
- Exporting of DICOM files to arbitrary locations on the filesystem including overwriting existing files.
As importing does try to parse the DICOM file, it does need to be valid file which is the semi-controlled aspect of this attack. Fortunately for the researchers in this case the DICOM file specification starts with a 128-byte chunk of unspecified data meant to be used by other applications to attack application specific data to the DICOM file. After that chunk then you get the usual magic bytes
DICM that can be matched to determine the file type and a more structured file format.
The authors were able to use that 128 byte section to craft a valid configuration file inside those 128 bytes, ending with a null-byte so the C++ JSON library in use would stop processing and ignore the rest of the DICOM file. And then use a
/reset endpoint to restart the server and reload the configuration. This crafted configuration file would enable a normally disabled endpoint that would accept arbitrary Lua script to be executed.
While this vulnerability was in authenticated endpoints, they point out that there are instances running and exposed to the internet with default credentials.
We rarely talk about memory corruption on our bounty episodes, but this one its a good one to keep in mind. Its a problem with
snprintf the “secure”
sprintf function. It allows you to specify the maximum number of bytes to write so it won’t overflow the target buffer.
The primary “gotcha” with using
snprintf is that unlike the other
printf family of functions its return value is not the number of bytes written but the number of bytes that would have been written if the buffer was large enough.
So with that background in mind, we come to Citrix’s
ns_aaa_oauth_send_openid_config function which is used to handle requests to the OpenID Connect Discovery endpoint which responds with a JSON blob containing the identity provider’s (Citrix) OIDC configuration information, things like the various endpoints and issuer information. This endpoint generates the JSON using a
snprintf to write into a
0x20000 byte buffer, in order to dynamically determine the right hostname to use in URLs it reads the
Host header from the HTTP request and reflects it back into the configuration JSON.
It then passes that buffer along with the size (as reported by the return value of
snprintf) to be written out in thee response. body. An attacker who provides a malicious
Host header that is long enough to fill the buffer for the JSON will end up with a buffer that is only filled to the maximum length (
0x20000 bytes) but
snprintf will still return a larger value. When the buffer is written out in the response body, it treats this larger value as the size and reads out of bounds reading whatever is in adjacent memory. In this case the authors were able to leak session cookies that could then be used to authenticate with the application.