從補丁對比檔案來看,在DecodeableRpcInvocation.java檔案133-135行增加了對Method方法進行驗證,如果驗證不通過則丟擲非法引數異常終止程式執行,核心程式碼程式碼如下:
if (!RpcUtils.isGenericCall(path, this.getMethodName()) && !RpcUtils.isEcho(path, this.getMethodName())){throw new IllegalArgumentException("Service not found:" + path + ", " + this.getMethodName());}
跟進isGenericCall和isEcho方法,發現驗證邏輯十分簡單,如果method等於$invoke、$invokeAsync或者$echo則返回true。不得不說此處站在開發角度思考是沒問題的,非Dubbo自帶service中的$invoke、$invokeAsync、$echo方法以外,其他函式名全部丟擲異常,但是萬萬沒想到RPC呼叫過程中方法名是使用者可控的,所以攻擊者可輕易的將method設定為其中任意一個方法來繞過此處限制。
public static boolean isGenericCall(String path, String method) {return "$invoke".equals(method) || "$invokeAsync".equals(method);}
public static boolean isEcho(String path, String method) {return "$echo".equals(method);}
通過對歷史版本的回溯,發現在2019.10.31日的一次提交中DubboProtocol類的getInvoker函式的RemotingException程式碼塊中增加了getInvocationWithoutData方法,對inv物件的arguments引數進行置空操作,用來緩解後反序列化攻擊,此處正是CVE-2020-1948漏洞後反序列化利用的觸發點。
如下getInvocationWithoutData函式,可能是為了方便開發者排查問題,如果系統配置log4j debug級別或者不配置任何其他級別,則不會將inv物件的arguments引數設定為null,會直接返回invocation物件,所以還是存在被後反序列化漏洞攻擊的風險。所謂後反序列化簡單理解就是漏洞是在物件被正常反序列化之後觸發,比如在異常處理中對成功反序列化的物件進行間接或直接的函式呼叫,從而導致的程式碼執行。
/*** only log body in debugger mode for size & security consideration. * * @param invocation * @return */private Invocation getInvocationWithoutData(Invocation invocation) {if (logger.isDebugEnabled()) {return invocation; }if (invocation instanceof RpcInvocation){RpcInvocation rpcInvocation = (RpcInvocation) invocation;rpcInvocation.setArguments(null);return rpcInvocation;}return invocation; }
由上可知,DecodeableRpcInvocation#decode請求體解碼函式的驗證邏輯存在繞過DubboProtocol#getInvocationWithoutData函式的後反序列緩解存在無效情況。
構造POC
知道了method的驗證邏輯,修改CVE-2020-1948 Poc中的的service_name和method_name引數的值,分別為:org.apache.dubbo.rpc.service.GenericService和$invoke。
在DecodeableRpcInvocation類中的decode函式方法起始處設定斷點進行Debug。
程式碼123-124行首先通過path(對應客戶端的service_name)引數來獲取服務描述物件repository,該物件包含了服務名、介面型別和方法資訊等。
繼續跟進,由於params是我們構造Gadget, 最終repository物件獲取到函式描述物件為null。
繼續跟進,由於pts變數沒有被賦值,所以pts== DubboCodec.EMPTY_CLASS_ARRAY表示式成立, 接著進入isGenericCall函式,由於rpc呼叫設定的method的值為$invoke,此處可以驗證通過。
最後進入hession反序列化流程,成功執行了程式碼。
可以看到呼叫棧如下:
另外,Dubbo暴露的埠如果開啟了Telnet協議,攻擊者可以連線到服務通過ls命令檢視service、method等資訊,甚至可以執行shutdown危險操作直接關停服務。白帽子@CF_HB在Dubbo 2.6.8版本通過Telnet服務中InvokeTelnetHandler.java類在處理invoke命令時的漏洞結合Fastjson反序列化漏洞成功進行了利用。隨著越來越多的安全研究人員對Dubbo安全問題的關注,相信後面會有更多的漏洞被挖掘出來。
漏洞的修復
1.由社群aquariuspj使用者給出的對DecodeableRpcInvocation增加入參型別校驗
2.漏洞發現者ruilin建議刪除RpcInvocation類的toString方法中輸出的arguments引數,防範後反序列化攻擊。同時對Hessian進行黑白名單加固來防範Hessian反序列化攻擊。
目前官方和社群給出的修復方法都是單點防禦,很容易被攻擊者繞過,短期防護可參考玄武實驗室給出的方案:
• 出網限制
經研究當前存在的反序列化利用鏈大多需要遠端載入惡意類,如果沒有特殊需求,建議在不影響業務的情況下將伺服器配置出外網限制。
• IP白名單
建議使用者將能夠連線至Dubbo服務端的消費端IP加入到可信IP白名單裡,並在服務端配置可信IP白名單,以防止攻擊者在外部直接發起連線請求。
• 更換預設的反序列化方式
Dubbo協議預設採用Hessian作為序列化反序列化方式,而Hessian存在危險的反序列化漏洞。使用者可以在考慮不影響業務的情況下更換協議以及反序列化方式,如:rest,grpc,thrift等。
• 關閉公網埠
不要將Dubbo服務端的開放埠暴露在公網,但需要注意這種場景若攻擊者在內網環境仍然可以進行攻擊。
參考
Apache Dubbo Provider 遠端程式碼執行漏洞 (CVE-2020-1948)
https://xlab.tencent.com/cn/2020/06/23/xlab-20-001/
Java“後反序列化漏洞”利用思路
http://rui0.cn/archives/1338
[CVE-2020-1948] Apache Dubbo Provider default deserialization cause RCE
https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html