Brute Force

low级别

QMFPu6.png

爆破利用burpsuite即可完成

抓包,并送至intruder

QMFUvq.png

选择爆破点password和字典开始爆破

QMFs54.png

点击页面长度进行排序,可看到密码为password返回的页面长度与别的明显不同

QMFoIe.png

载入详情,查看渲染,回显登录成功,由此爆破出密码为password

QMkPRs.png

medium级别

查看源代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );

// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$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>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];

// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

比low级别增加了mysqli_real_escape_string()函数,此函数会转义在 SQL 语句中使用的字符串中的特殊字符。可以抵御sql注入。登录失败会做sleep(2)但仍可以进行爆破,可用同low方法进行爆破,只是速度会变慢。

high级别

查看源代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );

// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$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>' );

if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];

// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>

对比medium级别,username和password的过滤机制增加了stripslashes()函数,可以进一步的抵御sql注入,代码加入了user_token,每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。并在结尾处增加了sleep( rand( 0, 3 ) )登录失败的延时机制。low方式无法再次使用。每次登录都要提交Login、user_token、username和password。

代理抓包,发送至intruder,选择Pitchfork攻击方式,并确认爆破点password和user_token

QMlEOe.png

在options栏找到Grep - Extract,点击Add。

QMltTs.png

点击Refetch response,进行一个请求,即可看到响应报文,直接选取需要提取的token

QMtlUe.png

设置payload1为常用密码

QMtB5Q.png

设置payload2,选择payload type为“Recursive grep”

QMtoG9.png

开始爆破,查看爆破结果,爆破成功,密码password

QMN9xI.png

SQL Injection

low级别

image.png

输入1' and '1'='1,回显正常id=1的情况,存在SQL注入

image.png

之后猜测SQL语句字段数,输入1' order by 3 # ,提示不存在,由此判断查询语句存在2字段。

image.png

之后可用1′ union select 1,database() #查询数据库

image.png

查表1′ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #

image.png

查字段名1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #

image.png

查字段值1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #

image.png

medium级别

本题只能选择再提交,所以选择抓包的方式进行数据包修改参数id

QMzIzV.png

更改成1‘ and 1=2 #,出现报错,说明不是字符型注入

QQpM1x.png

尝试数字型注入,1 and 1=1#,回显正常,找到注入点

QQpN4A.png

剩下爆数据库,表名可参考low级别payload

high级别

与Medium级别的代码相比,High级别的只是在SQL查询语句中添加了LIMIT 1,控制只输出一个结果。

1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #

QQ90i9.png

High级别的查询提交页面与查询结果显示页面不是同一个,也没有执行302跳转,这样做的目的是为了防止一般的sqlmap注入,因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。

SQL Injection (Blind)

low级别

只会显示exists和MISSING,其中exists代表查询正确,MISSING代表查询错误

QQCEFJ.png

开始尝试数据库名长度,使用语句1' and length(database())=1#,发现不成功一直到4回显exists,表明数据库名长度为4

QQPg4H.png

开始猜测数据库名,1' and ascii(substr(database(),1,1))>97#1' and ascii(substr(database(),1,1))<123#判断第一个字母是否在a-z中间,返回都是exists

QQPjvq.png

接着用二分法证实得第一个字母为d。同理求出完整数据库名为 dvwa

之后开始猜有几个表,猜解表的长度,猜解表的名称

猜测表个数1' and (select count(table_name) from information_schema.tables where table_schema='dvwa')=2#,为2。

QQiJMt.png

猜第一个表名,从第一位开始猜,二分法,可用以下语句尝试,最后得到表1完整名称是guestbook

1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=103#

猜第二个表名,用以下语句尝试,得到表2的名称是users

1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=117#

以后按照以上类似方法就可得到字段值,不再累述

medium级别

QQE1Tf.png

查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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();
}
?>

相比low级别添加了mysqli_real_escape_string(),过滤了一些单引号等字符,并使用post方法发送数据。但此题没有涉及字符型注入,所以直接构造语句

1 and length(database())=4#

盲注方法参照low级别,不再累述

high级别

QQVX24.png

查看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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);
}
?>

High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。由于时间盲注的准确性受到影响,所以只利用基于布尔的盲注

抓包将cookie中参数id改为1' and length(database())=4 #,显示存在

XSS(Reflected)

Low级别

QdOxwn.png

直接引用了name参数,并没有任何的过滤和检查,存在明显的XSS漏洞,输入<script>alert(/xss/)</script>,成功弹框:

image.png

Medium级别

image.png

对输入进行了过滤,使用str_replace函数将输入中的<script>删除输入<sc<script>ript>alert(/xss/)</script>,成功弹框:

image.png

High级别

image.png

preg_replace() 函数用于正则表达式的搜索和替换,这使得双写绕过、大小写混淆绕过(正则表达式中i表示不区分大小写)不再有效,可以通过img、body等标签的事件或者iframe等标签的src注入恶意的js代码。

输入<img src=1 onerror=alert(/xss/)>,成功弹框:

image.png

XSS(Stored)

Low级别

image.png

对输入并没有做XSS方面的过滤与检查,且存储在数据库中,因此这里存在明显的存储型XSS漏洞。message一栏输入<script>alert(/xss/)</script>,成功弹框:

image.png

Medium级别

image.png

由于对message参数使用了htmlspecialchars函数进行编码,因此无法再通过message参数注入XSS代码,但是对于name参数,只是简单过滤了<script>字符串,仍然存在存储型的XSS。

抓包改name参数为<sc<script>ript>alert(/xss/)</script>,成功弹窗

image.png

High级别

image.png

使用正则表达式过滤了<script>标签,但是却忽略了img、iframe等其它危险的标签,因此name参数依旧存在存储型XSS

抓包改name参数为<img src=1 onerror=alert(/xss/)>,成功

image.png