跳转至

项目介绍

什么是DVWA

DVWA是一个非常易受攻击的PHP/MySQL Web应用程序。其主要目标是帮助安全专业人员在法律环境中测试他们的技能和工具,帮助web开发人员更好地理解保护web应用程序的过程,并帮助学生和教师在受控教室环境中学习web应用程序安全. DVWA的目标是 练习一些最常见的web漏洞, 有着不同的难度, 并具有简单直观的界面.

为什么研究 DVWA

  • 非常经典 持续迭代更新
    DVWA(Damn Vulnerable Web Application)是OWASP组织维护的,已知最早的漏洞靶场, 并在不断地更新,最近刚增加了 权限绕过 和 任意URL跳转漏洞 样例

  • 全面漏洞详情
    每种漏洞分4个等级
    初级 是完全的没有任何防护的漏洞
    中级 是增加了安全防御 但存在绕过权限 高级 是进一步完善了安全校验,但可能仍然存在缺陷 或需要其他漏洞配合
    困难/不可能等级 基本上没有安全漏洞,修复了已知的所有问题 但可能由于实现问题,仍然存在安全漏洞
    每个题都可以在界面上查看源代码 和查看每个等级的帮助信息,比较人性化

  • 适合代码审计 思度安全在打造的代码审计课程,希望每个人都能从代码层面发现安全漏洞,知道漏洞产生的根本原因,在此基础上,知道怎样在代码层面进行修复,所以这套代码非常适合做思度安全代码审计课程的实验环境

DVWA的不足

  • 汉化支持的不好
    目前只有首页的README有中文版本

  • 部分漏洞依赖第三方资源
    并且这些资源是在墙wai,比如google的验证码

  • 部分题 讲解不到位
    比如文件包含漏洞,忽视了php.ini 路径配置的限制作用

DVWA的汉化改造

为了让更多初学者能够快速使用DVWA系统进行代码审计工作,由思度安全团队发起了对DVWA汉化项目,希望汉化后的系统是代码审计初学者入门级试验场。

汉化的目标

对最新版本的DVWA进行汉化,使其支持汉语和英语两种语言,并且用户/使用者可自行切换

项目收益

  • 团队成员 能够学习PHP基础,了解DVWA整体代码结构
  • 团队成员 能够看懂常见安全漏洞代码,并能够进行修改
  • 团队成员 能够学习基本的英文,并进行实际应用
  • 团队成员 真实的参与项目,体现项目过程 和分工

实现切换语言功能

原始功能分析

DVWA默认是在\config\config.inc.php 的38行有对语言的配置信息

# Default locale
# Default locale for the help page shown with each session.
# The default is 'en'. You may wish to set this to either 'en' or 'zh'.

$_DVWA[ 'default_locale' ] = 'en';  

默认是en,表示英文,zh表示中文

在 \dvwa\includes\dvwaPage.inc.php 在第57行开始设置dvwa的语言

if (!array_key_exists ("default_locale", $_DVWA)) {
$_DVWA[ 'default_locale' ] = "en";
}
dvwaLocaleSet( $_DVWA[ 'default_locale' ] );

直接将配置文件中设置的语言,通过dvwaLocaleSet函数设置到session中,然后通过dvwaLocaleGet函数获得当前设置的语言类型。

这里主要对dvwaLocaleGet函数进行改造,最终代码如下

function dvwaLocaleGet() {  
    $dvwaSession =& dvwaSessionGrab();
    // 如果没有local 在session里面 从浏览器获取local
    $language = 'en';
    if (!array_key_exists ("locale", $dvwaSession)) {
         if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            // 获取浏览器支持的语言列表
            $accepted_languages = $_SERVER['HTTP_ACCEPT_LANGUAGE'];

            // 按逗号进行分隔
            $language_list = explode(',', $accepted_languages);

            // 读取第一个语言表示
            $language = substr($language_list[0], 0, 2);
            setcookie( 'local',$language, 0, "/", "", false, $httponly );

            //echo "Browser preferred language: $language";
        }else{
            if (!array_key_exists ("default_locale", $_DVWA)) {
                $_DVWA[ 'default_locale' ] = "en";
            }
            $language = $_DVWA[ 'default_locale' ];
        }

        $default_locale = array('en', 'zh'); 
        if(!in_array( $language, $default_locale )){
            $language = 'en';
        }
        dvwaLocaleSet($language);  
    }

    return $dvwaSession[ 'locale' ];
}

首先,判断用户的session是否已经包含 locale字段,如果没有则读取客户端浏览器信息,获得第一个支持的语言, 目前只支持 zh和en两种语言

页面添加语言切换功能

在security.php中原来是实现每种漏洞等级的切换的,现在增加对语言的切换代码,具体代码如下

<?php
....
$securityHtml = '';
if( isset( $_POST['seclev_submit'] ) ) {
    // Anti-CSRF
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'security.php' );

    $securityLevel = '';
    switch( $_POST[ 'security' ] ) {
        case 'low':
            $securityLevel = 'low';
            break;
        case 'medium':
            $securityLevel = 'medium';
            break;
        case 'high':
            $securityLevel = 'high';
            break;
        default:
            $securityLevel = 'impossible';
            break;
    }

    dvwaSecurityLevelSet( $securityLevel );
    $msg = "Security level set to {$securityLevel}";


    $language = '';
    switch( $_POST[ 'language' ] ) {
        case 'zh':
            $language = 'zh';
            break;
        case 'en':
            $language = 'en';
            break;

        default:
            $language = 'zh';
            break;
    }

    $msg .= " language set to {$language}";
    dvwaLocaleSet($language);
    dvwaMessagePush( $msg );

    dvwaPageReload();
}

$securityOptionsHtml = '';
$securityLevelHtml   = '';
foreach( array( 'low', 'medium', 'high', 'impossible' ) as $securityLevel ) {
    $selected = '';
    if( $securityLevel == dvwaSecurityLevelGet() ) {
        $selected = ' selected="selected"';
        $securityLevelHtml = "<p>Security level is currently: <em>$securityLevel</em>.<p>";
    }
    $securityOptionsHtml .= "<option value=\"{$securityLevel}\"{$selected}>" . ucfirst($securityLevel) . "</option>";
}
$LanguageOptionsHtml = '';
$now_lan = dvwaLocaleGet();

foreach( array( 'zh', 'en') as $localLevel ) {
    $selected = '';
    if( $localLevel == $now_lan) {

        $selected = ' selected="selected"';
        $securityLevelHtml .= "<p>language  is currently: <em>$localLevel</em>.<p>";
    }
    $LanguageOptionsHtml .= "<option value=\"{$localLevel}\"{$selected}>" . ($localLevel) . "</option>";
}

// Anti-CSRF
generateSessionToken();

$page[ 'body' ] .= "
<div class=\"body_padded\">
    <h1>DVWA Security <img src=\"" . DVWA_WEB_PAGE_TO_ROOT . "dvwa/images/lock.png\" /></h1>
    <br />

    <h2>Security Level</h2>

    {$securityHtml}

    <form action=\"#\" method=\"POST\">
         ....
        </ol>
        <select name=\"security\">
            {$securityOptionsHtml}
        </select>
        <select name=\"language\">
            {$LanguageOptionsHtml}
        </select>


        <input type=\"submit\" value=\"Submit\" name=\"seclev_submit\">
        " . tokenField() . "
    </form>
</div>";

dvwaHtmlEcho( $page );

?>

至此,基本完成添加语言切换的功能

实现切换每种漏洞题目

左侧主菜单的汉化

具体功能是在dvwaPage.inc.php 的 dvwaHtmlEcho函数中,在该函数增加判断,如果是zh语言,则切换到 dvwaHtmlEcho_zh函数,代码如下

function dvwaHtmlEcho( $pPage ) {
    // 如果是汉语 则直接重写
    if(dvwaLocaleGet() == 'zh'){
        return dvwaHtmlEcho_zh($pPage);
    }

对应汉化菜单代码如下 (dvwaHtmlEcho_zh函数)

function dvwaHtmlEcho_zh( $pPage ) {
    $menuBlocks = array();

    $menuBlocks[ 'home' ] = array();
    if( dvwaIsLoggedIn() ) {
        $menuBlocks[ 'home' ][] = array( 'id' => 'home', 'name' => '首页', 'url' => '.' );
        $menuBlocks[ 'home' ][] = array( 'id' => 'instructions', 'name' => '介绍', 'url' => 'instructions.php' );
        $menuBlocks[ 'home' ][] = array( 'id' => 'setup', 'name' => '安装/重置 数据库', 'url' => 'setup.php' );
    }
    else {
        $menuBlocks[ 'home' ][] = array( 'id' => 'setup', 'name' => '安装 DVWA', 'url' => 'setup.php' );
        $menuBlocks[ 'home' ][] = array( 'id' => 'instructions', 'name' => '介绍', 'url' => 'instructions.php' );
    }

    if( dvwaIsLoggedIn() ) {
        $menuBlocks[ 'vulnerabilities' ] = array();
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'brute', 'name' => '暴力破解(BruteForce)', 'url' => 'vulnerabilities/brute/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'exec', 'name' => '命令注入(Command Injection)', 'url' => 'vulnerabilities/exec/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'csrf', 'name' => '跨站请求伪造(CSRF)', 'url' => 'vulnerabilities/csrf/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'fi', 'name' => '文件包含(File Inclusion)', 'url' => 'vulnerabilities/fi/.?page=zh.include.php' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'upload', 'name' => '任意文件上传(File Upload)', 'url' => 'vulnerabilities/upload/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'captcha', 'name' => '不安全的验证码(Insecure CAPTCHA)', 'url' => 'vulnerabilities/captcha/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'sqli', 'name' => '数据库注入(SQL Injection)', 'url' => 'vulnerabilities/sqli/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'sqli_blind', 'name' => '数据库盲注(SQL Injection (Blind))', 'url' => 'vulnerabilities/sqli_blind/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'weak_id', 'name' => '弱会话id(Weak Session IDs)', 'url' => 'vulnerabilities/weak_id/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'xss_d', 'name' => 'DOM型跨站脚本攻击(XSS (DOM))', 'url' => 'vulnerabilities/xss_d/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'xss_r', 'name' => '反射型跨站脚本攻击(XSS (Reflected))', 'url' => 'vulnerabilities/xss_r/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'xss_s', 'name' => '存储型跨站脚本攻击(XSS (Stored))', 'url' => 'vulnerabilities/xss_s/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'csp', 'name' => '绕过内容安全策略(CSP Bypass)', 'url' => 'vulnerabilities/csp/' );
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'javascript', 'name' => 'JavaScript攻击(JavaScript)', 'url' => 'vulnerabilities/javascript/' );
        if (dvwaCurrentUser() == "admin") {
            $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'authbypass', 'name' => '认证绕过(Authorisation Bypass)', 'url' => 'vulnerabilities/authbypass/' );
        }
        $menuBlocks[ 'vulnerabilities' ][] = array( 'id' => 'open_redirect', 'name' => 'URL跳转(Open HTTP Redirect)', 'url' => 'vulnerabilities/open_redirect/' );
    }

    $menuBlocks[ 'meta' ] = array();
    if( dvwaIsLoggedIn() ) {
        $menuBlocks[ 'meta' ][] = array( 'id' => 'security', 'name' => '安全等级设置', 'url' => 'security.php' );
        $menuBlocks[ 'meta' ][] = array( 'id' => 'phpinfo', 'name' => 'PHP 信息', 'url' => 'phpinfo.php' );
    }
    $menuBlocks[ 'meta' ][] = array( 'id' => 'about', 'name' => '关于', 'url' => 'about.php' );

    if( dvwaIsLoggedIn() ) {
        $menuBlocks[ 'logout' ] = array();
        $menuBlocks[ 'logout' ][] = array( 'id' => 'logout', 'name' => '登出', 'url' => 'logout.php' );
    }

    $menuHtml = '';

    foreach( $menuBlocks as $menuBlock ) {
        $menuBlockHtml = '';
        foreach( $menuBlock as $menuItem ) {
            $selectedClass = ( $menuItem[ 'id' ] == $pPage[ 'page_id' ] ) ? 'selected' : '';
            $fixedUrl = DVWA_WEB_PAGE_TO_ROOT.$menuItem[ 'url' ];
            $menuBlockHtml .= "<li class=\"{$selectedClass}\"><a href=\"{$fixedUrl}\">{$menuItem[ 'name' ]}</a></li>\n";
        }
        $menuHtml .= "<ul class=\"menuBlocks\">{$menuBlockHtml}</ul>";
    }

    // Get security cookie --
    $securityLevelHtml = '';
    switch( dvwaSecurityLevelGet() ) {
        case 'low':
            $securityLevelHtml = 'low';
            break;
        case 'medium':
            $securityLevelHtml = 'medium';
            break;
        case 'high':
            $securityLevelHtml = 'high';
            break;
        default:
            $securityLevelHtml = 'impossible';
            break;
    }
    // -- END (security cookie)

    $userInfoHtml = '<em>用户名:</em> ' . ( dvwaCurrentUser() );
    $securityLevelHtml = "<em>安全等级:</em> {$securityLevelHtml}";
    $localeHtml = '<em>语言:</em> ' . ( dvwaLocaleGet() );
    $sqliDbHtml = '<em>数据库引擎:</em> ' . ( dvwaSQLiDBGet() );


    $messagesHtml = messagesPopAllToHtml();
    if( $messagesHtml ) {
        $messagesHtml = "<div class=\"body_padded\">{$messagesHtml}</div>";
    }

    $systemInfoHtml = "";
    if( dvwaIsLoggedIn() ) 
        $systemInfoHtml = "<div align=\"left\">{$userInfoHtml}<br />{$securityLevelHtml}<br />{$localeHtml}<br />{$sqliDbHtml}</div>";
    if( $pPage[ 'source_button' ] ) {
        $systemInfoHtml = dvwaButtonSourceHtmlGet( $pPage[ 'source_button' ] ) . " $systemInfoHtml";
    }
    if( $pPage[ 'help_button' ] ) {
        $systemInfoHtml = dvwaButtonHelpHtmlGet( $pPage[ 'help_button' ] ) . " $systemInfoHtml";
    }

    // Send Headers + main HTML code
    Header( 'Cache-Control: no-cache, must-revalidate');   // HTTP/1.1
    Header( 'Content-Type: text/html;charset=utf-8' );     // TODO- proper XHTML headers...
    Header( 'Expires: Tue, 23 Jun 2009 12:00:00 GMT' );    // Date in the past

    echo "<!DOCTYPE html>

<html lang=\"en-GB\">

    <head>
        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />

        <title>{$pPage[ 'title' ]}</title>

        <link rel=\"stylesheet\" type=\"text/css\" href=\"" . DVWA_WEB_PAGE_TO_ROOT . "dvwa/css/main.css\" />

        <link rel=\"icon\" type=\"\image/ico\" href=\"" . DVWA_WEB_PAGE_TO_ROOT . "favicon.ico\" />

        <script type=\"text/javascript\" src=\"" . DVWA_WEB_PAGE_TO_ROOT . "dvwa/js/dvwaPage.js\"></script>

    </head>

    <body class=\"home\">
        <div id=\"container\">

            <div id=\"header\">

                <img src=\"" . DVWA_WEB_PAGE_TO_ROOT . "dvwa/images/logo.png\" alt=\"Damn Vulnerable Web Application 思度安全汉化版\" />

            </div>

            <div id=\"main_menu\">

                <div id=\"main_menu_padded\">
                {$menuHtml}
                </div>

            </div>

            <div id=\"main_body\">

                {$pPage[ 'body' ]}
                <br /><br />
                {$messagesHtml}

            </div>

            <div class=\"clear\">
            </div>

            <div id=\"system_info\">
                {$systemInfoHtml}
            </div>

            <div id=\"footer\">

                <p>Damn Vulnerable Web Application (DVWA) <a href=\"http://siduquan.com\" target=\"_blank\">思度安全团队汉化版 V0.1 2023-10-20</a></p>
                <p>排名不分先后: luvsicor,王较瘦,海洋,思安,神风,玉树</p>
                <script src='" . DVWA_WEB_PAGE_TO_ROOT . "dvwa/js/add_event_listeners.js'></script>

            </div>

        </div>

    </body>

</html>";
}

具体漏洞汉化

经过分析后得知,每种漏洞是一个文件夹,入口是index.php文件,因此在index.php文件首先判断当前设置的语言 如果是zh则加载汉语版入口,即zh.index.php文件,由于每个index.php函数都需要这个判断,因此新建一个 \dvwa\includes\change.local.php 文件 ,判断代码如下

<?php 

$local = dvwaLocaleGet();
if ( $local !== 'en'){
    require_once($local.'.' . basename($_SERVER['PHP_SELF']));
    exit;
}
?>

并且在每个index.php文件开头添加引用

require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/change.local.php';

zh.index.php文件的汉化过程,核心代码是修改$vulnerabilityFile,因为后面会用到这个变量进行判断


$vulnerabilityFile = 'zh.';
switch( dvwaSecurityLevelGet() ) {
    case 'low':
        $vulnerabilityFile .= 'low.php';
        break;
    case 'medium':
        $vulnerabilityFile .= 'medium.php';
        break;
    case 'high':
        $vulnerabilityFile .= 'high.php';
        break;
    default:
        $vulnerabilityFile .= 'impossible.php';
        break;
}

例如下面的判断

if( $vulnerabilityFile == 'zh.impossible.php' )
    $page[ 'body' ] .= "            " . tokenField();


加载不同漏洞等级,将会执行不同的代码片段。 都要进行对应修改

因此需要对index.php和source下的每个php文件都要建立 对应的zh开头的汉化文件

为了快速将每个文件都复制一份汉化版本,写一个小的py脚本来实现

import os
import shutil

path = r'/www/wwwroot/dvwa.siduanquan.com/DVWA-2.3/vulnerabilities'

for dirpath, dirnames, filenames in os.walk(path):
    for filename in filenames:

        fname =  os.path.join(dirpath,filename)
        if filename.find('help.php') > -1:
            shutil.copy(fname, fname.replace('help.php','help.zh.php'))
            print(fname, fname.replace('help.php','help.zh.php'))
        elif filename in ['index.php', 'low.php','medium.php','high.php','impossible.php']:
            shutil.copy(fname, fname.replace(filename,'zh.' + filename))
            print(fname, fname.replace(filename,'zh.' + filename))
sys.exit()

接下来就对每个zh文件进行汉化处理即可

实现切换帮助信息

默认DVWA的帮助信息实现在 /vulnerabilities/view_help.php 文件中,

$id       = $_GET[ 'id' ];
$security = $_GET[ 'security' ];
$locale = $_GET[ 'locale' ];

ob_start();
if ($locale == 'en') {
    eval( '?>' . file_get_contents( DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/{$id}/help/help.php" ) . '<?php ' );
} else {
    eval( '?>' . file_get_contents( DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/{$id}/help/help.{$locale}.php" ) . '<?php ' );
}

从代码看,其实支持不同语言的切换,通过传入的locale参数进行判断,加载不同的帮助信息,如汉语,只需要再每个漏洞的help文件夹下建立help.zh.php文件即可

最终汉化结果

将最终汉化后的代码部署到 DVWA汉化地址 部分翻译参考DVWA-Chinese

Back to top