SQL注入学习总结
前言
本文是作者由CSDN博客迁移而来,原文地址:https://blog.csdn.net/2302_81178149/article/details/142738173?spm=1001.2014.3001.5501
我们下面进行大部分Web手接触的第一个漏洞——SQL注入的学习,SQL注入是owasp top10之一,是Web安全常见漏洞。
为探讨清楚SQL注入的诸多细节,我们特选经典的sqli-labs进行从入门到进阶的强化训练。而本文是经由sqli-labs训练总结而来,因此配合作者的sqli-labs通关全详解食用更佳(怎么可以光学习不训练呢)。
作者的通关文章:sqli-labs通关全详解-CSDN博客
SQL基础知识
参考课程:
SQL的核心基础语法 | 快速入门MySQL_哔哩哔哩_bilibili(下图取自该课)
Web结构与MySQL数据库
Web结构:前端、后端、数据库(也有可能前后端不分离),而sql注入发生在与数据库交互时。
MySQL层次(由大到小):服务器、数据库、表格、表字段(列名)、用户数据
下面我们提到一个sql注入必需要学会的的一个数据库,也就是MySQL自带的系统库,里面存储了服务器内所有库的信息。
MySQL系统库信息:
1、information_schema 库(必学,下面三个表都要搞透澈):是信息数据库,其中保存着关于 MySQL 服务器维护的所有其他数据库的信息,比如数据库名、数据库表、表字段的数据类型与访问权限等。Web 渗透过程中用途很大。
1)SCHEMATA 表:提供了当前 MySQL 实例中所有的数据库信息,show databases 结果取之此表
2)TABLES 表:提供了关于数据中表的信息
3)COLUMNS 表:提供了表中的列信息,详细描述了某张表的所有列以及每个列的信息。
2、mysql 库(仅了解):MySQL 的核心数据库,主要负责存储数据库的用户、权限设置、关键字等 mysql 自己需要使用的控制和管理信息。
3、performance_schema 库(仅了解):内存数据库,数据放在内存中直接操作的数据库。相对于磁盘,内存的数据读写速度要高出几个数量级,将数据保存在内存中相比从磁盘上访问能够极大地提高应用的性能。
4、sys 库(仅了解):通过这个数据库数据库,可以查询谁使用了最多的资源(基于IP或是用户)。哪张表被访问过最多等等信息。
MySQL基础语法
(不用很精通,会增删查改即可,sql注入会有一些特需的语法,都已列在下文“注入语法详解“模块)
1 |
|
SQL注入专项知识
注入常识
一、什么是注入点
实行注入的地方
二、闭合的作用
提交闭合符号,可以结束前一段查询语句,后面就可以加入其他SQL语句,查询我们需要的参数
三、注释
不需要的语句可以用注释符号’– ’或‘#’注释掉,利用注释符号暂时将程序段脱离运行(若在url中注入,需要进行URL转义,--
转义为 --+’
,#’
转义为 %23’
)。
简单来说,把某段程序“注释掉”,就是让它暂时不运行(而非删除掉)
四、如何判断是否存在注入漏洞
1、输入单引号
输入单引号,页面报错,You have an error in your SQL syntax; ……(翻译为有SQL语法错误),那么基本存在注入漏洞
2、输入双引号
输入双引号,有时用双引号闭合时,输入单引号不会报错,需要输入双引号
3、输入其他闭合符号(形式多样)
根据不同的闭合,判断是否存在注入漏洞的方法也是不一样的,比如有时要输入’))或者”),这时进行判断需要多次尝试,这个过程往往会和确定闭合符号步骤有部分重合。
**注意:凡是与数据库交互的地方,都有可能发生SQL注入。**所以我们需要测试的地方包括但不限于:
输入框(包括搜索框、评论框等),
传递的get、post参数(建议直接抓包找),
Referer、User-Agent、Cookie等http头传参。
我们必须认真全面地对上面各处进行仔细的测试,借以找到开发及维护人员的疏忽所在。
五、注入的分类
1、按照查询字段:
数字型:
当输入的参数为整型则为数字型注入
字符型:
当输入参数为字符串则为字符型注入
2、按照注入方式:
union注入、报错注入、布尔注入、时间注入、堆叠注入等
3、按照请求方式:
常见的有GET型注入与POST型注入,还有header注入、Cookie注入、Referer注入……。
GET型注入直接在URL上直接注入就可以(这就是GET传参方式),POST型有时可以直接在登录页面注入(注意是登录页面不是URL),但往往需要借助burpsuite、hackbar、postman或sqlmap等工具(具体使用方法见下文)
POST型注入、header注入、Cookie注入和Referer注入等的应对方法和get型差不多,主要是操作位置有所不同,然后各自有一点点独特的注入技巧。
六、怎么判断是数字型注入还是字符型
(实际做题不需要我们刻板的先去判断是什么类型的注入,但我们发现这一步骤却往往是必不可少的,因为这一步实际上是判断闭合方式)
方法一:id=1 and 1=2;
若成功则是字符型,失败则是数字型。
(解释:数字型对比数值,1!=2,报错;字符型用引号将数字、符号等包裹,视为字符串,and不会执行,就不会报错。但并不绝对,这一方法有时也不可靠)
方法二:id=2-1;
如果可以运算则是数字型,如果不能运算则为字符型
方法三:id=1‘
通过报错信息查看注入类型。输入的内容直接放入数据库里就是数字型注入,此时报错就是多了一个单引号‘‘’
(我们这里用了3个单引号,因为1已经放入SQL语句里,单引号单独出来用以报错,注意报错时外面往往会再套一对单引号用来标注,所以共三个单引号);但若报错是直接指出了我们整个的输入:1‘
,加上数据库里已有的一对单引号和用来标注的一对单引号,表示为‘’1‘‘’
,就是字符型注入。
方法四:当没有报错时,我们如何确定闭合方式?不断尝试,当发现使用某种闭合时,使下面这种情况成立,则该闭合正确:
闭合后不加注释无回显(假如有报错,此时应该是报错,但这里没报错,所以没有回显);而加注释后有回显(注释成功代替掉后面的闭合,我们自己加的符号完美补上闭合,会产生我们想要的回显)
注入语法详解
(可以用到的时候再来了解一下,不需要先学完再继续)
1、limit分页
limit n,m
:从第n+1条往后取m条
例如,limit 8,9
:从第9条数据往后取9条数据。
2、count(*)
统计数量,如果结合group by 就是统计分组的数量。
3、合并函数:concat()、concat_ws()、group_concat(arg)
concat()函数
用于将多个字符串拼接到一起。MySQL的 CONCAT 函数是一个非常实用的字符串函数,用于将两个或多个字符串参数连接成一个单一的字符串。如果任何一个参数为 NULL,则 CONCAT 函数的结果也会是 NULL。这一点在构建包含潜在NULL值的数据库查询时特别重要,因为它可能影响到你的查询结果。
基本语法:
1 |
|
参数string1, string2, …, stringN
:这些是要连接的字符串。你可以连接任意数量的字符串。
concat_ws()
用于将多个字符串拼接到一起,并在其之间插入指定的分隔符。
1 |
|
group_concat(arg)
函数
可以合并多行的某列(或多列)数据为一行,默认以逗号分隔。
例如,
1 |
|
有时会有分组函数和统计函数的组合使用,用于将一组字符串用指定的分隔符连接在一起,通常与group by 子句一起使用,以便在每个分组中连接字符串
1 |
|
通过使用distinct可以排除重复值
使用order by
子句对结果中的值进行排序
separator用于指定分隔符 ,如果缺少则默认为 逗号
注意:
group_concat
只有与group by语句同时使用才能产生效果。 所以使用GROUP_CONCAT()
函数必须对源数据进行分组,否则所有数据会被合并成一行。
4、mysql中相关语句的执行顺序
(上面的比下面的优先执行)
1 |
|
由上可知,group by 比order by先执行,order by不会对group by 内部进行排序,如果group by后只有一条记录,那么order by 将无效。
5、GROUP BY
分组
group by一般用于分组统计,它表达的逻辑就是根据一定的规则,进行分组。
6、order by
排序
ORDER BY
关键字用于对结果集进行「排序」。
ORDER BY
关键字可以按照列的「索引」进行排序,比如最左边第一列 username 的索引是 1,右边第二列 password列的索引是 2,依次类推…比如按照第1列排序(在SQL注入中可以确定列数,即注入点的字段数)
ORDER BY
关键字默认按照「升序」对返回的结果集进行排序。如果需要按照「降序」对记录进行排序,可以使用 DESC 关键字。
7、RAND()和RAND(x)
获取随机数的函数。
rand()
函数:随机返回01间的小数,rand()*2则随机返回02的小数
RAND(x)
返回一个随机浮点值v,范围在0到1之间(即0≤v≤1.0)。 若已指定一个整数参数x,则它被用作种子值,用来产生重复序列。
注:x与随机浮点值v不同,仅用于产生重复序列
8、floor()
函数
小数向下取整数。
floor(rand(0)*2)
返回0或1两个数值。
9、substring()与substr()
substring()
1 |
|
从字符串的第m位开始,取出n位。
例如,
1 |
|
效果为:
substr
:俗称字符截取函数
格式1:
1 |
|
str 需要截取的字符串
a 截取字符串的开始位置(注:当a等于1时,从第一位开始截取;当a等于0时,无截取字符串)
b 要截取的字符串的长度
格式2:
1 |
|
str 需要截取的字符串
a 可以理解为从第a个字符开始截取后面所有的字符串。
10、ASCII()
MySQL中ASCII() 函数的语法:
1 |
|
参数character:必需的。 要返回 ASCII 值的字符。 如果多于一个字符,它将只返回第一个字符的 ASCII 值。
返回值:MySQL ASCII()
函数返回给定的字符串的第一个字符的 ASCII 值。如果参数 character 为 NULL,它将返回 NULL。
11、if()
函数的使用
IF函数根据判断条件是否成立进行选择执行,成立时执行一条语句,不成立时执行另一条语句。
语法结构:
1 |
|
参数说明:
condition: 判断条件
value_if_true: 如果condition的结果为TRUE,返回该值
value_if_false: 如果condition的结果为FALSE,返回该值
12、sleep()
sleep(int m)
:延时m秒
13、LOAD_FILE()
函数
在mysql中,load\_file()
函数读取一个文件并将其内容作为字符串返回。
语法:load_file(file_name)
,其中file_name是文件的完整路径。
SQL语句为
1 |
|
效果如下(注意,文件路径符号必须使用斜线/,不可以使用反斜线\)
手动注入方式及步骤
UNION联合查询注入
一、原理
union select 是合并两个或多个表查询的结果集,如果我们将查询语句前面的表设为不存在的表,那么查询结果就返回了后续的表,充分利用可以回显我们的目标表格的内容。
如果前面的查询语句查询的是数值,我们可以通过设为-1等操作,使其不存在,从而只显示union后面我们操纵的内容
二、步骤
1、先判断有无漏洞,比如输入单引号(见上文)
2、判断是字符型注入还是数字型注入(即判断闭合方式)
3、判断字段数,并且确定各字段在回显的位置。用union select 1,2,3,……尝试,若有四个字段,在union select 1,2,3,4 时会显示成功;然后使union之前的语句错误,这样就可以看我们select出来的1,2,3,4分别在回显中的位置
4、在有回显的位置爆表名,例如
1 |
|
5、爆列名(字段名),例如
1 |
|
6、爆用户数据
1 |
|
报错型注入
一、定义及分类
报错注入是通过特殊函数错误使用并使其输出错误结果来获取信息的。是一种页面响应形式。报错注入用于有error回显,且能从报错中获得有用信息的情况。
分类(重点掌握前3种,其余了解即可)
- 通过floor()报错注入
- 通过extractValue()报错注入
- 通过updateXml()报错注入
- 通过NAME_CONST()报错注入
- 通过jion()报错注入
- 通过exp()报错注入
- 通过geometryCollection()报错注入
- 通过polygon()报错注入
- 通过multipoint()报错注入
- 通过multlinestring()报错注入
- 通过multpolygon()报错注入
- 通过linestring()报错注入
二、常用的报错注入(前三种重点掌握,剩下的了解即可)
1、通过 floor()报错注入:
通过注入语法详解我们已知,
1 |
|
下面我们看一下 floor()报错注入的例句,
1 |
|
在执行group by语句的时候,group by语句后面的字段会被运算两次。
第一次是group by后面的字段和虚拟表进行对比,第二次是插入时会进行运算。
由于rand()函数的随机性,导致第二次运算可能和第一运算结果不一致,运算的结果存在,这时插入就会出错。
其实就是rand()函数进行grand by分组时会多次执行,导致键值key重复
注意:用information_schema.tables
是因为这个数据库是一定已知存在的且内容多有足够的统计结果
2、通过extractValue()报错注入:
extractvalue()
函数:
1 |
|
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串),查询路径
注:查询参数路径写错时,查询不到内容,但不会报错;把查询参数格式符号写错时提示报错信息。
由上可知,当我们特意让查询出语法错误,会显示
XPATH syntax error : ’~XPath_string的内容’
我们可以把XPath_string作为注入点,显示出我们想要的信息,如下例
1 |
|
(0x7e是~的ASCII码)
然后,爆表名 ->爆列名->爆用户数据
(具体语句请查看sql-labs里的详例)
3、通过updateXml()报错注入:
extractvalue是查询,而updatexml是修改。但是用法和extractvalue是一样的。
updatexml函数:
UPDATEXML
1 |
|
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串) ,不满足Xpath格式的字符串都会产生报错。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
例句:
1 |
|
效果为
然后,爆表名 ->爆列名->爆用户数据
具体语句还是见sqli-labs中具体的例子
4、NAME_CONST()
1 |
|
5、 jion()
1 |
|
6、exp()
1 |
|
7、geometryCollection()
1 |
|
8、polygon()
1 |
|
9、multipoint()
1 |
|
10、multlinestring()
1 |
|
11、multpolygon()
1 |
|
12、linestring()
1 |
|
参考文章:
盲注
一、定义及分类
盲注就是在sql注入过程中,sql语句执行select之后,可能由于网站代码的限制或者apache等解析器配置了不回显数据,造成在select数据之后不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个判断的过程称之为盲注。
简单来说,就是回显的内容很少或没有回显(报错也是一种回显),不能通过回显看出有用的东西,就像盲了一样,所以叫盲注
所以我们总结出盲注的使用条件:
页面没有显示位(如果有显示位可以选择union联合查询),并且没有返回sql语句的执行错误信息。
只有在这种条件下我们才用盲注,因为盲注本质上就是不断地尝试,试出来我们想要的信息,所以盲注要发送大量的请求,对服务器占用较大,容易被禁止。盲注一般不被优先考虑,在不同情况下往往考虑有没有特殊方法解决,比如下文的DNS log注入。
分类
- 基于布尔类型的盲注
- 基于时间类型的盲注
在符合上文所说的条件下,我们怎么判断用哪种类型的盲注呢?
布尔盲注:网页返回的结果只有两种,输入的错误与正确,没有其他有效信息
时间盲注:页面上没有显示位和SQL语句执行的错误信息,正确执行和错误执行的返回界面一样
二、实现过程
从数据库名、表名到字段名,最后到用户数据,两种盲注都是不断重复‘提取数据长度’、‘提取数据内容’这两个步骤,过程非常繁琐,我们往往借助工具,比如sqlmap(使用方法见下文)或burpsuite
1、布尔盲注
以爆数据库名为例,先求出库名长度,
1 |
|
然后逐个字符确定,
1 |
|
也可以把substr((select database()),1,1)转换成ASCII码,
1 |
|
然后对照ASCII码表用数字依次尝试,也可以用>或<进行判断,这样就不需要逐个数字一一尝试,进一步可以使用二分查找,去减少请求次数
2、时间盲注
以数据库名为例,判断数据库长度:如果长度为1就延迟5s,不是就返回1。
1 |
|
有兴趣的话可以写一个python爬虫,通过判断响应时间来逐个确定字符,工具的原理大概就是这样。
当然,我们一般用sqlmap,用现成的工具会更方便一点。
但是要是对盲注只会用工具而没有其他办法,也是不行的,自己会写盲注的脚本才能算真的会盲注。
接下来我们再学习一些特殊的注入方式
DNS log注入
一、什么是DNSlog
虽然因特网上的节点都可以用IP地址标识,并且可以通过IP地址被访问,但即使是将32位的二进制IP地址写成4个0~255的十位数形式,也依然太长、太难记。因此,人们发明了域名(Domain Name),域名可将一个IP地址关联到一组有意义的字符上去。用户访问一个网站的时候,既可以输入该网站的IP地址,也可以输入其域名,对访问而言,两者是等价的。例如:微软公司的Web服务器的IP地址是207.46.230.229,其对应的域名是www.microsoft.com,不管用户在浏览器中输入的是207.46.230.229还是www.microsoft.com,都可以访问其Web网站。
简单来说,大家一开始用一串数字(ip地址)来表示网络上各个节点(网站),但这个太难记了,所以又给这些ip地址起了个别名(域名),域名与ip地址是等价的,只不过域名更好记。只使用域名虽然方便了我们,但实际上还需要把域名转换成ip地址再访问,所以我们引出DNS。
DNS:域名系统(英文:Domain Name System,缩写为DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。
简单来说,DNS就是一项将域名解析为ip的服务,各种域名与其对应的ip地址就存储在它的服务器上,用户在浏览器上输入一个域名A.com,就要靠DNS服务器将A.com解析到它的真实ip,这样就可以访问真实ip服务器上的相应服务。
那么DNSlog是什么呢?
log有日志的意思,DNSlog就是存储在DNS服务器上的域名信息,它记录着用户对各种域名的访问信息,类似日志文件,也有点像浏览记录。
为什么要进行DNSlog注入呢?
一般情况下,在我们无法通过联合查询直接获取数据的情况下,我们只能通过盲注,来一步步的获取数据,但是使用盲注去手工测试是需要花费大量的时间的,我们可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,会有很大的几率使得网站把我们IP给封掉,这就影响了我们的测试进度。
这种情况下用DNSlog注入就是必要的,下面我们进一步确定DNSlog注入的适用范围。
DNSlog注入通常用在哪些地方?
SQL注入中的盲注(布尔盲注、时间盲注),注入的效率低且线程高容易被waf拦截,往往采用DNSlog注入。
又或者是目标站点没有回显:无回显的命令执行,我们在读取文件、执行命令注入等操作时无法明显的确认是否利用成功;无回显的SSRF,也不能给出有效信息。
那怎么利用DNSlog进行注入呢?这得深入了解一下DNSlog。
二、DNSlog回显原理
因特网采用层次树状结构命名方法。域是名字空间中一个可被管理的划分(按机构组织划分),域可被划分为子域,子域可再被划分,即形成了顶级域名、二级域名、三级域名等。从右向左为顶级域名、二级域名、三级域名等,用点隔开。如:
tieba.baidu.com
它由三个标号组成, com即为顶级域名,baidu为二级域名,tieba即为三级域名。且域名不分区大小写。
我们简单看看下面图片,
(重点!!!)
域名从右向左解析,用 . 分割级别,低级别的域名要在高级别的域名中去解析,所以当tieba.baidu.com去baidu.com中去解析的时候,就会留下解析记录,通过获取前来解析的低级域名前缀来获取我们需要的信息。
所以我们首先需要一个可以自己配置的域名,比如:ceye.io,然后通过代理商设置域名ceye.io的nameserver为自己的服务器A,然后在服务器A上配置好DNS server,这样以来所有ceye.io及其子域名的查询都会到服务器A上,这时就能够实时地监控域名查询请求了。
(当然,这里也可以用免费的记录dnslog的平台来代替这些步骤,比如DNSLog Platform)
那么上面说的这些和我们这个DNS log注入有什么关系?DNS在解析的时候会留下记录,然后我们可以读取这个多级域名的解析日志来获取我们所想要的内容。
三、注意要点及踩坑记录
**1、dnslog回显只能用于windows系统。**sql盲注的后端数据库只能在windows系统中,原理就是’\\\\‘代表Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。双斜杠表示网络资源路径多加两个\就是转义了反斜杠。
为什么DNSlog注入只能用于windows系统?答:load_file()函数在Linux下是无法用来做DNSLog攻击的,因为linux没有UNC这个东西,所以当MySQL处于Linux系统中的时候,是不能使用这种方式外带数据的。
什么是UNC路径?
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。UNC 代表通用(或一致、统一)命名约定,是一种用于访问计算机网络上的文件夹和文件的语法。
语法如下:
\\<computer name>\<shared directory>\
路径结构可以后跟任意数量的目录,并以目录或文件名终止,例如:
\\pondermatic\public\studyarea.gdb
\\omni\shared_stuff\wednesday\tools
计算机名称的前面始终使用双反斜线 (\\)。
在 UNC 中,计算机名称又称为主机名称。
对于 UNC 路径,存在以下几条规则:
- UNC 路径不能包含盘符(如 D)。
- 不能浏览至共享目录的上级目录。
- 用于文档和工具的存储相对路径名选项对 UNC 路径不起作用。
2、需要用到mysql中的load_file()函数,在Mysql中,load_file()函数读取一个文件并将其内容作为字符串返回。
通过DNSlog盲注需要用的load_file()函数,所以一般得是root权限。
在mysql中运行:
1 |
|
查看load_file()可以读取的磁盘。
1)、当secure_file_priv为空,就可以读取磁盘的目录。(上方图片就是这种情况)
2)、当secure_file_priv为G:\,就可以读取G盘的文件。(其他盘也是如此)
3)、当secure_file_priv为null,load_file就不能加载文件。
如果secure_file_priv为null,就说明没有权限,不能用load_file()函数。下面就是如何修改权限。
若我们用的phpstudy,点击设置,点击文件位置,找到MySQL版本并点击
找到并点击my.ini文件
找找有没有secure_file_priv 参数,没有就加上
1 |
|
注意等号后面什么也没有,而且注意添加的位置(下图最后),添加位置不对没有效果(含泪大坑)。
重启并再次输入show variables like '%secure%';
(记住重启)
secure_file_priv为空而不是NULL,就表示成功了
3、域名解析需要注意:
由于每一级域名的长度只能为63个字符,所以在mysql中获取到超过63个字节的字符时,会被当作一个错误的域名,不会产生去解析的动作,所以tbk74h.dnslog.cn也不会收到解析的记录,所以我们就获取不到想要的信息了
域名里有一个规则,只能出现数字,字母,下划线;所以在获取到的信息中包含了其他特殊符号时,load_file就会认为是一个错误的域名,就不会去从网络中解析了。
如:
当前数据库名为security,是以字母开头的,在域名规则内是允许的,所以load_file会向域名进行解析
当在域名中拼接一个@符号时,就不会进行解析
也就不会有解析记录
我们在使用group_concat合并查询时,会自动使用 “,” 连接我们查询到的每值,但是由于 , 在url中是不允许出现的,所以使用group查询到的值去解析时候,mysql就会认为这不是一个url地址,就不会出现解析的操作,所以就没法获取到值
总结下来,就是域名长度和特殊符号的问题。
下面我们给出如何解决。
四、利用过程
接下来我们用DNSLog Platform 生成一个网址qnpqsu.dnslog.cn(点击Get SubDomain)
上面提到了各种与域名解析冲突的情况,尤其是group_concat()函数的逗号问题。
不过事情总是有解决的办法的,通过使用replace,substr等函数,成功绕过了url解析的问题,如:
1 |
|
通过正则替换将replace中的 “ ,”全部替换为 “ _ ” 这样就可以符合url的解析规则了(但要是username本来就有_,那对我们来说就是麻烦事了),并且我们只需要将查询结果的字符长度控制在63个就可以了。
因为load_file()函数访问的是文件,所以域名后面需要添加/abc,当然/aaa也可以,后面的文件名随便编一个就可以,\\aaa也是可以的,但\aaa不可以。
如图:
上面我们提到username如果本来就有_,那对我们来说又是麻烦事,有人说我们可以不用_,用别的符号,但username也有可能会用别的符号。
不过我们有别的方法。
那就是转换为16进制来避免字符过滤,推荐这种方法,它更全面且不易混淆符号
1 |
|
然后再用在线16进制字符串转换工具转换出来,比如在线16进制字符串转换工具 - 在线工具网
参考文献:
DNSlog注入详细解析 - FreeBuf网络安全行业门户
DNS log注入 - 简书(DNSlog注入踩坑记录)
二次注入
一、什么是二次注入?
简单的说,二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
网站对我们输入的一些重要的关键字进行了转义,但是这些我们构造的语句已经写进了数据库,可以在没有被转义的地方使用
可能每一次注入都不构成漏洞,但是如果一起用就可能造成注入。
普通注入
(1)在http后面构造语句,是立即直接生效的
(2)一次注入很容易被扫描工具扫描到
二次注入
(1) 先构造语句(有被转义字符的语句)
(2)我们构造的恶意语句存入数据库
(3)第二次构造语句(结合前面已经存入数据库的语句,成功。因为系统没有对已经存入数据库的数据做检查)
(4)二次注入更加难以被发现
二、二次排序注入思路
1、黑客通过构造数据的形式,在浏览器或者其他软件中提交 HTTP 数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的 SQL 语句或者命令。
2、服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。
3、黑客向服务端发送第二个与第一次不相同的请求数据信息。
4、服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的 SQL 语句或者命令在服务端环境中执行。
5、服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。
简单来说,就是我们提交了注入语句,比如注册用户(起了用户名,用户名是注入语句),可能是被过滤或特意设置,没发生效果,只是存到了数据库里,当我们在再次用这个语句时,比如更改用户密码(查询用户名),它从数据库里读取出来(这时候开发人员没有过滤注入语句,我们用户名里的注入语句执行了),虽然我们没重新写注入语句,但原来的注入语句在读取时发生效果了,注入最终还是成功了。
堆叠注入
一、什么是堆叠注入?
堆叠用简单通俗的话来解释就是多条命令一起执行,比如在MySQL中我们知道在输入一个命令之后要用;表示一个指令的输入完成,那么我们就想是否可以在一句指令之后用;断开然后再加上一句指令。按照这样多条命令进行注入就是堆叠注入。
二、堆叠注入原理:
在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在分号(;)结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。
用户输入:
1 |
|
服务器端生成的sql语句为:(因未对输入的参数进行过滤)
1 |
|
当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。所以堆叠注入更随意,威力更加巨大。
三、局限性:
并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎。
在 Web 中代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略。
使用堆叠注入前,我们还需要了解数据库的相关信息才可以,如表名、列名等,这个就是为什么我们尝试用 union select 联合查询的原因。
其实从实际来讲,堆叠注入最大的局限就在于它太少见了,由于堆叠注入威力过于巨大,开发者往往不会给用户开放多条语句的权限,从而使堆叠注入根本行不通。
四、利用:
和正常注入基本相同,区别在于可以多写几条语句,能进行删库等操作。
这里就不讲常规的select语句了,我们提一下不用select的(有select过滤时)。
先爆出数据库。
1 |
|
然后尝试爆表。
1 |
|
爆表
1 |
|
至于报数据,如果不利用select,也是有方法的。
比如HANDLER命令或者编码后预处理,这里简单提一下。
这里我们假设表名为words,然后我们想执行select * from words
我们payload为
1 |
|
解释如下,
1 |
|
这样就绕过select执行成功了。
然后我们详解一下HANDLER命令,
HANDLER
命令提供了一种比SELECT更直接的访问表数据的方式:
不需要完整的SQL解析
性能更高
但功能有限,主要用于顺序扫描表
常见HANDLER操作:
1 |
|
下面是举例payload
1 |
|
payload解释如下
第一个操作:handler `FlagHere` open as `a`
HANDLER
是MySQL特有的低级表访问接口语法:
HANDLER table_name OPEN [AS alias]
这里打开名为
FlagHere
的表,并赋予别名a
反引号(``)用于包裹表名,防止特殊字符引起问题
第二个操作:handler `a` read next
使用之前打开的handler别名
a
READ NEXT
从表中读取下一行数据这会返回表中的第一行内容(因为这是第一次调用)
混淆与绕过
普通注入方式过于明显,很容易被检测,因此需要改变攻击手法,绕过检测和过滤,即混淆和绕过。注意所有的技巧都是分情况的。
一、常见方法
1、编码绕过(URL编码)
空格可以URLcode为%20
。
常规代替空格的字符及其URL编码:
%09
TAB 键(水平);
%0a
新建一行;
%0b
TAB 键(垂直);
%0c
新的一页;
%0d
return 功能;
%a0
空格。
2、大小写转换绕过
union
变为uNion
。
但是所有大小写的情况都会被替换咋办?
3、双写绕过(包裹绕过)
UNIunionON
。
只适用于服务端做一次处理的情况,如果是递归过滤写多少次都没用。
还可以用注释/*!*/
包裹字符,例如/*!union*/
。
4、多个参数绕过
有时候开发人员由于疏忽,只会对第一个参数过滤,我们可以传递多个参数,在后面参数处写注入代码,例
id=1 & id=0' union select 1,2,3--+
5、近似替换
可代替where的字符:Limit;Having;like
除了上面常规代替空格的字符外,多行注释 /**/
也可以代替字符,甚至利用的好,括号()也可以代替空格。
and
变式&&
、or
变式||
。
有时候union
也可以被||
替换,相当于条件判断。
替换也常常全被过滤,并不是绝对有效。
注意:
有时候我们需要多种过滤结合运用,如/!UNIunionON/
二、其余绕过方法
1、宽字节注入
原理:
当某字符的大小为一个字节时,称其字符为窄字节;当某字符的大小为两个字节时,称其字符为宽字节。所有英文默认占一个字节,但汉字占两个字节。
宽字节是指一个字符占用两个字节的编码方式。常见的宽字节编码包括GB2312、GBK、GB18030、BIG5、Shift_JIS等。与单字节编码(如ASCII)相比,宽字节编码能够表示更多的字符,特别是中文、日文和韩文等语言中的复杂字符。
简单来说,宽字节就是用两个字节来表示更多的字符,特别是中文的汉字这么多,用单字节来表示是远远不够的。
如果使用了类似于 set names gbk 这样的语句,MySQL 在使用 GBK 编码的时候,mysql 数据库就会将 Ascii 大于等于128(比如%df)的字符当作是汉字字符的一部分(当作汉字处理),同时把两个字节认成一个汉字,例如 %aa%5c 就被认作是一个汉字;Ascii小于128还是单字节表示。
这种情况下如果我们想去掉sql语句中的一个字节,那么我们在想去的字节前加上一个Ascii 大于等于128(%df)的字节就行了。自己加的字节和想去掉的那个字节会被合起来解析成为汉字。或者把库名、表名等转换成16进制,从而省略掉单引号
2、上面提到的HANDLER命令以及编码后预处理
这两种方法都可在堆叠注入的情况下绕过select。
注入工具
sqlmap
sqlmap使用常用步骤:
进入sqlmap所在文件夹,在文件路径处输入cmd并回车(即在sqlmap路径下打开命令行)。
get型:
检测「注入点」
1 |
|
查看所有「数据库」
1 |
|
查看当前使用的数据库
1 |
|
查看「数据表」
1 |
|
查看「字段」
1 |
|
查看「数据」
1 |
|
post型:
需要用burpsuite抓包,写入txt文件,然后使用-r参数
检测「注入点」
1 |
|
查看所有「数据库」
1 |
|
(后续步骤依次类推)
参考文献:
【SQL注入】Sqlmap使用指南(手把手保姆版)持续更新_sqlmap使用教程-CSDN博客
sqlmap官方使用指南:Usage · sqlmapproject/sqlmap Wiki · GitHub
上面文章讲的都很好,在此不多作赘述,
第一个文章讲了在windows下安装sqlmap的教程
第二个文章可以作为入门的尝试,学习基础功能;
第三个文章有更多的参数,可以进一步学习;
官方文档可以作为查询工具,用来入门的话太过枯燥
Hackbar
请见:
burpsuite
可以看上面链接简单了解,具体用法建议在实际过程中学习,请移步至作者的sqli-labs通关。
Postman
与hackbar功能相近,用法相似,会一种即可。