fastjson有一些已知的严重RCE漏洞,例如CVE-2017-18349和CVE-2022-25845。一般其影响范围在服务器端,多为Spring Boot框架的服务,而在安卓的影响却没有搜到任何资料。我们来分析一下,如果一个安卓客户端应用使用了老版本的fastjson库,是否可以被攻击。
在查看fastjson 1.1.53.android的maven网页时,发现其标注了存在已知CVE漏洞。
Just a moment...
mvnrepository.com/artifact/com.alibaba/fastjson/1.1.53.android
查看fastjson的github修复公告,却表示安卓环境不涉及此漏洞(CVE-2017-18349)。除此之外,没有找到更多的fastjson漏洞在安卓上的分析。
e28K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8r3W2T1j5h3u0S2i4K6u0r3k6X3q4K6N6r3A6K6L8$3&6Q4x3V1k6%4K9h3E0A6i4K6u0r3M7$3g2U0N6i4u0A6N6s2W2Q4y4h3k6#2M7r3c8S2N6r3g2Q4y4h3j5J5x3o6p5%4x3o6x3I4y4g2)9K6b7%4y4H3j5h3&6Q4x3@1g2Q4x3@1y4Q4x3V1k6K6M7r3q4F1i4K6y4q4
github.com/alibaba/fastjson/wiki/security_update_20170315
尽管fastjson出过好几次RCE的漏洞,但是本质上原理相同,仅仅是安全检查绕过的方法不同,因此我们本文先研究最初被发现的RCE,CVE-2017-18349。
经过一些搜索,找到了网上一些关于此漏洞的信息,但是没有找到为什么安卓版本不涉及此漏洞。
我发现fastjson发布的版本号中,除了普通的版本还有安卓版本,后缀为“.android”。安卓版是主要针对安卓环境进行优化,除此之外没有特殊改动。
441K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8r3W2T1j5h3u0S2i4K6u0r3k6X3q4K6N6r3A6K6L8$3&6Q4x3V1k6%4K9h3E0A6i4K6u0r3b7h3&6V1M7X3!0A6k6q4)9J5y4f1f1%4i4K6t1#2z5o6W2Q4x3U0f1^5z5q4)9J5y4f1f1$3i4K6t1#2z5f1y4Q4x3U0g2m8b7#2)9K6b7%4y4H3j5h3&6Q4x3@1g2Q4x3@1y4Q4x3V1k6K6M7r3q4F1i4K6y4q4
github.com/alibaba/fastjson/wiki/Android%E7%89%88%E6%9C%AC
Android版本
3e3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8r3W2T1j5h3u0S2i4K6u0r3k6X3q4K6N6r3A6K6L8$3&6Q4x3V1k6%4K9h3E0A6i4K6u0r3b7h3&6V1M7X3!0A6k6q4)9J5y4f1f1%4i4K6t1#2z5o6W2Q4x3U0f1^5z5q4)9J5y4f1f1$3i4K6t1#2z5f1y4Q4x3U0g2m8b7#2)9K6b7%4y4H3j5h3&6Q4x3@1g2Q4x3@1y4Q4x3V1k6K6M7r3q4F1i4K6y4q4
部分fastjson安卓版的差异
以目前的信息,可以得到以下两个猜测:
为了分析fastjson在安卓上的影响,我们搭建3个环境。
使用vulhub上的样例可以成功复现,具体参考其wiki。
771K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6$3N6h3I4Z5N6h3u0Q4x3V1k6$3N6h3I4Z5N6h3u0Q4x3V1k6@1M7X3g2W2i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3k6X3q4K6N6r3A6K6L8$3&6Q4x3V1j5I4i4K6u0W2x3W2)9J5k6e0t1@1i4K6u0V1M7X3y4W2i4K6y4o6M7%4m8S2L8W2)9K6c8g2)9K6b7#2)9J5c8Y4y4H3j5h3&6Q4x3@1f1`.
github.com/vulhub/vulhub/tree/master/fastjson/1.2.24-rce
但是这个样例是docker容器,其中包含的jar文件,没有源码。为了能好地分析,我们需要用源码搭建一套复现环境。要找到源码,可以尝试从docker中复制出来然后反编译。
运行其容器后,使用以下命令将里面的jar拷贝到本地宿主机。
docker cp <container-id>:<src-path> <dest-path>
然后用jadx打开,找到相关json反序列化入口
可见此poc使用的是一个spring boot框架下的普通的json反序列化,没有特殊条件。后续我们将用 JSON.parseObject(<json-str>) 的方式来复现
我们本地也构造这样一个环境试试。首先尝试新建一个spring boot工程,使用较新的jdk 17和spring boot 3.2.2版本,但是老的fastjson 1.2.24。
请注意当前的846K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4J5N6q4)9J5k6i4y4H3M7X3W2F1k6#2)9J5k6h3W2G2i4K6u0r3默认初始模板中的依赖是错误的。如果你是用的是其初始模板,需要将build.gradle中“org.springframework.boot:spring-boot-starter”需要改成“org.springframework.boot:spring-boot-starter-web”,否则会编译失败。
使用一个简单的反序列化payload, 在服务器根路径收到get请求时触发
在请求localhost:8080后,发现服务器报错
java.lang.reflect.InaccessibleObjectException: Unable to make public com.sun.rowset.JdbcRowSetImpl() accessible: module java.sql.rowset does not "exports com.sun.rowset" to unnamed module
搜索此报错,看到如下github issue。
da7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6V1j5X3N6W2k6g2)9J5c8X3k6S2M7%4c8B7M7$3!0F1i4K6u0V1M7X3y4W2i4K6u0r3K9i4y4K6N6h3g2K6i4K6u0r3x3W2)9K6b7%4y4H3j5h3&6Q4x3@1g2Q4x3@1y4Q4x3V1k6K6M7r3q4F1i4K6y4q4
github.com/dbgee/fastjson-rce/issues/2
看上去jdk 17是无法构造RCE的,因此尝试换成jdk 1.8 (8u202)。
这样修改后编译是失败的,因为spring boot 3.2.2不兼容。由于依赖的环境版本比较老,所以有一些配置需要修改才能正常跑起来。
根据这篇Stack Overflow回答,我们去找spring boot 2.7
cf8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4U0K9$3!0$3k6i4u0X3L8r3!0%4i4K6u0W2j5$3!0E0i4K6u0r3M7i4g2W2M7%4c8A6L8$3&6K6i4K6u0r3y4K6j5@1y4U0M7#2x3U0u0Q4x3V1k6U0j5h3&6@1i4K6u0V1j5$3!0E0M7r3W2D9k6g2)9J5k6s2y4H3M7X3W2F1k6#2)9J5k6r3u0G2L8%4c8Q4x3X3c8G2L8W2)9J5k6r3A6S2N6X3q4Q4x3X3b7I4i4K6u0V1z5q4)9K6b7%4y4H3j5h3&6Q4x3@1g2Q4x3@1y4Q4x3V1k6K6M7r3q4F1i4K6y4q4
stackoverflow.com/questions/76467522/cant-compile-spring-boot-on-java-1-8
按照spring boot 2.7的gradle设置,重新修改build.gradle。
e37K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2M7%4m8J5K9h3&6Y4i4K6u0W2K9h3!0Q4x3V1k6K6M7s2u0A6L8X3N6Q4x3X3c8T1L8$3!0@1i4K6u0r3k6r3!0U0M7#2)9J5c8U0u0Q4x3X3f1%4i4K6u0W2x3e0S2Q4x3V1k6Y4M7X3q4V1L8r3g2Q4x3X3c8H3L8s2g2Y4K9h3&6Q4x3V1k6J5k6h3k6W2M7X3g2F1j5$3g2Q4x3V1k6Z5N6r3#2D9M7$3W2F1k6$3I4W2i4K6u0r3i4K6t1K6k6$3g2@1N6r3W2F1k6#2)9J5k6s2y4@1j5i4u0@1k6h3c8Q4x3@1y4K6M7r3q4F1i4K6y4q4i4K6y4o6i4K6u0r3M7%4m8S2L8W2)9K6c8b7`.`.
docs.spring.io/spring-boot/docs/2.7.18/gradle-plugin/reference/htmlsingle/#getting-started
这是我的最终build.gradle。注意第22行不能加,否则在当前的环境中跑不起来。
与此同时,如果没有jdk 1.8,去oracle官网创建一个账号,然后下载。
由于我是用gradlew启动的程序,所以在其bash脚本中进行修改,无脑使用了java 1.8
在启动nc监听的情况下,再次访问该网页,复现成功。可见右侧的监听收到了rmi的请求。为了更简便地分析调试,我们这里不必使用完整的PoC,而是在收到请求之后就确定是有问题的。
我们先尝试一下安卓版本能否复现这个bug。由于描述上写的fastjson版本小于1.2.25都存在这个问题,所以我这里用了1.1.52.android这个版本。现在我们把build.gradle中的fastjson版本修改后重新编译并运行服务器。
然后使用同样的payload,发现服务器依然会去请求jndi。如果我们打断点,是可以看到这次调用栈进入的是1.1.52.android版本的fastjson。
那么排除fastjson的安卓版有不同于普通版的地方,所以安卓版是安全的的这个假设。
经过一些搜索,我们可以看到安卓是不支持jndi的。
类似fastjson,log4shell也是在安卓不受影响。其同样利用了jndi的方式进行了RCE。可以看到这篇文章分析得出安卓不影响,原因是安卓不支持jndi。
support.nowsecure.com/hc/en-us/articles/4417200289421-Log4Shell-and-Its-Impact-on-Mobile-Security
由于我们尝试的poc是依赖jndi的,因此安卓的老版本确实无法用相同的方式攻击。
我们搭建一个kotlin的安卓项目,并使用fastjson 1.1.52.android,其余均为默认值。
尝试在安卓应用中使用相同的payload,在activity的onCreate的时候直接运行反序列化。得到以下报错:类无法找到
由于老版本fastjson在反序列化时,可以指定任意类进行反序列化。在反序列化时也会调用其get、set、constructor等方法,也就相当于代码执行。
任何可以被fastjson反序列化攻击的类称为fastjson gadget,而我们最熟知的便是jndi的类。攻击者能用fastjson执行的代码,仅限于fastjson gadget。具体的反序列化分析不在本文中讨论。
安卓版本不能使用jndi的gadget,但是其他的很多gadget可以使用,也存在很多Spring Boot没有的gadget。目前已知的安卓fastjson gadget中,没有能造成实际影响的。
目前已知的PoC无法攻击安卓应用,因为使用的fastjson gadget是jndi,且安卓不支持jndi。攻击者可以利用fastjson进行代码执行,但是目前(2024-02-26)没有发现可以在安卓上造成实际影响的fastjson gadget。
本次研究的fastjson的安卓版本(后缀为“.android”)和普通版本在服务器端使用都存在RCE漏洞,攻击方式相同。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课