sql注入那些事儿——如何优雅地进行SQL注入(1)

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片发自简书App

sql注入相信童鞋们都有所耳闻吧?不少童鞋应该都已经亲自动手尝试过了吧?或用工具(sqlmap之类的),或手工注入。虽然目前sql注入的漏洞真的非常非常不好找,各种网站搭建都基于较为成熟的框架,框架提供了底层的关键词过滤或转换,也有提供参数化查询和存储过程,这些方式从根本上杜绝了sql注入的可能。
之前听过一句话,sql注入攻击就像把门踹开,如何从屋里拿东西一样。我觉得还是有一定道理的,但是这并不是说sql注入真的简单到完全不用防护,事实上,直到现在为止,都还存在一定数量的网站存在sql注入漏洞。

关于注入点:

好了,我们言归正传,要sql注入,首先要找到sql注入点,注入点是什么?就是你可以用来注入的地址,比如说某个请求会发送某个参数,而这个参数在后台用于作为数据库查询的拼接字段,且未完全过滤,那么这个点就有可能是一个注入点。事实上,注入点在参数不同的时候通常会有不同的表现(可以用来实现盲注),或者会有相应的错误提示。关于注入点,这里不是我们要讨论的重点,找注入点通常是个大工程,实在非人力可为,可以尝试利用工具找到注入点后,再进行人工注入。

sql注入分类:

sql注入根据注入数据类型,通常可分为数字型和字符型;根据注入方式,通常可分为基于错误提示、双注入和盲注等,盲注又可以分为根据时间响应的和基于条件的。

正式开始:

好了,前面说了那么多废话,现在我们开始我们的sql注入教程。我们的教程主要基于sqli-labs。感兴趣的童鞋可以自己安装一下环境(强烈推荐!!!),然后实践一下。如果觉得安装环境很麻烦的童鞋,可以移步sunny的sqli-labs (极度不推荐!!!)进行实践操作。
这一篇,我们主要说说基于错误提示的sql注入,这应该算是sql注入里面的入门了(毕竟,正常情况下,没有哪个网站在生产环境中还会把错误提示放到前端页面)。
我们以sqli-labs等lesson1作为本篇教程的例子。

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 1527260774350.jpg

上图就是主要界面,算是我们找到的一个注入点?现在的我们不懂任何的sql注入知识,只看到界面中有一个sqlidumb series-1非常引人注目——好了,其实这根本不是重点,重点是那行提示:“Please input the ID as parameter with numeric value”,这是啥意思?学过英语的童鞋应该都看懂了吧——看不懂的童鞋可以在线翻译一下哈~

好!现在我们按提示来做,想着输入参数,但是不知道是在哪里输入呢?哦对,get请求可以尝试放在地址栏!那么我们就试试,万一运气好对了呢。

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 1527261180634.jpg

确认过眼神,真的是对的方式。很明显,id为1的用户信息都出来了,包括他的login name和password。再一试,发现id从2到12也有相应的用户信息,一直到id=13。

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 1527261510574.jpg

什么嘛!一夜回到解放前?咋啥都没有了,连“Please input the ID as parameter with numeric value”都没了…

难道sql注入初体验就这样结束了???这完全没有体验嘛!事实上,生活中总是有那么些惊喜,比如:

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

我们不小心在id=1后面加了个单引号(纯属不小心,手残),然后界面上出现了如此提示,一下子多了那么多信息!有了错误提示哎,正如我前面所说的,这真是天上掉馅饼了,连sql报错都出来了!现在,我们发动自己的小脑筋思考一下,什么情况下会返回这个错误呢?看一下错误提示的语句部分,”1” LIMIT 0,1’,这么多引号真的看的人头晕。那么我们来分析一下,首先最前面和最后的引号是用来包裹语句的,所以可以成对去掉便于我们分析,因此语句应该为 ‘1” LIMIT 0,1 很明显,1的后面多了个单引号,正常情况应该为 ‘1’ LIMIT 0,1 而1后面多的那个单引号,不就是我们不小心加在参数里的吗?而且id=’1’后面的语句 LIMIT 0,1都已经暴露给我们。所以,整个sql语句应该大概是

select [column...] from [table_name] where ... id = '[param]' limit 0,1;

param处是我们填入的参数。这时候,我有了一个大胆的想法。如果我们利用某种方法构造一个参数的值,在这个语句中加入自己的语句是不是可行呢?比如:

select [column...] from [table_name] where ... id = '1' order by 1,'1' limit 0,1;

即:

id=1' order by 1,'1

怀着忐忑的心情,我颤抖的双手将参数输入了地址栏,竟然发现结果毫无波澜,和id=1的时候一模一样嘛!

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

咦,等等!这个结果没有报错!那么说明我们的猜想是正确的【手动撒花】。这个结果是根据查询的第一个字段进行排序的结果,那么我们将order=1改成2改成3,一直到4,发现界面上又有了提示!

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

Unknown column '4' in 'order clause'

好了,现在就明白了。说明这个查询只查询出来了3个字段,看到界面上的login name和password,我若有所思,难道另外一个是用户id字段?(好了,这个猜想和后面的注入过程无关,纯粹是瞎猜)
知道了三个字段,我们又开始思考,这个我们如果不仅仅是要查这张表的信息呢?如果我们还想查其他信息怎么办?熟悉sql的童鞋可能就想到了union select,通过union select我们就可以查到其他的内容了。比如,基于我们之前知道的结果。我们可以构造

select [column...] from [table_name] where ... id = '1' union select 1,2,3 order by 1,'1' limit 0,1;

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

好的,非常棒,可以看到2和3已经显示在界面上了,这说明我们这个语句是有效的。1是看不到的,这样我们完全可以用2和3这两个字段来显示我们需要的信息。比如我们要看当前的数据库名以及mysql版本:

select [column...] from [table_name] where ... id = '1' union select 1,database(),version() order by 1,'1' limit 0,1;

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

可以发现,里面并没有我们需要的东西。这就奇怪了,刚才试验明明出来需要的信息了。这时候,我们静下心来想想,发现union select把结果集合并了,但是我们不能保证我们所需要的信息在第一个结果(因为界面上只显示一行)。那怎么办呢?最简单暴力的就是让dumb这个结果消失,具体如何消失就参考我们之前让id=13的时候,发现界面上没信息了,这说明id=13是没有用户信息的。于是 …

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

现在我们就可以满意地一笑,毕竟该有的信息已经出现了。但是贪心的我还是有些不满足,这样每次只能查两个数据,如果我有很多数据要查怎么办。于是我喝了一口水压压惊,想到了如果是多个字段信息,完全可以用字符串连接函数嘛,如果是多行结果,可以考虑group_concat——拼接函数很重要!然后继续修改sql:

select [column...] from [table_name] where ... id = '13' union select 1,2,concat_ws(':',database(),version(),user()) order by 1,'1' limit 0,1;

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

现在就可以把很多个字段的结果放到一个位置上了,同样的如果有多行结果:

select [column...] from [table_name] where ... id = '13' union select 1,2,group_concat(table_name) from information_schema.tables order by 1,'1' limit 0,1;

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

这就将mysql中所有表名都列出来了,界面都显示不下了…
受到这个启发,聪明的我们是不是又想到了什么。
如果我们要查出当前数据库security下所有的表和字段和记录,该怎么做呢?结合之前的结果,我们可以考虑先查出当前数据库中的所有表名,当然也是通过information_schema数据库(这个数据库很重要!!!)——说几句题外话,infromation_schema这个数据库中,主要会有几张表我们之后经常会打交道,tables表和columns表,这两张表分别存储了所有表的信息和字段信息,童鞋们可以自行进入数据库中查看,information_schema这个数据库是自带的。
构造如下sql:

select [column...] from [table_name] where ... id = '13' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' order by 1,'1' limit 0,1;

参数为:

13' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' order by 1,'1

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

还好还好,只有四张表(再多一些可要老命了哦)。那么我们就以users为例子。查询它的字段:

select [column...] from [table_name] where ... id = '13' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security' order by 1,'1' limit 0,1;

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

很好,非常完美,字段都列出来了(果然我们的猜想很厉害,除了显示的两个字段外,另外一个字段是id,【捂嘴笑】)。
字段都出来了,那么记录还会远吗?我们这就把users表中的所有字段都查出来!

select [column...] from [table_name] where ... id = '13' union select 1,2,group_concat(concat_ws(':',id,username,password)) from users order by 1,'1' limit 0,1;

先不急着看结果,我们先看看这个语句,concat_ws之前没有介绍,这个就是一个拼接的函数,第一个参数’:’是拼接的连接符号,如concat_ws(‘:’,1,2,3)的结果就是1:2:3,在sql语句中就是id:username:password。group_concat算是我们的老朋友了,将多条结果拼接。好了,现在让我们看看结果验证一下:

《sql注入那些事儿——如何优雅地进行SQL注入(1)》 图片.png

非常完美!users表中的所有记录信息都列出来了。
难道sql注入这样就完成了吗?
没错!已经完成了,毕竟现在的你已经可以随心所欲查询各个表的信息了,还有啥不满足的!

至此,我们的sql注入教程第一课就到这里了,相信大家已经会了大概的套路,后面我们将会说一下其他方式的注入。童鞋们如果有疑问或者想和我交流的话有两种方式:

第一种

评论留言

第二种

邮箱联系:zsunny@yeah.net

    原文作者:程序员Sunny
    原文地址: https://www.jianshu.com/p/679788d1971d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞