Sunday 24 July 2011

Error-Based SQL Injection in Oracle 11g

Hello all,

So, I was in the following situation the last couple of days after quite some time:

A friend of mine from the past called in a favor and pinged me for a get together over the weekend and some help in exploiting a SQL injection vulnerability he found. Plus he was buying pizza so...

The SQL injection was in the Login Form of a web application he was testing. After checking the year on the clock (twice) to see if I stepped in a time warp and found myself in 2001 again, we got together and  set off to produce a POC that would show the effects and risks of this vulnerability.

So, to move into the more interesting bits of the post, inserting a single quote in the username field yielded an "ORA-01756: quoted string not properly terminated". 

The logic behind the form was such that the SQL injection could not be used to bypass the authentication ala 1=1-- fashion and exfiltration via blind SQL injection was not possible due to the fact that both 1=1 and 1=0 where returning identical pages on the browser.  Timing attacks, as much as I like them, can be rather unreliable and experience has shown that they are not held to the same value when presented back to the interesting parties.

Since all error data were returned to the user browser I set of to exploit it via the typical  utl_inaddr.get_host_address such as the following:
' or 1=utl_inaddr.get_host_address((select banner from v$version where rownum=1))--

This failed with an "ORA-24247: network access denied by access control list (ACL)" . Since this was a new error for me I googled it to see what was the problem with my SQL query.

What I found was not amusing, there was not a problem with my query but with the database itself. Apparently in Oracle 11g someone actually thought it would be a good idea to revoke all network communication functions from any user other than SYS. This applies to the following packages:

  • UTL_TCP

  • UTL_SMTP

  • UTL_MAIL

  • UTL_HTTP

  • UTL_INADDR


as well as - by inheritance - to any other package/procedure/function that directly utilises any of the aforementioned packages. More info on that can be found in http://www.dba-oracle.com/t_ora_24247_network_access_denied_by_access_control_list_tips.htm and  http://www.oracle.com/technetwork/articles/sql/11g-security-100258.html . This enhancement also killed the UTL_HTTP.REQUEST out-of-bounds data retrieval and its derivatives such as HTTPURITYPE.

Researching more into the specifics of Oracle 11g lead me to the http://2009.confidence.org.pl/materialy/prezentacje/alexander_kornbrust_confidence_2009.pdf presentation given by Alexander Kornbrust of Red-Database-Security GmbH, however both of the functions he proposes using for returning user controlled output in the body of the error string, namely CTXSYS.DRITHSX.SN & ORDSYS.ORD_DICOM.GETMAPPINGXPATH were either not present on the database or my user had not execute permissions on them.

In a different section of the aforementioned presentation - the one for mass data exfiltration - I found another function that took a user supplied query as an argument and I thought I should have a go on that to see what I could achieve. This function was DBMS_XMLGEN.GETXML (more info on that here http://psoug.org/reference/dbms_xmlgen.html). Two minutes on my test Oracle 11g yielded the following query
select dbms_xmlgen.getxml('select "a" from sys.dual') from sys.dual;

which produced the following error "ORA-00904 "a": invalid identifier". "a" was something I could control in the following manner
select dbms_xmlgen.getxml('select "'||(select user from sys.dual)||'" from sys.dual') from sys.dual;

which will return the name of the user being used to connect to the database as the invalid identifier in the ORA-00904 error in the following manner "ORA-00904 "SCOTT": invalid identifier".

A quick check  - and troubleshooting as to the returned type of the function and the one being compared to ( "ORA-00932: inconsistent datatypes: expected - got CLOB" ) - showed that dbms_xmlgen.getxml was both available and executable by my user in the application I was testing. So the following was the final valid SQL injection string that allowed me to create a suitable POC for the app.
t' or '1'=to_char(select dbms_xmlgen.getxml('select "'||(select user from sys.dual)||'" from sys.dual') from sys.dual)--

I am assuming this is an issue to any function/procedure that can use a user supplied string as a part of a query and not specific to the aforementioned function. The real task is finding an accessible function to the user in hand. I must note that there is caveat to using the aforementioned method, the included in the double quotes "" as identifier has to be 30 characters or less or you are hit with the following error "ORA-00972 identifier is too long" .

./Z

BTW, if anyone knows of any other functions that can be used to return user controlled input  to a forced error message, please include them in the comments along with the default permissions set by the database in terms of execution rights.