WordPress 4.2.0-4.5.1 flashmediaelement.swf 反射型 XSS 漏洞附poc

2016年5月16日 282点热度 0人点赞 0条评论

图片

WordPress于2016年5月6号发布新版本4.5.2,其中修了一个反射型XSS漏洞,直接影响4.2.0至 4.5.1版本。

      图片

       该反射型 XSS 源 于 WordPress 一个处理媒体的插件 MediaElement,攻击者能够构 造恶意 Payload,使之解析媒体文件时触发 XSS

  一般来说, 开源CMS所使用的Flash文件也都来自其他的开源项目, 所以直接获取WordPress所使用的SWF文件的actionscript源码并不是一件困难的事情. 以本次漏洞的目标文件wp-includes/js/mediaelement/flashmediaelement.swf为例, 通过查看Github上与这个文件相关的最新的commit, 可以发现flashmediaelement.swf来自于一个开源项目MediaElement, 版本号为2.18.1. 恰好, 这个开源项目也托管在Github上, 那么直接下载它对应版本的Release. 同时, 在对MediaElement的源码目录初步研究后发现, 该项目也引用了另外一个开源项目flashls的swc文件, 还贴心低注释了版本号为4.3.4, 该swc文件对应版本的源码同样也可以在Github上找到Release. 这样的好处一是可以避免反编译过程中本地变量名混淆/代码块失踪的尴尬, 二是可以通过手动添加debug信息再本机编译, 更快地消化代码分支结构及数据流.

  首先来看存在漏洞的输出, 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 static function info(message : *) : void {
        if (HLSSettings.logInfo)
            outputlog(LEVEL_INFO, String(message));
    };
 
    public static function debug(message : *) : void {
        if (HLSSettings.logDebug)
            outputlog(LEVEL_DEBUG, String(message));
    };
 
    public static function debug2(message : *) : void {
        if (HLSSettings.logDebug2)
            outputlog(LEVEL_DEBUG, String(message));
    };
 
    public static function warn(message : *) : void {
        if (HLSSettings.logWarn)
            outputlog(LEVEL_WARN, String(message));
    };
 
    public static function error(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属性.

/** 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();
    }
    return super.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 const TAG_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 + "/" + _msg;
    }
}

  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.

  POC

#!/usr/bin/env python
# coding: utf-8
 
import urlparse
import hashlib
 
from pocsuite.api.request import req
from pocsuite.api.poc import register
from pocsuite.api.poc import Output, POCBase
 
 
class TestPOC(POCBase):
    vulID = '91515'  # ssvid
    version = '1.0'
    author = ['Fooying']
    vulDate = ''
    createDate = '2016-05-10'
    updateDate = '2016-05-10'
    references = ['http://www.seebug.org/vuldb/ssvid-91515']
    name = 'WordPress 4.2.0-4.5.1 反射型 XSS 漏洞 '
    appPowerLink = 'https://wordpress.org/'
    appName = 'WordPress'
    appVersion = '(4.2.0<=version<=4.5.1'
    vulType = 'XSS'
    desc = '''
      WordPress 于 2016 年 5 月 6 号发布新版本 4.5.2,其中修了一 个反射型 XSS 漏洞,直接影响 4.2.0 至 4.5.1 版本。该反射型 XSS 源 于 WordPress 一个处理媒体的插件 MediaElement,攻击者能够构 造恶意 Payload,使之解析媒体文件时触发 XSS.
    '''
    samples = ['']
    install_requires = ['']
 
 
    def get_md5(self, file_content):
      md5obj = hashlib.md5()
      md5obj.update(file_content)
      hash = md5obj.hexdigest()
      return hash
 
    def _attack(self):
        result = {}
        self._verify()
 
        return self.parse_output(result)
 
    def _verify(self):
        result = {}
        url = urlparse.urljoin(self.url, '/wp-includes/js/mediaelement/flashmediaelement.swf')
        res = req.get(url)
        md5_num = self.get_md5(res.content)
        print md5_num
 
        match_md5 = (
            "079d5276459e0e3526afbdb7e1017037",
            "2400c991b8473d44a7edc75605496760",
            "42c6680f8035fc10855ee2e559c9cc78",
            "a77bd46c3904a70f0e4ed6f3f714099a",
            "bed216acd6fb5318c139087a0a9d6b4d",
            "e61c004611ce5cc855a3b2ab3e89602d",
        )
        if res.status_code == 200 and md5_num in match_md5:
            result['VerifyInfo'] = {}
            result['VerifyInfo']['URL'] = url
        return self.parse_output(result)
 
    def parse_output(self, result):
        #parse output
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('Internet nothing returned')
        return output
 
 
register(TestPOC)


图片

长按公众号,可“置顶

----------------------------------

要闻、干货、原创、专业
关注“黑白之道” 微信:i77169
华夏黑客同盟我们坚持,自由,免费,共享!


图片

25970WordPress 4.2.0-4.5.1 flashmediaelement.swf 反射型 XSS 漏洞附poc

root

这个人很懒,什么都没留下

文章评论