有一阵时间没在写博客,倒是没有闲下来。只是能写的东西到了一段落。
新起一个段落。本来这个分类是为了写信息安全的东西,包括网络和系统两方面。能力加精力的原因,下面以网络安全为主吧。
这篇文章介绍sql注入的基础,会从一个简单的例子进入,一点点讲解sql注入的基础原理。大佬让行就好了,不要在这里浪费时间。
如果你是小白,对渗透感兴趣,但也只是停留在了解的层面上,希望你看完这篇文章有一点新的认识。
首先,环境搭建。(这是你练习技术的主要手段,不要尝试着在没有拿到授权的情况下,渗透别人的网站。那是wei(二声) fa(三声)的,算不上fan(四声) zui(四声),因为既然你在看这篇文章,就说明你的技术水平不允许你fan zui。)
例子借助DVWA讲解,有很多小盆友不晓得是啥子。这是一个歪果仁写的web,旨在学习渗透。链接在这里:
https://github.com/ethicalhack3r/DVWA
当下来部署在本地,就可以在本地进行渗透测试,你也就不用担心人家查水表了。本地服务器怎么部署呢?我们借助phpstudy。又有小朋友不知道phpstudy是啥子。emmmm,它是一个web集成环境Apache + PHP + MySQL。
apache是什么?php呢?(小声BB)
emmm,要不然您别往下看了,有点难。(笑)
好吧,apache和php听说过就好,如果sql语句不懂,就真的别在往下看了。你的时间很珍贵,先去学习数据库基础。
apache就是我们经常说的服务器,其实并不准确,apache只是一个容器,让你可以去访问它。既然访问了它,就要它提供一些服务,比如你点开这篇文章,服务器返回给了你这篇博文,这就是它提供给你的服务。至于提供什么服务,需要代码编写,php就是其中一种语言。
最后mysql是数据库的一种,以下的内容,默认你有sql基础。phpstudy下载链接:
这里的部署过程我们就不说了,去搜索一下,很简单。
把下载好的DVWA解压到www目录下面。
启动服务器:
打开浏览器访问:localhost/DVWA-master(或127.0.0.1/DVWA-master),就会跳转到登录页面。默认登录账号密码admin和password。
登录成功后:
左侧列表会看到一些渗透的目录,sql注入就是我们接下来要用的。phpstudy还有很多玩法,感兴趣的小朋友自己去寻吧。
首先,在下方位置:
调换一下难度系数。既然我们的水平那么好,那我们就选最low的那个。(记得点击submit)
之后我们进入sql注入:
随便输入点什么东西。
其它选项--mysql工具--mysql命令行。
phpstudy集成了数据库可视化工具及可视化web工具,这里我们不使用。还是最开始说的,本文章旨在弄懂原理。
查看数据库,如果DVWA部署没问题,会自动创建一个dvwa数据库。看看里面有什么东西。
里面有两张表(table),users和guestbook。我们查看users表的内容,发现其中的first_name字段就是网页上显示出来的内容。也就是说,服务器将收到我们网页传过来的1和2作为user_id对users表进行查询,拿出来first_name字段显示在网页上。而我们要做的就是,通过修改传递给服务器的数据,让你输出一些其他的东西。比如这个表中的password字段。
下一步,看这里:
url(拜托别问我url是什么,网址!!!!)中,?之后的为传递的参数,通过url传递参数的这种方式叫做GET,另一个常用的方法是POST。
简单了解下就行,这里有篇以前写的文章,主要写的是抓包后的修改,对GET和POST没什么了解的可以简单看下。知道区别就行。(不翻墙可能有点慢,别急)
https://huayuquan.github.io/page/Http_request_response.html
我们知道了,url中id的值就是我们输入的参数。尝试下直接修改url,把id=2换成id=1,回车访问看看是不是admin?
这种url中有类似?id=xx,U_id=xx,诸如此类的,就是我们判断可能存在注入的警示。
如何判断?输入一些奇特但又符合语法的东西,例如1' and 1=1#:
网页正常返回,就说明存在注入。什么意思呢?我们回到最开始的地方,这个输入框的功能是什么?接收用户发送的一个数字,然后查询数据库,把对应ID的name给你返回。
注意,它默认你输入的是数字,并没有对你的输入进行检测。这就是问题所在,我们可以通过输入一些变相的sql语句让它查询一些我们想要查询的东西。
一般我们都是进行黑盒测试,即在不知道源码的情况下进行渗透。为了方便讲解,为什么这样可以判断是否有渗透。我们看下源码它是怎样处理的。
在web的尾部,有个view source:
这是一段php脚本,不懂没关系,等会我教你做英语翻译。
文本也放出来:
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>
发散一下,我们在做英语阅读或者英语翻译的时候。词都不认识,即阅读了,但是没理解。怎么办?
找主干,比如:
A boy, who was considered smart and handsome, achieve a great socroe in this exam ,which we feel normal.
最后剩下什么呢?A boy achieve a great score(随便找的例子,原题这里应该写错了)。一个小男孩得到一个不错的分数。(完,其他那么多东西知不知道,完全不重要。)
回来看那段php脚本。找主干:
划拉划拉,就剩一句(这里纯粹是为了搞笑,当然不影响你对这篇文章的阅读,但是如果你想在这个方向继续下去,php语言是必须会的,起码要看懂。小本本记上,找时间把php补回来。):
当我们从前端传递参数1时,$id的位置就会被1替换。形成如下的sql语句:
$query = " select first_name, last_name from users where user_id = '1'; ";
而数据库中执行的就是双引号中的语句:
select first_name, last_name from users where user_id = '1';
我说过,你要有sql基础,如果上面请你离开的时候,你坚持到了现在。Now,你可以点击关闭了,因为我不会说基础的sql语法知识,只谈逻辑。
好的,扯回来。后面我们发送了1' and 1=1#,为什么它能判断是否有注入。把输入替换$id看看是什么:
"select first_name, last_name from usres where user_id = '1' and 1=1# ' ; ";
同样的,数据库执行双引号之间的sql语句:
select first_name, last_name from usres where user_id = '1' and 1=1# ' ;
mysql中#为注释,也就是说#之后的内容不会执行。
换而言之数据库执行的sql语句是:
select first_name, last_name from usres where user_id = '1' and 1=1
返回来说,我们通过一些特殊方法,让数据库执行的sql语句与正常相比多了一条 and 1=1语句。如果页面正常返回了说明什么?
对,and 1=1被执行了。如果他没有执行,会报错的。
在回看一下参数对$id的替换,明白后我们继续。
我们说过,渗透一般是黑盒测试,我们根本不知道数据库里面表有什么,内容是什么。而这就是我们注入要做的。
首先,我们需要猜测,后台执行的sql语句有几个字段。通过源码我们看到select first_name, last_name两个字段,但是我们不知道源码。这时需要order by 配合着报错信息来碰出来。
先介绍order by,大家肯定不陌生。就是对查询结果排序嘛。例如:
我们对first_name排序。有一种方法大家可能不熟悉,order by后可以接数字。例如:
order by 2是什么意思?就是按你查询字段的第二个字段排序,select user_id(第一个字段), first_name(第二个字段)
如果我们order by 3呢?
报错了!!!
此时你应该开心,因为你发现如果order by的字段不存在就会报错。那怎么注入呢?
同样的,如果理解不了,就把输入替换$id看看是什么:
"select first_name, last_name from usres where user_id = '1' order by 1# ' ; ";
执行SQL语句为:select first_name, last_name from usres where user_id = ‘1’ order by 1
网页正常返回,说明order by 1正确执行了。 说明select 的字段大于等于1!
换个数,我们直接换成3:
报错了!!!Amazing!!换成2,正常返回。得到了,后台执行的sql语句查询了两个字段。
有什么用呢?别急,先放下,我们说另一个sql关键词union。
简单点说,就是把两个查询结果合并,例:
网页不是会显示查询结果吗。那我们就把伪造的后半段结果合并到查询结果里,让网页显示出来,这就是整个的思路。
之前介绍一个mysql的系统函数,database();
显示当前所在数据库,换句话说,我们可以执行此命令获得数据库的名字。
我们知道union前后的查询结果需要相同数量的字段(union本身的要求),这就是我们爆破前半句sql查询字段数的原因所在。
继续注入: 1' union select 1,database()#
实际执行语句:select first_name, last_name from usres where user_id = ‘1’ union select 1,database()
有看到一些重要的东西吗?数据库的名字被显示出来了——dvwa。
目前为止,黑盒测试中,我们知道了这个表在一个叫做dvwa的数据库中。下一步是什么?yeah,得到这个表的名字。
先不急下一步注入,说下理论。
mysql命令行中show databases;,可以看到所有的数据库,也有我们的dvwa。
这之中有一个数据库很重要,就是图中的第一个——information_schema。
这个数据库你可以了理解成是这个mysql中数据库的宏图。进去看看有什么:
众多的table中,有几个很重要。tables,columns,schemata。
tables表中存着数据库中所有表的结构信息。columns中放着列(或者说字段)的信息。schemata中放着数据库信息,和show databases的结果相同。
现在我们从tables表中获取dvwa数据库中的表(注意tables表中的两个字段table_name和table_schema):
现在我们知道了dvwa数据库中有两个表,一个是users,一个是guestbook。我们想得到users中的字段信息。How?对,就是infomation_schema中的columns表。(注意两个字段column_name和table_name)
这样我们就得到了users表中的字段名。我们惊奇的发现竟然有password。吼吼。来,返回到前面,继续注入。注入的流程就是刚刚我们展示的流程。
黑盒测试中,我们通过database()系统函数知道了数据库名字是dvwa。
下一步,看dvwa中的表。
注入:1' union select 1, table_name from information_schema.tables where table_schema="dvwa"#
如果前面看懂了,这里不复杂。我们看到这里爆出了dvwa表的名字,其中有一个users表。
为了美观,你可以使用group_concat函数。效果如下:1' union select 1, group_concat(table_name) from information_schema.tables where table_schema="dvwa"#
会将此字段的结果合并起来。
现在得到了users表的名字,继续下手,看表里的字段。
注入:1' union select 1, column_name from information_schema.columns where table_name="users"#
数据之大,一页放不下。还是用美观版本的吧。加group_concat()
注入:1' union select 1, group_concat(column_name) from information_schema.columns where table_name="users"#
好看多了是吧,而且一目了然。有user_id,first_name, last_name, password.......,竟然有password。别管呵呵还是嘿嘿了。
拿出来!!!
现在我们有了所有的信息,sql语句在一个叫dvwa的数据库中执行,其中有一个users表,里面有个password字段。
最后一脚,注入,把id为1的那个用户的名字和密码调出来!!!:
注入:1' union select first_name, password from users where user_id='1'#
看到这个有什么反应?Ohhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh。
有的同学疑问,用户名admin我知道,password那一堆是什么东西:5f4dcc3b5aa765d61d8327deb882cf99。
不用疑问,它就是密码,只是MD5加密过了,我们登录的时候需要的是明文。解密超简单,找个md5在线工具,查一下:
Ohhhhhhhhhhhh,这就是我们最开始登录Dvwa网站时候用的用户admin和password,致此,我们的一次简单渗透完成了。而最终的结果,我们得到了一个可以登录网站的账户。酷吗?
后记:
为什么存在sql注入,因为后台需要前端用户提交的输入参数。为了防止被sql注入,后台都会对用户的输入进行过滤,比如把你输入中的and,union,order by等等去掉,防止信息泄露。
但是注意一点,它必须需要你的输入,那过滤就是不完全的,总有绕过它的方法。如果你的注入失败了,那是因为姿势不正确(我这句话没有一个污点,麻烦不要突然开车)。
举个例子,后台对union进行了过滤,输入的数据会把union去掉之后再去执行。比如注入1' union select 1,2 from users#。在执行前,先过滤union,你的参数就变了1' select 1,2 from users#。
替换$id是什么?
SELECT first_name, last_name FROM users WHERE user_id = '1' select 1,2 from users。
这完全是个错误语句,执行失败,报错。你的注入到此为止。
我说过是姿势不对,试想,如此注入:1' uniunionon select 1,2 from users#
在union之间任意位置插入union,过滤时把union过滤掉,然后呢?就成了1' union select 1,2 from users#。
哈哈哈,没想到吧。你过滤掉之后,我前后的内容仍旧能拼出来一个union。
这只是一种绕过的姿势,而且在之后的学习过程中,你会使用各种各样奇葩的工具,根本不用如此大费周章的进行注入。学习到的绕过技巧也会越来越多,越来越花哨。
此例中的数据库仅为mysql,不用担心。流程都是一样的,只是不同的数据库结构的不同,对症下药即可。像最开始说的,这篇博文只为让入门的小盆友搞清注入的原理。入了门,剩下的就是怎么把“刀”磨亮了。
以下是看雪的一张ppt截图,贴出来,当个总结吧。
希望这篇博文没有让你丧失对网络安全的热情。
(ps:初学者,撰写错误与不妥之处欢迎指出,我会将您的评论贴进去。另,最近在找社会工程的教程,如果有哪位读者有,又无所谓分享的话,也欢迎一并散出。阿里卡多~)