RSS Feed
更好更安全的互联网
  • 波场 DeFi 项目 Myrose 无法提现 USDT 技术分析

    2020-11-03

    作者:昏鸦,Al1ex@知道创宇404区块链安全研究团队
    时间:2020年9月16日

    事件起因

    2020年9月14日晚20:00点,未经安全审计的波场最新Defi项目Myrose.finance登陆Tokenpocket钱包,首批支持JST、USDT、SUN、DACC挖矿,并将逐步开通ZEUS、PEARL、CRT等的挖矿,整个挖矿周期将共计产出8400枚ROSE,预计将分发给至少3000名矿工,ROSE定位于波场DeFi领域的基础资产,不断为持有者创造经济价值。

    项目上线之后引来了众多的用户(高达5700多人)参与挖矿,好景不长,在20:09左右有用户在Telegram"Rose中文社区群"中发文表示USDT无法提现:

    Telegram

    截止发文为止,无法提现的USDT数量高达6,997,184.377651 USDT(约700万USDT),随后官方下线USDT挖矿项目。

    https://tronscan.io/#/contract/TM9797VRM66LyKXq2TbxP1sNmuQWBrsnYw/token-balances
    total_value

    分析复现

    我们直接通过模拟合约在remix上测试。

    USDT模拟测试合约代码如下,USDT_Ethereum和USDT_Tron分别模拟两个不同平台的USDT代币合约,分别代表transfer函数有显式return true和无显式return true

    Myrose模拟测试合约代码如下:

    Remix部署USDT_EthereumUSDT_TronTest三个合约。

    调用USDT_Ethereum和USDT_Tron的mint函数给Test合约地址增添一些代币。

    然后调用Test合约的withdraw函数提现测试。

    success-and-false

    可以看到USDT_Ethereum提现成功,USDT_Tron提现失败。

    失败的回滚信息中,正是safeTransfer函数中对最后返回值的校验。

    Missing Return Value Bug

    上文的合约模拟实验揭示了以太坊与波场两个不同平台下USDT代币合约中transfer函数关于返回值处理差异性带来的安全风险,而关于"missing return value bug"这一个问题,早在2018年就有研究人员在Medium上公开讨论过,只不过是针对以太坊的,这里对以太坊中的"missing return value bug"问题做一个简单的介绍:

    ERC20标准是以太坊平台上最常见的Token标准,ERC20被定义为一个接口,该接口指定在符合ERC20的智能合同中必须实现哪些功能和事件。目前,主要的接口如下所示:

    在ERC20的开发过程中,有研究人员对于ERC20合约中的transfer函数的正确返回值进行了讨论,主要分为两个阵营:一方认为,如果transfer函数允许在调用合约中处理Failed error,那么应该在被调用合约中返回false值,另一方声称,在无法确保安全的情况下,ERC20应该revert交易,关于这个问题在当时被认为都是符合ERC20标准的,并未达成一致。

    事实证明,很大比例的ERC20 Token在传递函数的返回值方面表现出了另一种特殊的方式,有些智能合约的Transfer函数不返回任何东西,对应的函数接口大致如下:

    那么符合ERC20标准的接口的合约试图与不符合ERC20的合约进行交互,会发生什么呢?下面我们通过一个合约示例来做解释说明:

    在solidity中,函数选择器是从它的函数名和输入参数的类型中派生出来的:

    函数的返回值不是函数选择器的一部分,因此,没有返回值的函数transfer()和函数transfer()返回(bool)具有相同的函数选择器,但它们仍然不同,由于缺少返回值,编译器不会接受transfer()函数作为令牌接口的实现,所以Goodtoken是Token接口的实现,而Badtoken不是。

    当我们通过合约去外部调用BadToken时,Bad token会处理该transfer调用,并且不返回布尔返回值,之后调用合约会在内存中查找返回值,但是由于被调用的合约中的Transfer函数没有写返回值,所以它会将在这个内存位置找到的任何内容作为外部调用的返回值。

    完全巧合的是,因为调用方期望返回值的内存槽与存储调用的函数选择器的内存槽重叠,这被EVM解释为返回值“真”。因此,完全是运气使然,EVM的表现就像程序员们希望它的表现一样。

    自从去年10月拜占庭硬叉以来,EVM有了一个新的操作码,叫做returndatasize,这个操作码存储(顾名思义)外部调用返回数据的大小,这是一个非常有用的操作码,因为它允许在函数调用中返回动态大小的数组。

    这个操作码在solidity 0.4.22更新中被采用,现在,代码在外部调用后检查返回值的大小,并在返回数据比预期的短的情况下revert事务,这比从某个内存插槽中读取数据安全得多,但是这种新的行为对于我们的BadToken来说是一个巨大的问题。

    如上所述,最大的风险是用solc ≥ 0.4.22编译的智能合约(预期为ERC0接口)将无法与我们的Badtokens交互,这可能意味着发送到这样的合约的Token将永远停留在那里,即使该合约具有转移ERC 20 Token的功能。

    类似问题的合约:

    有两种方法可以修复这个错误:

    第一种:受影响的Token合约开放团队需要修改他们的合约,这可以通过重新部署Token合约或者更新合约来完成(如果有合约更新逻辑设计)。

    第二种:重新包装Bad Transfer函数,对于这种包装有不同的建议,例如:

    另一方面,正在编写ERC 20合约的开发人员需要意识到这个错误,这样他们就可以预料到BadToken的意外行为并处理它们,这可以通过预期BadER 20接口并在调用后检查返回数据来确定我们调用的是Godtoken还是BadToken来实现:

    事件总结

    造成本次事件的主要原因还是在于波场USDT的transfer函数未使用TIP20规范的写法导致函数在执行时未返回对应的值,最终返回默认的false,从而导致在使用safeTransfer调用USDT的transfer时永远都只返回false,导致用户无法提现。

    所以,在波场部署有关USDT的合约,需要注意额外针对USDT合约进行适配,上线前务必做好充足的审计与测试,尽可能减少意外事件的发生


    智能合约审计服务

    针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

    知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html
    联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)欢迎扫码咨询:

    区块链行业安全解决方案

    黑客通过DDoS攻击、CC攻击、系统漏洞、代码漏洞、业务流程漏洞、API-Key漏洞等进行攻击和入侵,给区块链项目的管理运营团队及用户造成巨大的经济损失。知道创宇十余年安全经验,凭借多重防护+云端大数据技术,为区块链应用提供专属安全解决方案。欢迎扫码咨询:

    参考链接

    [1] Missing-Return-Value-Bug
    https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1337/

    作者:江 | Categories:安全科普技术分享 | Tags:
  • Weblogic12c T3 协议安全漫谈

    2020-11-03

    作者:laker@知道创宇404实验室
    时间:2020年8月28日

    前言

    WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件。 主要用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。 近几年频繁爆发出多个RCE漏洞,而在今年,其T3协议被频繁攻击和发布补丁与绕过,本文主要对今年来由T3协议入口所产生的多个RCE漏洞进行分析,其中主要包括CVE-2020-2555、 CVE-2020-2883(bypass CVE-2020-2555补丁)、 CVE-2020-14645 (bypass CVE-2020-2883补丁)。

    环境搭建

    两种搭建环境,第一种是利用docker搭建环境,利用IDEA动态调试,可参考[1],本文调试建议使用Weblogic Server版本12.2.1.4.0,对于该版本的docker文件在https://hub.docker.com/_/oracle-weblogic-server-12c?tab=reviews

    第二种是在官方下载安装包[2],并安装安装指引进行安装[3]

    我们采用第二种进行。在Oracle官网下载后进行安装。

    java.exe -jar C:\Users\Administrator\Desktop\fmw_12.2.1.4.0_wls_lite_generic.jar

    image-20200825230040761

    安装完后导入IDEA再进行配置即可。

    漏洞版本

    CVE-2020-2555 && CVE-2020-2883(bypass CVE-2020-2555补丁)

    CVE-2020-14645 (bypass CVE-2020-2883补丁)

    漏洞成因

    简单理解该漏洞成因便是Weblogic 默认开启 T3 协议,攻击者可利用T3协议进行反序列化漏洞实现远程代码执行

    基于代码的漏洞介绍:CVE-2020-2555主要源于在coherence.jar存在着用于gadget构造的类(反序列化构造类),并且利用weblogic默认存在的T3协议进行传输和解析进而导致weblogic服务器反序列化恶意代码最后执行攻击语句。

    T3协议

    WebLogic Server 中的 RMI 通信使用 T3 协议在 WebLogic Server 和其他 Java 程序(包括客户端及其他 WebLogic Server 实例)间传输数据。同时

    T3协议包括

    1. 请求包头 2. 请求主体

    因此,在T3数据包构造过程中,需要发送两部分的数据

    • 请求包头,形如

    t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://localhost:7001 LP:DOMAIN 1

    \n结束

    同时,我们发送t3的请求包,可用于刺探服务器weblogic版本,该服务器会将自身版本进行响应,形如

    HELO:12.1.3.0 false AS:2048 HL:19 MS:10000000

    • 序列化数据部分,序列化部分的构成方式有两种:
    • 第一种生成方式为,将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
    • 第二种生成方式为,将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。

    具体T3的数据结构可参考http://drops.xmd5.com/static/drops/web-13470.html,这里我们不关注T3具体数据结构,而是将重心放在T3的反序列化漏洞上。

    综上,为实现T3协议的JAVA序列化包,需要在T3数据结构头部发送后在其中插入序列化恶意数据,该恶意数据与JAVA的原生ObjectOutputStream数据类型是一样的,然后发送T3数据结构尾部。

    CVE-2020-2555

    由于CVE-2020-2883是对2555补丁的绕过,我们先看看原来的CVE-2020-2555利用链。

    我们使用12.2.1.4.0对此进行调试。

    根据已知的一些漏洞信息

    漏洞的产生点是 coherence.jar 包中的 LimitFilter 函数,我们将相关漏洞包coherence.jar和tangsol.jar 添加到库函数并反编译add as library

    在server\lib\console-ext\autodeploy\tangosol.jar!\com\tangosol\util\filter\LimitFilter.class#toString下一些断点,调试并发送POC。

    根据堆栈信息,Weblogic收到POC的数据后,对其进行分发后对T3的数据段部分进行了反序列化还原操作,进而产生了该漏洞的入口。

    1598413132395

    利用 BadAttributeValueExpException类实例可以用来调用任意类的toString()方法 ,这里可能有小伙伴会好奇,为什么这个类的实例能调用在任意类的toString()方法?原因如下:

    利用 java.io.ObjectInputStream反序列化一个类时会默认调用该类的readObject方法。

    javax.management.BadAttributeValueExpException#readObject方法会对传入的ObjectInputStream实例提取其val属性的值(这也是为什么我们要将恶意对象注入到val属性)。

    然后将该值进行判断(valObj受到我们的控制,就是我们注入val属性的对象),我们需要进入的是val = valObj.toString();进而调用控制的valObj对象的toString方法:

    1598422858241

    这里的System.getSecurityManager需要为null才会进入toString逻辑。

    1598433641810

    因此我们可以操控valObj成为任意对象并对让其使用toString方法,这里我们选择的恶意宿主是LimitFilter类,原因如下:

    了解到LimitFilter类会被我们操作执行toString方法,其toString方法存在如下操作

    注意到在LimitFilter.class#toString方法中, 获取到该类的m_comparator成员属性后,转换为(ValueExtractor)对象并调用自身extract方法 :

    1598424104916

    这里可能会有疑问,如何去控制m_comparator成员属性呢?因为这个类其实就是我们自己写的恶意类,当然可以控制其成员属性了。

    到这里,我们就可以控制我们构造的恶意类里面m_comparator成员的extract方法了,而m_comparator成员可控。因此我们可以控制任意类的extract方法了。而后我们选取的利用类是com.tangosol.util.extractor.ChainedExtractor#extract,因为它的extract方法是这样的,该方法会将this.getExtractors返回的数组依次调extract并返回给oTarget:

    1598427153167

    this.getExtractors方法继承自AbstractCompositeExtractor,返回成员属性this.m_aExtractor

    1598427817664

    而这个this.m_aExtractor则来自原始方法AbstractCompositeExtractor(),即是初始化该示例的时候传入的:

    1598428298804

    那么可以理解为,com.tangosol.util.extractor.ChainedExtractor类会依次对 初始化实例时调用传入的ValueExtractor[]类型的列表 调用extract方法。

    至此我们便有了调用多个对象extract的能力。

    又是一个疑问,这里都是调用extract方法,怎么才能从extract到Runtime.getRuntime.exec()的调用呢?答案是反射。如果我们可以找到一个类,该类的extract方法可控并且传入参数会被顺序进行反射,那么就可以通过控制extract和传入参数进行RCE了。这个类是com.tangosol.util.extractor.ReflectionExtractor#extract

    1598429645257

    反射的形式这里不细讲了,有兴趣的可以参考[4]

    这里需要形成需要被调用的方法.invoke(被调用类class, 执行的代码)

    诸如

    然后利用com.tangosol.util.extractor.ReflectionExtractor#extract进行传入构造再invoke。

    综上,我们构造如下代码片段。

    1598432563582

    POC逻辑

    1.组装ReflectionExtractor成为列表赋值给valueExtractors(ReflectionExtractor有反射的extract函数)。

    1598433412916

    2.然后通过放入ChainedExtractor(列表依次extract) (ChainedExtractor有列表extract函数)。

    1598433368921

    3.然后通过放入limitFilter(limitFilter可让ChainedExtractor使用extract)。

    1598433840870

    4.然后通过放入BadAttributeValueExpException(令limitFilter使用toString)。

    1598433747099

    于是构成了该利用链。

    最后序列化数据源代码大致如下:

    CVE-2020-2555补丁

    本地补丁检测方式:

    1598434137149

    可以看到,Oracle官方在一月发布了CVE-2020-2555的补丁[5]

    该补丁需要用户持有正版软件的许可账号,使用该账号登陆官方网站方可下载。

    该补丁阻断了LimitFilter传入的对象使用extract方法.

    CVE-2020-2883

    后续 VNPT ISC的研究员Quynh Le向ZDI提交了一个漏洞][6]

    该补丁阻断了LimitFilter,也就是阻断了从readObject ---> toString ----> extract的路径

    然而该研究员找到了另一个路径去连接readObject ----> extract

    java.util.PriorityQueue.readObject

    https://blog.csdn.net/systemino/article/details/106117659
    https://github.com/Y4er/CVE-2020-2883

    java.util.PriorityQueue#readObject会调用heapify函数,如下图,具体利用时使用双参构造方法,我们看看文档的描述。

    1598494724625

    使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器对元素进行排序。

    1598499536733

    这里我们指定的比较器是 ExtractorComparator ,初始容量为2

    PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));

    显而易见,这里我们调用的ExtractorComparator这个比较器compare函数存在着extract方法。

    1598504648966

    o1和o2的值:

    1598500033271

    让m_extractor对象使用extract方法。这里操控m_extractor的方法就是反射(具体前面有)。

    1598505115016

    于是乎,和前面一样的,这个m_extractor对象被修改为数组以达到多个对象调用extract方法。然后就进入到com.tangosol.util.extractor.ChainedExtractor。

    1598500176069

    至此,完成了从readObject ---> compare ----> extract的连接。后续调用就和CVE-2020-2555相同了。

    调用链:

    1598505333220

    POC可以参考https://github.com/Y4er/CVE-2020-2883/blob/master/CVE_2020_2883.java

    CVE-2020-2883补丁

    Oracle官方对于CVE-2020-2883的补丁[7]将 extract 方法存在危险操作的 MvelExtractor 和 ReflectionExtractor 两个类加入到了黑名单中(ReflectionExtractor与MvelExtractor 有反射的extract函数)。

    CVE-2020-14645

    ReflectionExtractor与MvelExtractor 被加入了黑名单,如果我们能找到一个类(类的extract函数中有可控的反射操作),便可继续该链条(这里我们有的是readObject ---> compare ----> extract ---> 多个类的extract --> extract中可控反射)。

    可采用这个类com.tangosol.util.extractor.UniversalExtractor#extract。

    1598505960398

    遗憾的是其被transient修饰,被transient关键字修饰的变量不再能被序列化。

    1598506105053

    但是此处在75行对oTarget传入了extractComplex方法。

    1598506370964

    又见希望,该方法中也存在可控反射。

    1598511058884

    值得注意的是,两条method获取方法只能从第一个if去取,原因是else中需要确保fProperty==false, 然而184行中m_fMethod存在transient修饰,被transient关键字修饰的变量不再能被序列化因此无法构建序列化字节流。

    1598511307172
    1598511434564

    而在if条件中收到参数影响有sBeanAttribute--> sCName--->this.getCanonicalName(),这里做的工作就是187行对sCName首字母大写并将其与BEAN_ACCESSOR_PREFIXES列表的值进行拼接,取到则停止返回method。

    1598512105619

    那么BEAN_ACCESSOR_PREFIXES列表是什么样的呢?其存储了get和is两个字符串。因此,在拼接的时候,只能形成get___或者is___这样的方法调用。

    1598514396005

    于是可以利用 com.sun.rowset.JdbcRowSetImpl#getDatabaseMetaData()方法进行反射调用构建JNDI注入,这也是为什么之前都是利用原有的ReflectionExtractor直接反射到Runtime类执行而这里却只能发起JNDI请求在低版本的JDk来执行代码。

    POC逻辑

    在POC构造上,先初始化JDBC对象,设置this.m_sName参数为getDatabaseMetaData()

    1598517138292

    然后是关键点的sName会被去掉前缀,因此后面要进行拼接。

    1598517012173

    依旧让queue使用ExtractorComparator这个比较器。

    对该queue实例设置成员变量(反射)。此处让该实例queue拥有两个成员变量,一个是queue,值为new Object[]{rowSet, rowSet},一个是size,值为2。这里用了写的Reflections工具类,当然也可以一点点用反射进行设置。

    POC可以参考https://github.com/Y4er/CVE-2020-2883/blob/master/CVE_2020_2883.java

    收到的LDAP请求:

    1598517751999

    该CVE漏洞利用服务器有JDK条件,且只能在Weblogic Server 12.2.1.4.*存在。

    LDAP: < JDK6u201/7u191/8u182/11.0.1 RMI: < JDK6u141/7u131/8u121

    参考文章

    [1]利用docker远程动态调试weblogic

    https://blog.csdn.net/sojrs_sec/article/details/103237150

    [2]官方下载

    https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html

    [3]官方安装指引

    https://docs.oracle.com/en/middleware/fusion-middleware/12.2.1.4/wlsig/installing-oracle-weblogic-server-and-coherence-software.html#GUID-5C7D4437-46A2-45A2-85F3-738B0DFE9AE2

    [4] JAVA 反射

    https://www.jianshu.com/p/9be58ee20dee

    [5]patch for CVE-2020-2555

    https://support.oracle.com/portal/oracleSearch.html?CVE-2020-2555

    [6]Quynh Le向ZDI提交漏洞

    https://www.zerodayinitiative.com/advisories/ZDI-20-570/

    [7]patch for CVE-2020-2883

    https://support.oracle.com/portal/oracleSearch.html?CVE-2020-2883

    https://www.oracle.com/security-alerts/cpuapr2020.html


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1321/

    作者:江 | Categories:安全科普技术分享 | Tags:
  • 开源=安全?RVN 盗币事件复盘

    2020-11-03

    作者:ACce1er4t0r@知道创宇404区块链安全研究团队
    时间:2020年7月22日

    在7月15号,v2ex上突然出现了一个这样标题的帖子:三行代码就赚走 4000w RMB,还能这么玩?

    帖子内容里,攻击者仅仅只用了短短的几行代码,就成功的获利千万RMB,那么他是怎么做到的呢?

    让我们来回顾一下这次事件。

    事件回顾

    2020年1月16日,开源项目Ravencoin接到这么一则pull request

    image.png

    代码中,提交者将原本定义模糊的报错细分,让人们能够更直观的了解究竟出了什么错误,看起来是在优化项目,但是,事实真是这样么?

    2020年6月29日,Solus Explorer开发团队一位程序员在修bug后同步数据时发现了一个suspected transactions with unbalanced VOUTs被Explorer标记出,之后他检查RVN时发现RVN大约被增发了约275,000,000,并发现了大量可疑地reissue asset Transaction,这些交易不仅仅有Asset Amount,而且获得了RVN。在他发现这一事件后,马上和他的团队一起将事件报告给Ravencoin团队。

    2020年7月3日,Ravencoin团队向社区发布紧急更新

    2020年7月4日,13:26:27 (UTC),Ravencoin团队对区块强制更新了新协议,并确认总增发量为 301,804,400 RVN,即为3.01亿RVN.

    2020年7月5月,Ravencoin团队宣布紧急事件结束

    2020年7月8日,Ravencoin团队公布事件

    image.png

    事件原理

    在解释原理前,我们不妨先重新看看WindowsCryptoDev提交的代码

    这是一段Ravencoin中用于验证的逻辑代码。

    简单来说,提交者改变了CheckTransaction对Asset验证的判断,将原本isAsset && txout.nValue != 0的条件更改为下面的条件:

    1. isAsset && nType == TX_TRANSFER_ASSET && txout.nValue != 0
    2. isAsset && nType == TX_NEW_ASSET && txout.nValue != 0

    这段代码本身利用了开源社区PR的风格(在开源社区中,如果开发者发现提交的PR无关实际逻辑,则不会过度关注代码影响),看似只是细化了交易过程中返回的报错,使得正常使用功能的交易者更容易定位到错误,实则,通过忽略else语句,导致一个通用的限制条件被细化到了nType的两种常见情况下

    而代码中nTypt可能的值有如下:

    由于代码的改变,当nType == TX_REISSUE_ASSET时,txout.nValue可以不为0。

    通过对比正常的交易和存在问题的交易,我们也能验证这一观点。

    image.png

    在正常的Reissue操作中,我们需要向 Address RXReissueAssetXXXXXXXXXXXXXXVEFAWu支付100RVN,之后我们可以得到一个新的Amount为0的Address,如果新的Address的Amount不为0,那么将会返回bad-txns-asset-tx-amount-isn't-zero的错误信息(代码被更改前,修复后会返回bad-txns-asset-reissued-amount-isn't-zero的错误信息)

    image.png

    而攻击者修改了判断条件,导致了在CheckTransaction时并不会检测TX_REISSUE_ASSET,所以能够在Address的Amount不为0的情况下通过判断,最终实现增发RVN。

    看完代码后,我们点开这位叫做WindowsCryptoDev的用户的GitHub主页

    这是个在2020年1月15日新建的账号,为了伪造身份,起了个WindowsCryptoDev的id,并且同天建了个叫Windows的repo,最后的活动便是在1月16号向Ravencoin提交PR。

    而对于这个PR,项目团队的反馈也能印证我们的猜测。


    整个攻击流程如下:

    1. 2020年1月15日,攻击者伪造身份
    2. 1月16日,攻击者提交pull request
    3. 1月16日,当天pull request被合并
    4. 5月9日,攻击者开始通过持续制造非法Reissue Asset操作增发RVN,并通过多个平台转卖换为其他虚拟货币
    5. 6月29日,Solus Explorer开发团队一位程序员发现问题并上报
    6. 7月3日,Ravencoin团队向社区发布紧急更新,攻击者停止增发RVN
    7. 7月4日,13:26:27 (UTC),Ravencoin团队对区块强制更新了新协议
    8. 7月5月,Ravencoin团队宣布紧急事件结束
    9. 7月8日,Ravencoin团队公布事件

    至此,事件结束,最终,攻击者增发了近3亿的RVN。

    总结

    随着互联网时代的发展,开源文化逐渐从小众文化慢慢走向人们的视野中,人们渐渐开始认为开源社区给项目带来源源不断的活力,开源使得人人都可以提交请求、人人都可以提出想法,可以一定层度上提高代码的质量、增加社区的活跃度,形成一种正反馈,这使开源社区活力无限。

    但也因此,无数不怀好意的目光也随之投向了开源社区,或是因为攻击者蓄谋已久,抑或是因为贡献者无心之举,一些存在问题的代码被加入到开源项目中,他们有的直接被曝光被发现被修复,也有的甚至还隐藏在核心代码中深远着影响着各种依赖开源项目生存着的软件、硬件安全。

    开源有利亦有弊,攻击者也在渗透着越来越多开发过程中的不同维度,在经历了这次事件之后,你还能随意的接受开源项目中的PR吗?

    REF

    [1] 三行代码就赚走 4000w RMB,还能这么玩?

    https://s.v2ex.com/t/690286

    [2] commit

    https://github.com/RavenProject/Ravencoin/commit/d23f862a6afc17092ae31b67d96bc2738fe917d2

    [3] Solus Explorer - Address: Illegal Supply

    https://rvn.cryptoscope.io/address/?address=Illegal%20Supply

    [4] Ravencoin — Emergency Update

    https://medium.com/@tronblack/ravencoin-emergency-update-dece62255fd9/https://medium.com/@tronblack/ravencoin-emergency-update-dece62255fd9

    [5] Ravencoin — Emergency Ended

    https://medium.com/@tronblack/ravencoin-emergency-ended-3f3181a0f6d2/https://medium.com/@tronblack/ravencoin-emergency-ended-3f3181a0f6d2

    [6] The anatomy of Ravencoin exploit finding

    https://medium.com/@cryproscope/the-anatomy-of-ravencoin-exploit-finding-8fa4fe7547a9/https://medium.com/@cryproscope/the-anatomy-of-ravencoin-exploit-finding-8fa4fe7547a9

    [7] RavencoinVulnerability — WTF Happened?

    https://medium.com/@tronblack/ravencoin-post-vulnerability-fix-fb3a4bd70b7b/https://medium.com/@tronblack/ravencoin-post-vulnerability-fix-fb3a4bd70b7b


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1275/

    作者:江 | Categories:安全科普 | Tags:
  • WordPress 5.0 RCE 详细分析

    2019-02-27
    作者:LoRexxar'@知道创宇404实验室
    时间:2019年2月22日
    2月20号,RIPS团队在官网公开了一篇WordPress 5.0.0 Remote Code Execution,CVE编号CVE-2019-6977,文章中主要提到在author权限账号下,可以通过修改Post Meta变量覆盖、目录穿越写文件、模板包含3个漏洞构成一个RCE漏洞。

    但在原文中,作者只大致描述了漏洞原理,其中大量的漏洞细节被省略,甚至部分的利用和后端服务器也有相对应的关系,所以在复现的过程中遇到了各种问题,我们花了大量的时间分析代码,最终终于完全还原了该漏洞,其中部分关键利用点用了和原文有些许差异的利用方式(原文说的太含糊其辞,无法复现)。在下面的分析中,我会尽量按照复现过程中的思考方式及流程,以便读者理解。

    感谢在复现、分析过程中一起的小伙伴@Badcode,帮助我修改了很多错误的@Venenof7、@sysorem,给我提供了很多帮助:>

    漏洞要求

    在反复斟酌漏洞条件之后,我们最终把漏洞要求约束为

    影响包括windows、linux、mac在内的服务端,后端图片处理库为gd/imagick都受到影响,只不过利用难度有所差异。

    其中,原文提到只影响release 5.0.0版,但现在官网上可以下载的5.0.0已经修复该漏洞。实际在WordPress 5.1-alpha-44280更新后未更新的4.9.9~5.0.0的WordPress都受到该漏洞影响。

    漏洞复现

    下面的复现流程包含部分独家利用以及部分与原文不符的利用方式,后面的详情会解释原因。

    传图片

    改信息

    保留该数据包,并添加POST

    裁剪

    同理保留改数据包,并将POST改为下面的操作,其中nonce以及id不变

    触发需要的裁剪

    图片已经过去了

    包含,我们选择上传一个test.txt,然后再次修改信息,如前面

    点击查看附件页面,如果图片被裁剪之后仍保留敏感代码,则命令执行成功。

    详细分析

    下面我们详细分析一下整个利用过程,以及在各个部分踩的坑。我们可以简单的把漏洞利用链分为4个大部分。

    1、通过Post Meta变量覆盖,修改媒体库中图片的_wp_attached_file变量。

    这个漏洞是整个利用链的核心点,而WordPress的修复方式也主要是先修复了这个漏洞。WordPress很良心的在所有的release版本中都修复了这个问题(官网下载的5.0.0已经修复了),由于原文中曾提到整个利用链受到4.9.9和5.0.1的另一个安全补丁影响,所以只有5.0.0受影响。在分析还原WordPress的更新commit中,我们寻找到了这个漏洞的修复commit,并获得了受该漏洞影响的最新版本为WordPress commit <= 43bdb0e193955145a5ab1137890bb798bce5f0d2 (WordPress 5.1-alpha-44280)

    2、通过图片的裁剪功能,将裁剪后的图片写到任意目录下(目录穿越漏洞)

    在WordPress的设定中,图片路径可能会收到某个插件的影响而不存在,如果目标图片不在想要的路径下时,WordPress就会把文件路径拼接为形似http://127.0.0.1/wp-content/uploads/2019/02/2.jpg 的url链接,然后从url访问下载原图

    如果我们构造?或者#后面跟路径,就能造成获取图片位置和写入图片位置的不一致。。

    这部分最大问题在于,前端的裁剪功能并不是存在漏洞的函数,我们只能通过手动构造这个裁剪请求来完成。

    ps: 当后端图片库为Imagick时,Imagick的Readimage函数不能读取远程http协议的图片,需要https.

    3、通过Post Meta变量覆盖,设置_wp_page_template变量。

    这部分在原文中一笔带过,也是整个分析复现过程中最大的问题,现在公开的所有所谓的WordPress RCE分析,都绕开了这部分。其中有两个最重要的点:

    • 如何设置这个变量?
    • 如何触发这个模板引用?

    这个部分在下文中会详细解释。

    4、如何让图片在被裁剪过之后,保留或者出现包含php敏感代码。

    这部分就涉及到了后端图片库的问题,WordPress用到的后端图片处理库有两个,gd和imagick,其中默认优先使用imagick做处理。

    • imagick
      利用稍微比较简单,imagick不会处理图片中的exif部分。将敏感代码加入到exif部分就可以不会改动。
    • gd
      gd的利用就比较麻烦了,gd不但会处理图片的exif部分,还会删除图片中出现的php代码。除非攻击者通过fuzz获得一张精心构造的图片,可以在被裁剪处理之后刚好出现需要的php代码(难度较高)。

    最后通过链接上述4个流程,我们就可以完整的利用这个漏洞了,接下来我们详细分析一下。

    Post Meta变量覆盖

    当你对你上传的图片,编辑修改其信息时,你将会触发action=edit_post

    post data来自于POST

    如果是修复过的,在line 275行有修复patch

    https://github.com/WordPress/WordPress/commit/43bdb0e193955145a5ab1137890bb798bce5f0d2

    这个patch直接禁止了传入这个变量

    一路跟下去这个函数可以一直跟到wp-includes/post.php line 3770

    update_post_meta会把所有字段遍历更新

    就会更新数据库中的相应字段

    配合变量覆盖来目录穿越写文件

    根据原文的描述,我们首先需要找到相应的裁剪函数

    这里传入的变量src就是从修改过的_wp_attached_file而来。

    在代码中,我们可以很轻易的验证一个问题。在WordPress的设定中,图片路径可能会受到某个插件的影响而不存在,如果目标图片不在想要的路径下时,WordPress就会把文件路径拼接为形似 http://127.0.0.1/wp-content/uploads/2019/02/2.jpg 的url链接,然后从url访问下载原图

    这里的_load_image_to_edit_path就是用来完成这个操作的。

    也正是因为这个原因,假设我们上传的图片名为2.jpg,则原本的_wp_attached_file2019/02/2.jpg

    然后我们通过Post Meta变量覆盖来修改_wp_attached_file2019/02/1.jpg?/../../../evil.jpg

    这里的原图片路径就会拼接为{wordpress_path}/wp-content/uploads/2019/02/1.jpg?/../../../evil.jpg,很显然这个文件并不存在,所以就会拼接链接为http://127.0.0.1/wp-content/uploads/2019/02/2.jpg?/../../../evil.jpg,后面的部分被当作GET请求,原图片就会成功的获取到。

    紧接着进入save函数的新图片路径会拼接为{wordpress_path}/wp-content/uploads/2019/02/1.jpg?/../../../cropped-evil.jpg,我们就能成功写入新文件了。

    后面的save函数会调用你当前图片库的裁剪功能,生成图片结果。(默认为imagick)

    但这里看上去没有任何限制,实际上不是的。在写入的目标目录下,存在一个假目录,为1.jpg?

    • 而linux、mac支持这种假目录,可以使用?号
    • 但windows在路径中不能有?号,所以这里改用了#号

    成功写入文件

    控制模板参数来导致任意文件包含

    进度进展到这就有点儿陷入僵局,因为原文中关于这部分只用了一句话带过,在实际利用的过程中遇到了很多问题,甚至不同版本的WordPress会有不同的表现,其中诞生了多种利用方式,这里我主要讲1种稳定利用的方式。

    设置_wp_page_template

    首先我们先正向分析,看看在什么情况下我们可以设置_wp_page_template

    首先可以肯定的是,这个变量和_wp_attached_file一样都属于Post Meta的一部分,可以通过前面的操作来对这个变量赋值

    但实际测试过程中,我们发现,我们并不能在任何方式下修改并设置这个值。

    • 如果你设置了这个值,但这个文件不存在,则会被定义为default
    • 如果该值被设置,则没办法通过这种方式修改。

    所以这里我们可能需要新传一个媒体文件,然后通过变量覆盖来设置这个值。

    加载模板

    当我们成功设置了该变量之后,我们发现,并不是所有的页面都会加载模板,我们重新回到代码中。

    最终加载模板的地方在

    只要是在$template_names中需要被加载的文件名,会在当前主题的目录下遍历加载。

    回溯跟入

    继续回溯我们就能发现一些端倪,当你访问页面的时候,页面会通过你访问的页面属性,调用不同的模板加载函数。

    在这么多的模板调用函数中只有两个函数get_page_templateget_single_template这两个在函数中调用了get_page_template_slug函数。

    get_page_template_slug函数从数据库中获取了_wp_page_template

    只要我们能让模板加载时进入get_page_templateget_single_template,我们的模板就可以成功被包含。

    由于代码和前端的差异,我们也没有完全找到触发的条件是什么,这里选了一个最简单的,即上传一个txt文件在资源库,然后编辑信息并预览。

    生成图片马

    这部分就涉及到了后端图片库的问题,WordPress用到的后端图片处理库有两个,gd和imagick,其中默认优先使用imagick做处理。

    • imagick

    利用稍微比较简单,imagick不会处理图片中的exif部分。将敏感代码加入到exif部分就可以不会改动。

    • gd

    gd的利用就比较麻烦了,gd不但会处理图片的exif部分,还会删除图片中出现的php代码。除非攻击者通过fuzz获得一张精心构造的图片,可以在被裁剪处理之后刚好出现需要的php代码(难度较高)。

    由于这不是漏洞最核心的部分,这里就不赘述了。

    修复

    1、由于该漏洞主要通过图片马来完成RCE,而后端图片库为gd时,gd会去除图片信息中exif部分,并去除敏感的php代码。但如果攻击者精心设计一张被裁剪后刚好生成含有敏感代码的图片时,就可以造成RCE漏洞。如果后端图片库为imagick时,则将敏感代码加入到图片信息的exif部分,就可以造成RCE漏洞。

    官网上可供下载的所有release版本中都修复了这个漏洞,更新至最新版或者手动将当前版本覆盖安装即可。

    2、 通用防御方案
    使用第三方防火墙进行防护(如创宇盾[https://www.yunaq.com/cyd/])。

    3、技术业务咨询
    知道创宇技术业务咨询热线 :
    400-060-9587(政府,国有企业)、028-68360638(互联网企业)

    总结

    整个RCE的利用链由4部分组成,深入WordPress的底层Core逻辑,原本来说这4个部分无论哪个都很难造成什么危害,但却巧妙地连接在一起,并且整个部分意外的都是默认配置,大大增加了影响面。在安全程度极高的WordPress中能完成这种的攻击利用链相当难得,从任何角度都是一个非常nice的漏洞:>

    最后再次感谢我的小伙伴们以及整个过程中给我提供了很大帮助的朋友们:>


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/822/

    作者:吴烦恼 | Categories:安全科普技术分享 | Tags:
  • APT 为什么不使用 HTTPS 协议?

    2019-01-24

    原文:Why does APT not use HTTPS?
    作者:Chris Lamb
    译者:Nanako@知道创宇404实验室

    (这篇文章代表了一段时间前,特别是在CVE-2019-3462之前的情形。它并不代表我的个人意见,也不代表Debian / Ubuntu。)

    tl;dr

    https用于防止入侵者窃听到您与您访问的网站之间的通信,以及避免在您不知情的情况下修改数据。

    然而,通过APT命令获取的文件往往都有自己的签名以通过系统的检查。

    您的计算机根据一组已存储的可信密钥检查这些签名。如果缺少有效签名或者密钥不可信[1],则APT会拒绝下载该文件。这样可以确保您安装的软件来自您的授权,并且未被修改或替换。

    如果下载服务器的磁盘上软件包发生了恶意篡改,https是无法检测出来的。因此也没有必要“安全的”传输一个受损的软件包。

    隐私

    https通常不会为获取数据包提供重要的私密性。由于窃听者通常可以看到您正在通信连接的主机,如果您正与发布镜像的网络进行连接,则很明显您在进行下载更新。

    此外,即使通信是经过加密的,也不难根据传输大小确定要下载的文件[2]。因此,https只适用于从那些提供类似的,或大小相同的包的服务器上进行下载。

    其实更应该关注的问题并不是加密,而是确保您正在安装的文件未被修改过。

    过度信任CA

    有超过400个“证书颁发机构”可以为任何域颁发证书,其中很多证书机构没有有效的安全记录,还有一些明确被政府控制[3]。

    这意味着https对于发布镜像网络上的攻击目标提供了微乎其微的保护,甚至没有任何保护。您可以限制APT可以接收的有效证书集合,但这容易产生错误,对现有的公钥方案来说某些额外的麻烦是不值得的。

    为什么不提供HTTPS呢?

    您所用的发行版可以使用现有方案对文件进行加密签名,另外还可以通过https为文件提供“深度防御”。

    然而,通过SSL提供一个巨大的全球镜像网络不仅是一项复杂的工程任务(需要私钥的安全交换和存储)。如上所述,它意味着会对最终用户的安全性和隐私级别产生误导性。

    切换到https还意味着您无法利用本地代理服务器来加快访问速度,而且还将禁止多种类型的P2P 镜像,其中文件存储在不受您分发控制的服务器上。这将对远程区域的用户产生不同程度的影响。

    重放攻击

    简单签名机制存在的问题是,它不能保证您看到的是最新版本的存档。

    这可能会导致重放攻击,攻击者将存档替换为较早的未经修改的版本,阻止APT注意到那些会被利用的安全更新。

    为了解决这个问题,APT存档包含一个时间戳,在此时间戳之后的所有文件都被认作是旧文件[4]。

    更多信息

    SecureAPT wiki页面上可以找到更多技术细节?。

    脚注

    1. 显示发布:无法验证以下签名,因为公钥不可用。
    2. 如果通过(假设)apt-transport-tor使用Tor,甚至有可能出现这种情况。
    3. 例如,请参阅在StackOverflow上的我应该信任哪些受信任的root证书颁发机构
    4. 请参阅Debian Wiki上DebianRepository页面的Date,Valid-Until部分

     

     

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/803/

    作者:Nanako | Categories:安全科普漏洞通告 | Tags:
  • 从 0 开始学 Linux 驱动开发(一)

    2019-01-08

    作者:Hcamael@知道创宇404实验室

    最近在搞IoT的时候,因为没有设备,模拟跑固件经常会缺/dev/xxx,所以我就开始想,我能不能自己写一个驱动,让固件能跑起来?因此,又给自己挖了一个很大坑,不管最后能不能达到我的初衷,能学到怎么开发Linux驱动,也算是有很大的收获了。

    前言

    我写的这个系列以实践为主,不怎么谈理论,理论可以自己去看书,我是通过《Linux Device Drivers》这本书学的驱动开发,Github上有这本书中讲解的实例的代码[1]

    虽然我不想谈太多理论,但是关于驱动的基本概念还是要有的。Linux系统分为内核态和用户态,只有在内核态才能访问到硬件设备,而驱动可以算是内核态中提供出的API,供用户态的代码访问到硬件设备。

    有了基本概念以后,我就产生了一系列的问题,而我就是通过我的这一系列的问题进行学习的驱动开发:

    1. 一切代码的学习都是从Hello World开始的,怎么写一个Hello World的程序?
    2. 驱动是如何在/dev下生成设备文件的?
    3. 驱动怎么访问实际的硬件?
    4. 因为我毕竟是搞安全的,我会在想,怎么获取系统驱动的代码?或者没有代码那能逆向驱动吗?驱动的二进制文件储存在哪?以后有机会可能还可以试试搞驱动安全。

    Everything start from Hello World

    提供我的Hello World代码[2]

    Linux下的驱动是使用C语言进行开发的,但是和我们平常写的C语言也有不同,因为我们平常写的C语言使用的是Libc库,但是驱动是跑在内核中的程序,内核中却不存在libc库,所以要使用内核中的库函数。

    比如printk可以类比为libc中的printf,这是在内核中定义的一个输出函数,但是我觉得更像Python里面logger函数,因为printk的输出结果是打印在内核的日志中,可以使用dmesg命令进行查看

    驱动代码只有一个入口点和一个出口点,把驱动加载到内核中,会执行module_init函数定义的函数,在上面代码中就是hello_init函数。当驱动从内核被卸载时,会调用module_exit函数定义的函数,在上面代码中就是hello_exit函数。

    上面的代码就很清晰了,当加载驱动时,输出Hello World,当卸载驱动时,输出Goodbye World

    PS:MODULE_LICENSEMODULE_AUTHOR这两个不是很重要,我又不是专业开发驱动的,所以不用关注这两个

    PSS: printk输出的结果要加一个换行,要不然不会刷新缓冲区

    编译驱动

    驱动需要通过make命令进行编译,Makefile如下所示:

    一般情况下,内核的源码都存在与/usr/src/linux-headers-$(shell uname -r)/目录下

    比如:

    而我们需要的是编译好后的源码的目录,也就是/usr/src/linux-headers-4.4.0-135-generic/

    驱动代码的头文件都需要从该目录下进行搜索

    M=$(PWD)该参数表示,驱动编译的结果输出在当前目录下

    最后通过命令obj-m := hello.o,表示把hello.o编译出hello.ko, 这个ko文件就是内核模块文件

    加载驱动到内核

    需要使用到的一些系统命令:

    • lsmod: 查看当前已经被加载的内核模块
    • insmod: 加载内核模块,需要root权限
    • rmmod: 移除模块

    比如:

    旧版的内核就是使用上面这样的方法进行内核的加载与移除,但是新版的Linux内核增加了对模块的验证,当前实际的情况如下:

    从安全的角度考虑,现在的内核都是假设模块为不可信的,需要使用可信的证书对模块进行签名,才能加载模块

    解决方法用两种:

    1. 进入BIOS,关闭UEFI的Secure Boot
    2. 向内核添加一个自签名证书,然后使用证书对驱动模块进行签名,参考[3]

    查看结果

    在/dev下增加设备文件

    同样先提供一份代码,然后讲解这份实例代码[4]

    知识点1 -- 驱动分类

    驱动分为3类,字符设备、块设备和网口接口,上面代码举例的是字符设备,其他两种,之后再说。

    如上图所示,brw-rw----权限栏,b开头的表示块设备(block),c开头的表示字符设备(char)

    知识点2 -- 主次编号

    主编号用来区分驱动,一般主编号相同的表示由同一个驱动程序控制。

    一个驱动中能创建多个设备,用次编号来区分。

    主编号和次编号一起,决定了一个驱动设备。

    如上图所示,

    设备sdasda1的主编号为8,一个此编号为0一个此编号为1

    知识点3 -- 驱动是如何提供API的

    在我的概念中,驱动提供的接口是/dev/xxx,在Linux下Everything is File,所以对驱动设备的操作其实就是对文件的操作,所以一个驱动就是用来定义,打开/读/写/......一个/dev/xxx将会发生啥,驱动提供的API也就是一系列的文件操作。

    有哪些文件操作?都被定义在内核<linux/fs.h>[5]头文件中,file_operations结构体

    上面我举例的代码中:

    我声明了一个该结构体,并赋值,除了owner,其他成员的值都为函数指针

    之后我在scull_setup_cdev函数中,使用cdev_add向每个驱动设备,注册该文件操作结构体

    比如我对该驱动设备执行open操作,则会去执行scull_open函数,相当于hook了系统调用中的open函数

    知识点4 -- 在/dev下生成相应的设备

    对上面的代码进行编译,得到scull.ko,然后对其进行签名,最后使用insmod加载进内核中

    查看是否成功加载:

    虽然驱动已经加载成功了,但是并不会在/dev目录下创建设备文件,需要我们手动使用mknod进行设备链接:

    总结

    在该实例中,并没有涉及到对实际物理设备的操作,只是简单的使用kmalloc在内核空间申请一块内存。代码细节上的就不做具体讲解了,都可以通过查头文件或者用Google搜出来。

    再这里分享一个我学习驱动开发的方法,首先看书把基础概念给弄懂,细节到需要用到的时候再去查。

    比如,我不需要知道驱动一共能提供有哪些API(也就是file_operations结构都有啥),我只要知道一个概念,驱动提供的API都是一些文件操作,而文件操作,目前我只需要open, close, read, write,其他的等有需求,要用到的时候再去查。

    参考

    1. https://github.com/jesstess/ldd4
    2. https://raw.githubusercontent.com/Hcamael/Linux_Driver_Study/master/hello.c
    3. https://jin-yang.github.io/post/kernel-modules.html
    4. https://raw.githubusercontent.com/Hcamael/Linux_Driver_Study/master/scull.c
    5. https://raw.githubusercontent.com/torvalds/linux/master/include/linux/fs.h

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/779/

    作者:Nanako | Categories:安全研究安全科普 | Tags:
  • 从 CVE-2018-8495 看 PC 端 url scheme 的安全问题

    2018-10-19
    作者:0x7F@知道创宇404实验室
    时间:2018年10月18日

    0x00 前言

    本文受 CVE-2018-8495 漏洞的启发,以学习的目的,针对 PC 端 url scheme 的安全问题进行了分析研究。

    说到 url scheme 的安全问题,这并不是一个新问题,早在 2008 年就有相关的研究和利用;如今 2018 年又陆续出现了安全问题,包括 1 月的 Electron 命令注入(CVE-2018-1000006) 以及 10 月的 Edge RCE(CVE-2018-8495),可见 url scheme 的安全问题值得去一探究竟。

    url scheme 也称为 url protocolurl handler,本文使用 url scheme 这个名称。

    0x01 url scheme是什么

    常见的url scheme应用场景

    在平时使用电脑的过程中,常常会发现点击某一个链接就会尝试启动本地的应用程序,比如点击类似 mailto://test@test.com,就会启动邮件客户端,点击 thunder://xxxxx,就会启动迅雷客户端;这就是 url scheme 的应用。除此之外,我们使用浏览器也会发现地址栏中一些不同的前缀,常用的有 http://https://ftp://file://,这同样是 url scheme 的应用场景。

    各大操作系统开发商和浏览器开发商为了提高用户体验,丰富浏览器的功能,允许开发人员将 URI 与本地的应用程序进行关联,从而在用户使用浏览器时,可以通过点击某一链接即可启动应用程序;将这个功能简称为 url scheme。比如在 windows7 下使用 IE8 启动默认邮件客户端 outlook

    正因为 url scheme 这个优秀的功能设计,各大操作系统开发商都对此进行了支持,无论是 PC 端 Windows, MAC, Linux,还是移动端 iOS, Android 都有良好的支持。本文针对 PC 端下的 url scheme 的安全问题进行分析,移动端下同样也有类似的问题,但利用方式不同,这里就不展开了。

    url scheme工作流程

    在了解 url scheme 的功能后,可以大致理解到 url scheme 的工作流程;应用程序在操作系统中注册 url scheme 项,当浏览器或其他支持 url 的应用访问 特定的 url scheme 时,从系统中匹配相对应的 url scheme 项,从而启动该应用程序;可见这是一个三方相互支持的功能。

    正因如此,对于 url scheme 这个功能,在操作系统、浏览器(或其他支持 url 的应用)、应用程序这三个环节中,无论哪个环节出现了安全问题,或者是相互支持出现了问题,都将影响 url scheme 功能,最终给用户带来安全问题。

    0x02 创建 url scheme

    那么 url scheme 功能是如何在操作系统中注册的呢?不同的操作系统都有不同的实现方式,这里以 Windows7 为例进行演示说明。

    在 Windows7 上,url scheme 被记录在注册表 HKEY_CLASSES_ROOT 下,如 mailto 的相关字段:

    如果要创建一个新的 url scheme,直接在 HKEY_CLASSES_ROOT 添加即可,并在相应的字段中填入对应的值。创建的子项名即为 url scheme 功能名,在该子项下还包含两个项:DefaultIconshellDefaultIcon 包含该功能所使用的默认图标路径;在 shell 项下继续创建子项,例如: open,然后在 open 项下创建 command 子项,用于描述应用程序的路径以及参数。

    举个例子,创建 calc 用于启动 C:\Windows\System32\calc.exe

    补充一点:实际上,在 Windows 中有两种添加 url scheme 的方式,以上是直接添加注册表的方式(Pluggable Protocol),还有一种是异步可插拔协议(Asynchronous Pluggable Protocol),注册的协议会记录在 HKEY_CLASSES_ROOT\PROTOCOLS\ 下。这里就不展开了,详情可以参考:https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767916(v%3dvs.85)

    0x03 安全隐患

    对于 url scheme 功能,简单来讲就是「通过 url 可以启动某一个本地的应用程序」,这无疑大大提高了用户体验,但同时引入一些安全隐患,比如用户可以通过浏览器启动一个恶意程序,或者用户启动的应用程序具有特殊的功能可以被调用(如:删除文件、启动网络连接)。

    除此之外,对于包含 url 的的相关应用,用户是往往作为一个使用者、阅读者,而不是编辑者;也就是说 url 可以被攻击者恶意构造,从而达到远程启动本地应用程序的效果。

    那么在操作系统中,有哪些 url scheme 是可以被调用的呢?这里提供三个脚本用于导出三大 PC 系统下 url scheme

    Windows: [https://images.seebug.org/archive/duh4win.vbs]
    MAC: [https://images.seebug.org/archive/duh4mac.m]
    Linux: [https://images.seebug.org/archive/duh4linux.sh]

    (脚本来源于:https://www.blackhat.com/presentations/bh-europe-08/McFeters-Rios-Carter/Whitepaper/bh-eu-08-mcfeters-rios-carter-WP.pdf)

    运行脚本程序,可以看到系统下有不少可以调用的 url scheme,其中包括操作系统默认支持的,如 httpftpmailto,也有第三方的应用程序,如 qqthunder;如果这些应用程序出现安全问题,比如支持删除文件、启动另一个程序等敏感操作,最终在 url scheme 的帮助下,都将远程触发的安全问题。

    除了应用程序可能出现的安全问题,浏览器(或其他程序)在进行 url 解析并启动应用程序的过程也可以出现安全问题;并且这三方相互支持的过程中,仍然可能出现问题;无论是哪一个环节出现的安全问题,其危害最终都会在 url scheme 下被放大。

    本文就这以上可能出现安全问题的环节进行分析,并举例说明。

    0x04 操作系统的问题

    在 2007 年,Heise Security 公开了由 「url scheme 导致远程命令执行」的漏洞,其出现在 Windows XP 下已安装 IE7 版本的系统中,影响范围包括所有支持 url scheme 的应用程序。

    其构造的 PoC 如下:

    在 Windows XP 下运行结果如下:

    图片来源于:http://www.h-online.com/security/news/item/URI-problem-also-affects-Acrobat-Reader-and-Netscape-733744.html

    其造成漏洞的原因是由于微软通过安装适用于 Windows XP 的 IE7 改变了操作系统对 url 的处理,而应用程序直接将路径传递给操作系统用于启动,最终导致包含 字符的特殊链接导致启动任意程序。

    在漏洞公开后,微软并没有发布修复补丁,并且认为这不是 Windows XP 的原因,随后各大应用程序开发人员对该漏洞进行了修复。当然,上层应用可以对输入的参数进行检查,但这里也可以认为是操作系统方面的问题,导致了 url scheme 远程命令执行。

    0x05 浏览器的参数注入

    2018 年,在 url scheme 的安全问题中,有两个问题是由于 Windows 下的 IE 和 Edge 参数注入引发的,其中一个是 Electron 自定义协议命令注入(CVE-2018-1000006),另一个是 Edge 远程代码执行(CVE-2018-8495)。

    在 Windows 下 IE 和 Edge 对 url scheme 的处理方式有些不同,在浏览器接收到一个 url scheme 后,访问注册表查询对应的应用程序路径,随后进行 url 解码,然后调用 ShellExecute 函数簇,启动应用程序;正是因为 url 解码这一步造成了双引号闭合,从而引起了参数注入问题。示意图如下:

    Electron 自定义协议命令注入

    2018 年 1 月,Electron 发布了由自定义协议而导致命令注入的安全公告(CVE-2018-1000006),由于参数注入而引发的问题,构造的 PoC 如下:

    使用 IE 浏览器访问该链接,最终生成的启动参数如下:

    通过参数注入,调用 electron 中支持的 --gpu-launcher 参数,传入 cmd.exe 启动计算器,如下图:

    图片来源于:https://xz.aliyun.com/t/1990,详情可以参考这个链接。

    Edge 远程代码执行

    2018 年 10 月,Edge 公开了远程代码执行的安全公告(CVE-2018-8495),同样也是利用参数注入,最终达到了远程代码执行的效果;整个利用过程颇具巧妙性,本文对此进行详细的分析。

    首先说一点的是,在 Edge 中居然可以打开一些不合法的 url scheme(没有包含 URL Protocol 字段),比如 WSHFile 项:

    当然在 Windows7 和 Windows8 下不能打开。

    而恰恰 WSHFile 项指向了 wscript.exe,这个应用程序非常熟悉是Windows 内置的脚本解释器,那么可以利用 WSHFile 尝试去运行一个脚本;除此之外,上文提到 Edge 浏览器中存在参数注入的问题,那么是否有脚本可以接收参数并用于执行呢?

    漏洞作者最终找到:

    该脚本文件支持接收参数,并且会将命令直接拼接到字符串中,然后通过 powershell 进行执行。

    最终构造的 PoC 如下:

    以及执行后触发的效果:

    目前 Windows10 上已经发布了修复补丁,Edge 已经不能调用这种不合法的 url scheme 了。

    除此之外,404实验室的小伙伴在分析漏洞的过程中,也有一些额外的发现,如在注册表 HKEY_CLASSES_ROOT 还发现了和 WSHFile 类似的 url scheme,都指向 wscript.exe,同样也可以触发远程代码执行。包括:

    还有在 C:\Windows\System32\ 下也存在 SyncAppvPublishingServer.vbs,同样也可以利用,并且比漏洞作者所提供的更加可靠。

    除了 SyncAppvPublishingServer.vbs 这个文件, 在 C:\Windows\System32\Printing_Admin_Scripts\zh-CN 下的 pubprn.vbs 也同样可以触发代码执行。

    补充一点,在 Windows7 系统下 chrome 与 Edge 有相同的特性——会打开一些不合法的 url scheme,但由于 chrome 不存在参数注入的问题,所以可以暂且认为是安全的。

    0x06 应用程序的问题

    2017 年 12 月,macOS 上的 helpViewer 应用程序被公开由 XSS 造成文件执行的漏洞(CVE-2017-2361),影响 macOS Sierra 10.12.1 以下的版本;该漏洞同样也利用了 url scheme,攻击者可以构造恶意页面,从而发动远程攻击。这是典型的由于应用程序所导致的 url scheme 安全问题。

    漏洞详情可以参考:https://bugs.chromium.org/p/project-zero/issues/detail?id=1040&can=1&q=reporter%3Alokihardt%40google.com%20&sort=-reported&colspec=ID%20Status%20Restrict%20Reported%20Vendor%20Product%20Finder%20Summary&start=100

    其构造的 PoC 如下:

    在这个漏洞的利用过程中,可以发现操作系统和浏览器并没有出现问题,而是通过 url scheme 打开的应用程序出现了问题。通过对利用链的分析,可以了解到其中几个巧妙的点:

    1. 利用 url scheme 中的 help 协议打开应用程序 Safari.help
    2. 使用双重 url 编码绕过 helpViewer 对路径的检查,打开一个可以执行 JavaScript 的页面
    3. 使用 helpViewer 的内置协议 x-help-script 打开应用程序(PoC不包含)

    0x07 总结

    url scheme 功能的便捷性得力于操作系统、浏览器(或其他支持 url 的应用)以及应用程序三方的相互支持;要保证 url scheme 功能安全可靠,就必须牢牢把关这三方的安全。

    除此之外,不同的操作系统对 url scheme 实现方式不同,不同的浏览器也有自己的特性,应用程序也各有各的处理方式,多种组合的结果,就有可能出现一些意料之外的安全问题。

    最后感谢 404 实验室小伙伴 @LoRexxar' 与 @dawu 在分析过程中给我的帮助。


    References:

    1. CVE-2018-8495分析: https://leucosite.com/Microsoft-Edge-RCE/
    2. Seebug.paper: https://paper.seebug.org/515/
    3. 先知: https://xz.aliyun.com/t/1990
    4. electronjs: https://electronjs.org/blog/protocol-handler-fix
    5. blackhat: https://www.blackhat.com/presentations/bh-europe-08/McFeters-Rios-Carter/Whitepaper/bh-eu-08-mcfeters-rios-carter-WP.pdf
    6. blackhat: https://www.blackhat.com/presentations/bh-dc-08/McFeters-Rios-Carter/Presentation/bh-dc-08-mcfeters-rios-carter.pdf
    7. oreilly: https://www.oreilly.com/library/view/hacking-the-next/9780596806309/ch04.html
    8. Github: https://github.com/ChiChou/LookForSchemes
    9. MSRC.CVE-2018-8495: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8495
    10. Microsoft: https://docs.microsoft.com/en-us/windows/uwp/launch-resume/reserved-uri-scheme-names
    11. Microsoft: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85)
    12. Microsoft: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767916(v%3dvs.85)
    13. h-online: http://www.h-online.com/security/news/item/URI-problem-also-affects-Acrobat-Reader-and-Netscape-733744.html
    14. chromium: https://bugs.chromium.org/p/project-zero/issues/detail?id=1040&can=1&q=reporter%3Alokihardt%40google.com%20&sort=-reported&colspec=ID%20Status%20Restrict%20Reported%20Vendor%20Product%20Finder%20Summary&start=100

     

    Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/719/

    作者:Nanako | Categories:安全研究安全科普 | Tags:
  • ECShop 0day 的堕落之路

    2018-09-12
    Author: Badcode@知道创宇404实验室
    Date: 2018/09/04

    背景

    ECShop是一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店。系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序。2018年6月13日,知道创宇404积极防御团队通过知道创宇旗下云防御产品“创宇盾”防御拦截并捕获到一个针对某著名区块链交易所网站的攻击,通过分析,发现攻击者利用的正式ECShop 2.x版本的0day漏洞攻击。于2018年6月14日,提交到知道创宇Seebug漏洞平台并收录

    随后于2018年8月31日,ID为“ringk3y”研究人员在其博客公开这个漏洞,并做了详细分析,该分析收录在Seebug Paper

    知道创宇404积极防御团队于2018年9月2日正式对外发布《ECShop全系列版本的远程代码执行漏洞》预警。

    从2018年的6月13日首次拦截后,知道创宇404实验室多个团队对这个利用ECShop 0day攻击事件进行持续的监控分析,从下文的分析结果可以看出一个0day漏洞在实际攻击中的各个阶段的“堕落”过程。

    漏洞分析

    该漏洞影响ECShop 2.x和3.x版本,是一个典型的“二次漏洞”,通过user.php文件中display()函数的模板变量可控,从而造成SQL注入漏洞,而后又通过SQL注入漏洞将恶意代码注入到危险函数eval中,从而实现了任意代码执行。

    值得一提的是攻击者利用的payload只适用于ECShop 2.x版本导致有部分安全分析者认为该漏洞不影响ECShop 3.x,这个是因为在3.x的版本里有引入防注入攻击的安全代码,通过我们分析发现该防御代码完全可以绕过实现对ECShop 3.x的攻击(详见下文分析)。

    注:以下代码分析基于ECShop 2.7.3

    SQL 注入漏洞

    首先看到ecshop/user.php

    可以看到$back_act是从HTTP_REFERER获取到的,HTTP_REFERER是外部可控的,这也是万恶的根源。

    接着将back_act变量传递给assign函数,跟进ecshop/includes/cls_template.php

    可以从注释了解这个函数的功能,是注册模板变量,也就是$back_act变成了$this->_var[$back_act]=$back_act,而后调用display函数

    user.php调用display函数,传递进来的$filenameuser_passport.dwt,从函数来看,首先会调用$this->fetch来处理user_passport.dwt模板文件,fetch函数中会调用$this->make_compiled来编译模板。user_passport.dwt其中一段如下:

    make_compiled会将模板中的变量解析,也就是在这个时候将上面assign中注册到的变量$back_act传递进去了,解析完变量之后返回到display函数中。此时$out是解析变量后的html内容,判断$this->_echash是否在$out中,若在,使用$this->_echash来分割内容,得到$k然后交给insert_mod处理。

    由于_echash是默认的,不是随机生成的,所以$val内容可随意控制。跟进$this->insert_mod

    $val传递进来,先用|分割,得到$fun$para$para进行反序列操作,$funinsert_拼接,最后动态调用$fun($para),函数名部分可控,参数完全可控。接下来就是寻找以insert_开头的可利用的函数了,在ecshop/includes/lib_insert.php有一个insert_ads函数,正好满足要求。看下insert_ads

    $arr是可控的,并且会拼接到SQL语句中,这就造成了SQL注入漏洞。

    根据上面的流程,可以构造出如下形式的payload

    实际可利用payload

    代码执行

    继续看insert_ads函数

    可以看到在SQL查询结束之后会调用模板类的fetch方法,在user.php中调用display,然后调用fetch的时候传入的参数是user_passport.dwt,而在此处传入的参数是$position_style,向上溯源,发现是$row['position_style']赋值而来,也就是SQL语句查询的结果,结果上面这个SQL注入漏洞,SQL查询的结果可控,也就是$position_style可控。

    要到$position_style = $row['position_style'];还有一个条件,就是$row['position_id']要等于$arr['id'],查询结果可控,arr['id']同样可控。

    之后$position_style会拼接'str:'传入fetch函数,跟进fetch

    因为之前拼接'str:'了,所以strncmp($filename,'str:', 4) == 0为真,然后会调用危险函数$this->_eval,这就是最终触发漏洞的点。但是参数在传递之前要经过fetch_str方法的处理,跟进

    第一个正则会匹配一些关键字,然后置空,主要看下最后一个正则

    这个正则是将捕获到的值交于$this-select()函数处理。例如,$source的值是xxx{$abc}xxx,正则捕获到的group 1 就是$abc,然后就会调用$this-select("$abc")

    跟进select函数

    当传入的变量的第一个字符是$,会返回由 php 标签包含变量的字符串,最终返回到_eval()危险函数内,执行。在返回之前,还调用了$this->get_var处理,跟进get_var

    当传入的变量没有.$时,调用$this->make_var,跟进make_var

    在这里结合select函数里面的语句来看,<?php echo $this->_var[' $val '];?>,要成功执行代码的话,$val必须要把['闭合,所以payload构造,从下往上构造,$valabc'];echo phpinfo();//;从select函数进入get_var的条件是第一个字符是$,所以payload变成了$abc'];echo phpinfo();//;而要进入到select,需要被捕获,payload变成了{$abc'];echo phpinfo();//},这里因为payload的是phpinfo(),这里会被fetch_str函数的第一个正则匹配到,需要变换一下,所以payload变为{$abc'];echo phpinfo/**/();//},到这里为止,php 恶意代码就构造完成了。

    接下来就是把构造好的代码通过SQL注入漏洞传给$position_style。 这里可以用union select 来控制查询的结果,根据之前的流程,$row['position_id']$arr['id']要相等,$row['position_id']是第二列的结果,$position_style是第九列的结果。$arr['id']传入' /*,$arr['num']传入*/ union select 1,0x27202f2a,3,4,5,6,7,8,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d,10-- -0x27202f2a' /*的16进制值,也就是$row['position_id']的值,0x7b24616263275d3b6563686f20706870696e666f2f2a2a2f28293b2f2f7d是上面构造的php代码的16进制值,也就是$position_style

    结合之前的SQL漏洞的payload构造,所以最终的payload的是