目录
Brute Force——暴力破解
Command Injection——命令注入
CSRF——跨站请求伪造
File Inclusion——文件包含
File Upload——文件上传
DOM Based Cross Site Scripting (XSS)——DOM型XSS
Reflected Cross Site Scripting (XSS)——反射型XSS
Stored Cross Site Scripting (XSS)——存储型XSS
源代码:
' . ((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 ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Username and/or password incorrect.
代码直接获取用户输入的用户名和密码,密码在用md5加一下密
先将DVWA设置为Low级别,然后在登陆界面随意输入用户名和密码,设置代理截取数据包,然后右键点击发送至Intruder
选择攻击方式为Cluster bomb,并设置用户名和密码位置为攻击位置,在此我设置的是假设用户名和密码都不知道的情况下进行暴力破解,不过非常耗时,一般往往都是针对某一用户名爆破密码,攻击方式设置为sniper即可,详情参考前文burpsuite——intruder
而后设置Payloads,Payload set 1设置为usernames,Payload set 2设置为passwords,设置好后,点击右上角的start attack开始攻击,当然了,这样会经历一个非常非常非常漫长的过程,也可以直接假设用户名为admin只使用sniper模式暴力破解密码(参考前文BurpSuite——Intruder)也可以自己填几个用户名和密码进行模拟实验。
在实验中,我仅填了几个用户名和密码,其中包括真实用户名和密码,之后匹配length长度,找出特殊的那个,即为正确用户名和密码
源代码:
' . ((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 ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Username and/or password incorrect.
这个级别利用函数mysqli_real_escape_string()进行了一些特殊字符的转义,抵御sql注入,并且如果密码错误的话,会加一个2秒的延时,但是思路和上一级别相同,只不过更慢了而已
因为时间关系,这次就只设置一个攻击位置,使用sniper攻击方式
设置好payloads后进行攻击即可,然后在根据length判断密码
源代码:
' . ((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 ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>
Username and/or password incorrect.
同样利用mysqli_real_escape_string()函数进行了一些特殊字符的转义,并且用stripslashes()函数去除掉了string字符的反斜杠,抵御sql注入以及xss
high级别增加了Anti-CSRF token,用来抵御CSRF攻击,但每次提交都会有一个随机的user_token
可以看到,即使提交的用户名和密码相同,user_token也是不同的,再次抓包发送至intruder,并选择pitchfork模式,设置密码和token值为攻击点
然后将Grep-Extract按下图这样设置
在设置总是允许重定向,并设置单线程攻击
设置payloads,第一个攻击位置和之前一样设置,只设置几个密码即可(适当的时候让电脑偷个懒)
第二个攻击位置设置成Recursive grep,并将之前获得的token值复制过来,点击start attack即可
然后找到结果即可
源代码:
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();
?>
这个不可能等级在high等级的基础上限制了登录次数,登录失败三次就会锁住账号15s,同时采用了PDO防御sql注入
源代码:
{$cmd}
";
}
?>
执行ping命令,但未对输入进行过滤,利用符号连接请求
源代码:
'',
';' => '',
);
// 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}
";
}
?>
使用了黑名单策略,对&&和;进行了转义,黑名单即不允许输入什么,但是往往存在其他方式利用漏洞
command1 && command2表示先执行1,成功后执行2,否则不执行2
command1 & command2表示先执行1,不管是否成功都执行2
所以即使上图中command1位置输入有误也会执行command2位置的命令
源代码:
'',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// 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}
";
}
?>
我们可以看到,这次过滤的字符比较多,进一步完善了黑名单,但仍然有缺陷
这个竖线后面有一个明显的空格,所以我们可以用 | 拼接两个命令,前后不留空格即可
command1 | command2表示将command1的输出作为command2的输入,并且只打印command2的结果
源代码:
{$cmd}
";
}
else {
// Ops. Let the user name theres a mistake
echo 'ERROR: You have entered an invalid IP.'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
stripslashes()函数回删除字符串中的反斜杠,返回没有反斜杠的字符串
explode()函数把字符串分散成数组
执行的差不多是白名单的思路,只有数字.数字.数字.数字才会被接收执行
源代码:
' . ((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); } ?>
可以看到只需要新密码与确认密码两栏相同就可以将密码更改, 所以我们可以制作一个请求链接来钓鱼http://192.168.61.128/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
只要用户点击这个链接,网页就会显示password changed,并且将密码改为password,我们的目的也就达到了
它利用用户的身份,诱使用户点击了相关链接,从而达到修改用户密码的过程。
比如说一个不知名的网站恰好存在此漏洞,黑客给目标用户发送钓鱼链接,等待用户登陆成功上线后收到消息,然后打开链接,那么该用户的密码则会被修改,因为链接上存在修改密码的参数,大部分用户可能会猜到,所以黑客往往需要将其隐藏,比如重新构造一个网站或者生成一个短链接等。
诱使用户访问这个链接同样能够达到攻击效果
源代码:
' . ((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); } ?>
相比low级别,这个级别验证了请求头中的referer字段,请求必须带有服务器IP地址才能发出,如果使用上一级别构造的url,报错如下
我们正常修改密码时抓包如下
在输入构造的url抓包如下,发现并没有referer参数
尝试手动加入一个,胡乱写了地址,但是包含了目标IP
成功修改了密码,然后按照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); } // Generate Anti-CSRF token generateSessionToken(); ?>
与前两个级别相比,这个级别加入了anti-csrf token,每次修改密码时都会返回一个随机的token参数,发起请求必须要提交该参数,服务器在处理时先查验token参数是否正确,然后决定是否修改密码
同样的,抓包查看
要完成此题,需要获取token值,所以需要构造代码,先获取token值,然后再改密码
浏览器的同源策略不允许跨域请求,看其他人的完成过程,发现可以结合xss一同攻击,在此不再叙述。
源代码:
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 "Password Changed.
";
}
else {
// Issue with passwords matching
echo "Passwords did not match or current password incorrect.
";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
需要输入原始密码,从根上解决了这一问题
源代码:
服务器没有做任何的过滤,按照网站开发人员的设想是,我们应该点击对应的链接跳转到对应的页面
点击file1.php对应的url为http://192.168.186.128/dvwa/vulnerabilities/fi/?page=file1.php
现在构造一个url http://192.168.186.128/dvwa/vulnerabilities/fi/?page=vegetable
服务器肯定是没有vegetable这个文件的,如果存在文件包含漏洞,就会出现如下结果
可以看到,页面报错显示出了正确的文件路径
构造url http://192.168.186.128/dvwa/vulnerabilities/fi/page=C:\phpStudy\PHPTutorial\WWW\dvwa\php.ini可以发现我们读取了php相关配置,这肯定是不被允许的
不仅如此,也可以使用../../../../来读取相对路径的内容,使用../是为了进入c盘根目录
除此之外,如果allow_url_fopen与allow_url_include开启的话,还可以还包含远程服务器上的文件在?page后加一个http://……即可,在此条件不足不进行演示
源代码:
可以看到代码过滤了http://,https://以及 ../ ..\,将其替换为空字符,但是str_replace可以通过双写绕过比如?page=http://……,此时会被过滤,但是可以写成?page=hthttp://tp://,代码将http://删除后,前后拼合依旧是http://
而../同样可以利用 ..././ 来绕过,如图所示
源代码:
从代码看出,使用了!fmatch函数检查?page后的参数,要求page开头的参数必须是file才行,这时,我们随便输一个值时,页面返回如下
使用之前构造的url也是一样的结果,但在这里可以利用file协议来绕过,我们在浏览器中可以直接打开本地内容,比如打开我刚刚存在桌面的.txt文件
同样的思路,构造?page=后的参数为 file://C:/phpStudy/PHPTutorial/WWW/1.html 即可和刚才一样成功利用漏洞‘
源代码:
可以看使用了白名单,依然从根上解决了问题,?page后的参数是规定好的,不可以更改,白名单yyds
源代码:
Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!"; } } ?>
代码非常简单,没有对上传文件进行任何形式的过滤,存在很明显的文件上传漏洞
我们可以在txt文件中写上一句话木马,然后将后缀改为 .php 进行上传
很容易的就上传成功了,并且给出了上传路径
然后使用菜刀等工具进行连接即可
源代码:
Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!"; } } else { // Invalid file echo '
Your image was not uploaded. We can only accept JPEG or PNG images.'; } } ?>
服务器对上传文件的大小和类型做了限制,只能上传文件类型为jpeg或png格式的文件,且大小小于100000字节
在此,依然上传刚才的 1.php 文件,然后用burpsuite抓包,修改文件类型即可
可以看到上传成功,在用菜刀等工具连接即可
源代码:
Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!"; } } else { // Invalid file echo '
Your image was not uploaded. We can only accept JPEG or PNG images.'; } } ?>
使用函数检测上传文件是否为图片格式,并约束文件后缀名必须为jpg、jpeg、png结尾。也就是文件格式和文件内容都必须是图片才可以
在命令行中使用 copy /b 1.png+1.php 2.png 将一句话木马与图片相结合
但是此时无法直接用菜刀链接,可以结合其他漏洞一起进行利用
源代码:
${target_file} succesfully uploaded!
";
}
else {
// No
echo 'Your image was not uploaded.'; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file echo '
Your image was not uploaded. We can only accept JPEG or PNG images.'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
直接对上传的文件进行了重命名并且防护csrf攻击,对文件内容进行了严格的筛查
源代码:
什么限制都没做,可以直接构造最简单的代码来触发xss
源代码:
可以看到过滤了
源代码:
', '', $_GET[ 'name' ] );
// Feedback for end user
echo "Hello ${name}
";
}
?>
代码可以看到过滤了
源代码:
Hello ${name}
";
}
?>
可以看到过滤加强了,并且不区分大小写
可以使用语句
源代码:
Hello ${name}
";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
htmlspecialchars会把预定义字符 < > & 转换为 html 实体,防止浏览器将其作为 html 元素
源代码:
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
//mysql_close();
}
?>
可以看到代码写了很多
trim会移除字符两侧的预定义字符,stripslashes 会去除反斜杠等等,但是name和message并没有进行xss过滤,最简单的代码即可使用
保存后随即产生弹窗
可以去后台数据库中查看,可以看到代码确实被写入其中
源代码:
', '', $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( '' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
//mysql_close();
}
?>
message加入了xss过滤,但是name仅仅过滤了