+5

Phân tích CVE-2017-3066 - AMF Deserialization trong Adobe ColdFusion

1. Giới thiệu

Adobe ColdFusion là một nền tảng phát triển ứng dụng web nhanh chóng, ngôn ngữ lập trình được sử dụng với nền tảng Adobe ColdFusion cũng thường được gọi là ColdFusion, mặc dù được biết chính xác hơn là CFML. ColdFusion ban đầu được thiết kế để giúp dễ dàng kết nối các trang HTML đơn giản với cơ sở dữ liệu. Đến phiên bản 2 (1996), nó đã trở thành một nền tảng đầy đủ bao gồm cả IDE

2. Về lỗ hổng

image.png

Đây là một lỗ hổng liên quan đến AMF Deserialization, tuy là một CVE từ lâu nhưng do có nhiều kiến thức mới với mình, vì vậy mình sẽ phân tích lại nó.

2.1. Vị trí lỗi

Vị trí gây ra lỗi là /flex2gateway/amf cấu hình trong cfusion/wwwroot/WEB-INF/flex/services-config.xml

image.png

Trong file web.xml, nơi handle chức năng này là

image.png

MessageBrokerServlet được handle bởi coldfusion.bootstrap.BootstrapServlet với param khi init service là coldfusion.flex.ColdFusionMessageBrokerServlet

image.png

2.2. Vòng đời của một Servlet

Một Servlet cơ bản sẽ có 3 phần, init(), service()destroy().

image.png

  • Init method được gọi bởi Servlet container để cho biết rằng một Servlet được khởi tạo thành công và sắp đưa vào sử dụng
  • Service method được gọi để thông báo cho Servlet container về các request của client. Method này sử dụng ServletRequest object để collect data request của client và dùng ServletResponse object để tạo output.
  • Destroy method chạy duy nhất 1 lần để báo hiệu sự kết thúc của Servlet instance.

2.3. Nơi diễn ra Deserialization

Đầu tiên, ta xét đến method init() của coldfusion.bootstrap.BootstrapServlet:

image.png

Hàm này lấy đầu vào là servlet.class, chính là coldfusion.flex.ColdFusionMessageBrokerServlet, sau đó đưa vào hàm ClassloaderHelper.initServletClass.

image.png

Trong hàm này, nó tiếp tục gọi đến loadClass để class này được load.

Tiếp theo, xét đến method service() của coldfusion.bootstrap.BootstrapServlet:

image.png

Nó sẽ gọi đến coldfusion.flex.ColdFusionMessageBrokerServlet#services()

image.png

Trong hàm này, nó gọi đến hàm services() của MessageBrokerServletColdFusionMessageBrokerServlet đã implement nó.

    public void service(HttpServletRequest req, HttpServletResponse res) {
            ...

            encoded = ((HttpServletRequest)req).getContextPath();
            String pathInfo = ((HttpServletRequest)req).getPathInfo();
            String endpointPath = ((HttpServletRequest)req).getServletPath();
            if (pathInfo != null) {
                endpointPath = endpointPath + pathInfo;
            }

            Endpoint endpoint;
            try {
                endpoint = this.broker.getEndpoint(endpointPath, encoded);
            } catch (MessageException var29) {
                ...

            try {
                ...
                endpoint.service((HttpServletRequest)req, res);

Như ta đã xem xét ở trên, với endpoint là /flex2gateway/amf thì Endpoint class của nó sẽ là flex.messaging.endpoints.AMFEndpoint.

Vì thế việc gọi endpoint.service((HttpServletRequest)req, res); sẽ gọi tới flex.messaging.endpoints.AMFEndpoint#services()

Điều đặc biệt ở đây là trong AMFEndpoint không có phương thức services, nhưng như ta thấy như sau:

image.png

image.png

flex.messaging.endpoints.AMFEndpoint kế thừa flex.messaging.endpoints.BasePollingHTTPEndpoint, rồi nó kế thừa từ flex.messaging.endpoints.BaseHTTPEndpoint, và trong flex.messaging.endpoints.BaseHTTPEndpoint có phương thức services(). Theo tính chất trong OOP, phương thức lớp cha không được lớp con override thì khi gọi phương thức đó từ lớp con sẽ gọi đến phương thức đó trong lớp cha. Do vậy AMFEndpoint.service() --> BasePollingHTTPEndpoint.service() --> BaseHTTPEndpoint.service().

image.png

Trong hàm này, nó gọi tới filterChain#invoke() với

image.png

Hàm createFilterChain() trả về một object là serializationFilter

image.png

AMFFilter là một abstract class và được 4 Filter Class implement, trong đó có flex.messaging.endpoints.amf.SerializationFilter.

Do vậy, this.filterChain#invoke() sẽ gọi tới flex.messaging.endpoints.amf.SerializationFilter.invoke().

    public void invoke(ActionContext context) throws IOException {
        SerializationContext sc = SerializationContext.getSerializationContext();

        try {
            MessageDeserializer deserializer = sc.newMessageDeserializer();
            InputStream in = FlexContext.getHttpRequest().getInputStream();
            deserializer.initialize(sc, in, debugTrace);
            int reqLen = FlexContext.getHttpRequest().getContentLength();
            context.setDeserializedBytes(reqLen);
            ...
            ActionMessage m = new ActionMessage();
            context.setRequestMessage(m);
            deserializer.readMessage(m, context);
            success = true;

Phương thức readMessage là liên quan đến AMF Deserialization, thứ ta sẽ tìm hiểu tiếp ở phần sau.

Kết quả: Gửi request tới /flex2gateway/amf với input là 1 Object để thực hiện deserialize.

2.4. Tìm hiểu về AMF Deserialization

AMF (Action Message Format) là định dạng binary serialization thường được sử dụng bởi các ứng dụng Flash. Có một số thư viện sử dụng AMF thường dùng như:

Mỗi thư viện này đều đã bị ảnh hưởng bởi một trong số các kiểu tấn công:

  • XML external entity (XXE)
  • Tạo đối tượng và thiết lập thuộc tính tùy ý
  • Deserialization thông qua RMI

Một trong những tính năng mới của các đối tượng AMF3 là bổ sung hai đặc điểm nhất định, được gọi là ExternalizableDynamic:

  • Externalizable: Một instance của class implement flash.utils.IExternalizable và kiểm soát hoàn toàn việc serialization các thành phần của nó. Nó tương tự với java.io.Externalizable interface. Trên thực tế, tất cả thư viện nêu trên đều xây dựng flash.utils.IExternalizable từ đặc điểm kỹ thuật tương đương vớijava.io.Externalizable của Java, cho phép tái tạo bất kỳ lớp nào implement java.io.Externalizable. Nó sử dụng 2 method là readExternal(java.io.ObjectInput)writeExternal(java.io.ObjectInput) để deserialize/serialize object.
  • Dynamic: Một instance của các class definition với các đặc điểm được khai báo động, các public variable và method có thể được thêm và bớt trong runtime. Nó tương tự với chức năng của JavaBean: cho phép khởi tạo đối tượng bằng cách chỉ định tên lớp, tên và giá trị của thuộc tính. Trên thực tế, nhiều triển khai sử dụng các tiện ích JavaBeans hiện có như java.beans.Introspector (Flamingo, Flex BlazeDS, WebORB) hoặc tự triển khai một chức năng tương tự (GraniteDS).

Trong trường hợp cụ thể này, lỗ hổng nằm ở thư viện Apache BlazeDS - CVE-2017-5641 version <= 4.7.2

image.png

Phương thức readMessage:

image.png

Trong đó readHeaderreadBody:

image.png

image.png

Hàm readObject():

image.png

image.png

readObjectvalue():

image.png

readScriptObject():

image.png

Method Amf3Input.readScriptObject()xác định class đang được xử lý có implement java.io.Externalizable hay không.

Nếu ta dùng 1 Object có implement Externalizable thì sẽ tới method readExternal() của object đang được xử lý.

readExternalizable():

image.png

Như vậy đây là 1 hướng trong khai thác AMF Deserialization.

Nếu class không implement Externalizable sẽ chuyển sang 1 nhánh khác lấy các property của class và sử dụng BeanProxy.setValue() để set lại các giá trị này.

image.png

image.png

Khi đó BeanProxy sẽ cố gắng tìm các setter method của class và invoke.

image.png

Đây là hướng khác trong khai thác AMF Deserialzation.

Như vậy, chain của lỗ hổng trong thư viện này có thể tóm tắt lại như sau:

image.png

Sau đây mình sẽ trình bày cách khai thác đối với lỗ hổng này.

2.5. Chuyển từ Externalizable.readExternal() sang ObjectInputStream.readObject()

Dùng MetaDataEntry.readExternal()

Tìm ra được class này bằng cách: tìm trong các class implement từ java.io.Externalizable có phương thức readExternal() call được sang readObject().

Trong org.apache.axis2.util.MetaDataEntry#readExternal sẽ gọi tới SafeObjectInputStream.install.

image.png

Khi đó nó sẽ khởi tạo bằng việc gọi tới org.apache.axis2.context.externalize.SafeObjectInputStream#readObject.

image.png

Rồi readObjectOverride thực hiện gọi readObject của Java Native.

image.png

Trong lib của server có Commons BeanUtils 1.8.0, vì vậy chain CommonsBeanutil1 sẽ hoạt động, tuy nhiên cần wrapper bởi MetaDataEntry. Cách dễ nhất là download ysoserial về rồi build lại (nhớ đổi phiên bản từ 1,9.2 thành 1.8.0 của common-beanutils) sau đó wrapper MetaDataEntry.

POC:

image.png

Tham khảo

https://docs.google.com/presentation/d/116DwvGitgknoiq_AOLmRlrkjCrUWfi38t_u-GdU4k2k

https://codewhitesec.blogspot.com/2017/04/amf.html


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí