专栏首页TeamsSix的网络空间安全专栏漏洞复现 | WordPress 4.2.0-4.5.1 flashmediaelement.swf 反射型 XSS

漏洞复现 | WordPress 4.2.0-4.5.1 flashmediaelement.swf 反射型 XSS

一、下载测试源码进行搭建

搭建教程:http://www.internetke.com/website/webinfo/2015/1217/1855.html

二、分析源码

https://github.com/mediaelement/mediaelement/blob/2.18.1/src/
https://midzer0.github.io/2016/wordpress-4.5.1-xss/

1、Vulnerable Output

首先来看存在漏洞的输出, 99%的Flash XSS都是由于ExternalInterface.call函数的参数注入导致的, 当然本次也不例外. 拿到源码之后, 第一件事就是看看源码中出现了几次调用ExternalInterface.call, 对应的参数是否都是可控的. 在排除了几个参数不可控或者已经做了对应防注入的调用, 唯一剩下的就是下面的代码。

public class Log {
   private static const LEVEL_INFO : String = "INFO:";
   private static const LEVEL_DEBUG : String = "DEBUG:";
   private static const LEVEL_WARN : String = "WARN:";
   private static const LEVEL_ERROR : String = "ERROR:";
   public staticfunction info(message : *) : void {
     if (HLSSettings.logInfo) outputlog(LEVEL_INFO, String(message)); };
   public static functiondebug(message : *) : void {
     if (HLSSettings.logDebug) outputlog(LEVEL_DEBUG, String(message)); };
   public static functiondebug2(message : *) : void {
     if (HLSSettings.logDebug2) outputlog(LEVEL_DEBUG, String(message)); };
   public static functionwarn(message : *) : void {
     if (HLSSettings.logWarn) outputlog(LEVEL_WARN, String(message)); };
   public static functionerror(message : *) : void {
     if (HLSSettings.logError) outputlog(LEVEL_ERROR, String(message)); };
     /** Log a message to the console. **/
   private static function outputlog(level : String, message : String) : void {
     if (ExternalInterface.available) ExternalInterface.call('console.log', level + message);
     else trace(level + message);
     }
};

只要攻击者能够控制传入Log类下5个静态方法的参数, 就可以触发XSS. 在默认的HLSSettings里面(同时也是WordPress里flashmediaelement.swf的设置), 只有logInfo/logError/logWarn这三个属性被设置为true, 所以我们着重跟踪Log.info/Log.warn/Log.error这三个方法. 通过跟踪代码发现, HLS类中的dispatchEvent方法调用了Log.error这个方法, 传入的参数是HLSEvent类的error属性。 通过跟踪代码发现, HLS类中的dispatchEvent方法调用了Log.error这个方法, 传入的参数是HLSEvent类的error属性。

/** Forward internal errors. **/
override public function dispatchEvent(event : Event) : Boolean {
 if (event.type == HLSEvent.ERROR) {
   CONFIG::LOGGING {
     Log.error((event as HLSEvent).error);
    }
    _hlsNetStream.close();
  }
  returnsuper.dispatchEvent(event);
};

继续查看HLSEvent类中error属性的定义

public class HLSEvent extends Event {
/* ... */ /** The error message. **/
public var error : HLSError;
/* ... *//** Assign event parameter and dispatch. **/
public function HLSEvent(type : String, parameter : *=null) {
 switch(type) {
 /* ... */
 case HLSEvent.ERROR:
   error = parameter as HLSError;
   break;
   /* ... */
   }
   super(type, false, false);
 };
}

继续看HLSError的定义

  public class HLSError {
 public static const OTHER_ERROR : int = 0;
 public static const MANIFEST_LOADING_CROSSDOMAIN_ERROR : int = 1;
 public static const MANIFEST_LOADING_IO_ERROR : int = 2;
 public static const MANIFEST_PARSING_ERROR : int = 3;
 public static const FRAGMENT_LOADING_CROSSDOMAIN_ERROR : int = 4;
 public static const FRAGMENT_LOADING_ERROR : int = 5;
 public static const FRAGMENT_PARSING_ERROR : int = 6;
 public static const KEY_LOADING_CROSSDOMAIN_ERROR : int = 7;
 public static const KEY_LOADING_ERROR : int = 8;
 public static const KEY_PARSING_ERROR : int = 9;
 public static constTAG_APPENDING_ERROR : int = 10; private var _code : int;
 private var _url : String;
 private var _msg : String; public function HLSError(code : int, url : String, msg : String) {   _code = code; _url = url; _msg = msg; }
 public function get code() : int {
     return _code;
    }
  public function get msg() : String {
     return _msg;
    }
 public function get url() : String {
     return _url;
    }
 public function toString() : String {
     return "HLSError(code/url/msg)=" + _code + "/" + _url + "/" + _m  sg;    
     }
 }

toString的方法会将HLSError类中的_code/_url/_msg三个属性输出, 这三个参数均是通过构造函数从外部传入, 并通过变量名来推测, 很有可能是攻击者可控的。

我们再缕一遍XSS攻击中可能的数据流。

攻击者控制的部分参数传入HLSError的构造函数的url/msg参数, 生成一个恶意的HLSError对象a, 这个恶意对象a又作为parameter参数传入了HLSEvent的构造函数, 生成一个恶意的HLSEvent对象b, 最后, 恶意对象b作为event参数被传入dispatchEvent函数, 进入Log.error时被隐式转换为字符串类型, 触发了toString方法, 对应的返回值传入了ExternalInterface.call函数, 导致XSS.

2、Evil Input

再回过头来看看输入端. flashmediaelement.swf对外部的输入有两层防御. 第一层, 它会检查所有的参数是否包含恶意字符, 如果包含, 则直接返回终止执行; 第二层, 它会检查外部传入的参数是否是在URL中的QueryString出现过, 如果是, 则删除该部分, 避免直接通过URL传参。

public class FlashMediaElement extends MovieClip { public function FlashMediaElement() {
   // 第一层, 检查参数是否包含非法字符
   checkFlashVars(loaderInfo.parameters);   if (securityIssue) {
     return;
   }   // 第二层, 忽略所有从URL中传入的参数
   var params:Object, pos:int, query:Object;
   params = LoaderInfo(this.root.loaderInfo).parameters;
   pos = root.loaderInfo.url.indexOf('?');
   if (pos !== -1) {
     query = parseStr(root.loaderInfo.url.substr(pos + 1));     for (var key:String in params) {
       if (query.hasOwnProperty(trim(key))) {
         delete params[key];
       }
     }
   }
   /* ... */
 } /* ... */
 private function checkFlashVars(p:Object):void {
   var i:Number = 0;
   for (var s:String in p) {
     if (isIllegalChar(p[s], s === 'file')) {
       securityIssue = true; // Illegal char found
     }
     i++;
   }
   if(i === 0 || securityIssue) {
     directAccess = true;
   }
 } /* ... */
 private function isIllegalChar(s:String, isUrl:Boolean):Boolean {
   var illegals:String = "' \" ( ) { } * + \\ < >";
   if(isUrl) {
     illegals = "\" { } \\ < >";
   }
   if(Boolean(s)) { // Otherwise exception if parameter null.
     for each (var illegal:String in illegals.split(' ')) {
       if(s.indexOf(illegal) >= 0) {
         return true; // Illegal char found
       }
     }
   }
   return false;
 } /* ... */
 private static function parseStr (str:String) : Object {
   var hash:Object = {},
     arr1:Array, arr2:Array;   str = unescape(str).replace(/\+/g, " ");   arr1 = str.split('&');
   if (!arr1.length) {
     return {};
   }   for (var i:uint = 0, length:uint = arr1.length; i < length; i++) {
     arr2 = arr1[i].split('=');
     if (!arr2.length) {
       continue;
     }
     hash[trim(arr2[0])] = trim(arr2[1]);
   }
   return hash;
 }
}

对于第二层防止直接从URL传参的防御, 可以利用Flash Player对URL参数的解析和flashmediaelement.swf代码中对URL参数的解析的差异进行绕过. Flash Player会丢弃URL里%后非16进制的字符, 所以a.swf?a%s=b对于Flash Player来说是给a赋值b, 但对于flashmediaelement.swf中的代码逻辑而言, 是给a%s赋值b, 这样就可以利用两者的不一致性绕过这一检测. 但对于第一层的检测, 目前没有更好的办法进行绕过, 只能查看flashmediaelement.swf是否会通过读取传入的URL中的内容, 给目标参数赋值, 如果可以的话, 那么我们就可以通过污染URL的内容, 而不是URL本身, 执行参数注入, 达到绕过第一层检测的目的。 通过上述的分析, 查找所有调用dispatchEvent函数的地方, 分析其传入的参数, 发现如下:

private function _fragLoadErrorHandler(event : ErrorEvent) : void {
   if (event is SecurityErrorEvent) {
       var txt : String = "Cannot load fragment: crossdomain access denied:" + event.text;
       var hlsError : HLSError = new HLSError(HLSError.FRAGMENT_LOADING_CROSSDOMAIN_ERROR, _frag_current.url, txt);
       _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
   } else {
       _fraghandleIOError("HTTP status:" + _frag_load_status + ",msg:" + event.text);
   }
};

上述代码是加载fragment时的一个处理错误的handler, 当加载的fragment为一外域的资源, 且该域的crossdomain.xml不允许swf所在域与其通信时, 会抛出一个hlsError的实例, 其中的参数就包含试图加载的fragment的url. 通过查询M3U8的文件格式, 我们可以通过文件内容指定加载的fragment的URL。

三、构造POC

http://127.0.0.1/wordpress/wp-includes/js/mediaelement/flashmediaelement.swf?jsinitfunctio%gn=alert'1'

本文分享自微信公众号 - TeamsSix(OldCat0111),作者:Teamssix

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 经验总结 | 常见的HTTP方法

    HTTP1.1新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT方法。

    TeamsSix
  • 漏洞复现 | WordPress _4.5 SSRF

    TeamsSix
  • 漏洞复现 | DNS域传送漏洞

    注:本文中使用的域名是不存在DNS域传送漏洞的,本文仅用作技术交流学习用途,严禁将该文内容用于违法行为。

    TeamsSix
  • 程序员保证能笑出腹肌

    客户需求 vs 最终产品 ? requirements vs. implementation 程序员的一天 ? The Programmers life 寂寞...

    前朝楚水
  • 原 ObjectToJson实现

    魂祭心
  • Java微信公众平台开发(九)--微信自定义菜单的创建实现 (一)使用网页调试工具生成菜单(二)采用代码实现菜单的生成

      自定义菜单这个功能在我们普通的编辑模式下是可以直接在后台编辑的,但是一旦我们进入开发模式之后我们的自定义菜单就需要自己用代码实现,所以对于刚开始接触的人来说...

    用户2417870
  • Java微信开发_Exception_01_The type org.xmlpull.v1.XmlPullParser cannot be resolved. It is indirectly ref

    这个异常是在做微信开发时出现的,在引入了XStream的jar包之后,还是出现了如下错误信息:

    shirayner
  • Code Snippet for Media on Android

    A few days ago,I have wrote down this post http://androidyue.github.io/blog/2014...

    技术小黑屋
  • Android OkGo网络请求库 自定义回调支持带泛型的对象

    这里写了两种方式请求接口 (文中用了Java和Kotlin两种语言 没有特殊标示的都是Java)

    剑行者
  • Vue 项目实战上传文件与接口OPTIONS

    在项目的开发过程中难免会遇到许多的坑,寻找答案成为了至关重要的一步,职场中解决问题的能力是必要切重要的,有些问题网上给的答案大多都是千篇一律重复性东西太多,而且...

    六小登登

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动