cve-2019-11581 Atlassian Jira未授權服務端模板注入漏洞

whatday發表於2020-12-21

漏洞描述

Atlassian Jira是澳大利亞Atlassian公司的一套缺陷跟蹤管理系統。該系統主要用於對工作中各類問題、缺陷進行跟蹤管理。

Atlassian Jira Server和Jira Data Center存在服務端模板注入漏洞,成功利用此漏洞的攻擊者可在執行受影響版本的Jira Server或Jira Data Center系統上執行任意命令。

目前PoC已放出,建議受影響的客戶儘快升級或採用臨時緩解措施。

  • 第一種情況,Jira服務端已配置好SMTP伺服器,且“聯絡管理員表單”功能已開啟(預設配置不開啟);

  • 第二種情況,Jira服務端已配置好SMTP伺服器,且攻擊者具有"JIRA管理員"的訪問許可權。在第一種情況下,“聯絡管理員表單”功能開啟的情況下,攻擊者可以未經任何認證,通過向/secure/ContactAdministrators.jspa發起請求利用此漏洞。在第二種情況下,攻擊者具有"JIRA 管理員"的訪問許可權下可通過/secure/admin/SendBulkMail!default.jspa利用此漏洞。

     

影響版本

  • 4.4.x

  • 5.x.x

  • 6.x.x

  • 7.0.x

  • 7.1.x

  • 7.2.x

  • 7.3.x

  • 7.4.x

  • 7.5.x

  • 7.6.x < 7.6.14

  • 7.7.x

  • 7.8.x

  • 7.9.x

  • 7.10.x

  • 7.11.x

  • 7.12.x

  • 7.13.x < 7.13.5

  • 8.0.x < 8.0.3

  • 8.1.x < 8.1.2

  • 8.2.x < 8.2.3

修復版本

  • 7.6.14

  • 7.13.5

  • 8.0.3

  • 8.1.2

  • 8.2.3

修復建議

升級到修復版本(7.6.14、7.13.5、8.0.3、8.1.2、8.2.3)。

漏洞簡析

第二種利用場景前提是拿到Jira管理員的許可權,利用條件較難滿足,這裡主要分析第一種情況。原因在於atlassian-jira/WEB-INF/classes/com/atlassian/jira/web/action/user/ContactAdministrators 未對Subject(郵件主題)處進行過濾,使用者傳入的郵件主題被當作template(模板)指令執行。在任何一種情況下,成功利用此漏洞的攻擊者都可在執行受影響版本的Jira Server或Jira Data Center的系統上執行任意命令。

第一種,未授權程式碼執行利用條件:Jira已配置好SMTP伺服器,且需開啟“聯絡網站管理員表單”功能。(從WEB介面設計上看,實際上如果沒配置SMTP伺服器,無法開啟此功能)

程式碼追蹤

以Jira Server 7.13.0版本為例。

HTTP請求中的payload如何傳入到服務端的subject物件中?

com.atlassian.jira.config.webwork.JiraActionFactory$SafeParameterSettingActionFactoryProxy#getActionImpl中呼叫setActionParameters方法,通過

webwork.action.ActionContext.getParameters();

拿到HTTP請求的各個引數。

 圖片

參考:https://docs.atlassian.com/DAC/javadoc/opensymphony-webwork/1.4-atlassian-17/reference/webwork/action/ActionContext.html 通過反射呼叫com.atlassian.jira.web.action.user.ContactAdministrators物件的setSubject(java.lang.String)方法,並將HTTP請求中的Subject引數傳入進去。 圖片圖片設定完之後,com.atlassian.jira.web.action.user.ContactAdministrators物件的subject屬性已被設定成我們的payload。

payload如何傳入到郵件物件的Subject中被Velocity模板引擎解析?

部分呼叫過程為:

...
=> com.atlassian.jira.web.action.user.ContactAdministrators#doExecute
=> com.atlassian.jira.web.action.user.ContactAdministrators#send
=> com.atlassian.jira.web.action.user.ContactAdministrators#sendTo
=> com.atlassian.jira.mail.builder.Email.Builder#withSubject(使用者可控的Subject模板)
=> com.atlassian.jira.mail.builder.Email.Builder#renderLater
...

看具體程式碼

 圖片

    protected String doExecute() throws Exception {        if (!this.getShouldDisplayForm()) { //若“聯絡管理員表單”功能已開啟,則進入else邏輯
            return "modebreach";
        } else {            this.send();            return this.getRedirect("/secure/MyJiraHome.jspa");
        }
    }    public boolean getShouldDisplayForm() {        //判斷“聯絡管理員表單”功能是否開啟
        return this.jiraContactHelper.isAdministratorContactFormEnabled();
    }

在send方法中,遍歷系統所有管理員賬號,看是否存在已啟用的管理員,若至少有一個(administrator.isActive()),則傳入該管理員物件,呼叫:this.sendTo(administrator)。 圖片

    public void send() throws MailException {
        Collection<ApplicationUser> administrators = this.userUtil.getJiraAdministrators();        // 遍歷系統所有管理員賬號,看是否存在已啟用的管理員
        Iterator var2 = administrators.iterator();        while(var2.hasNext()) {
            ApplicationUser administrator = (ApplicationUser)var2.next();            if (administrator.isActive()) {//若至少有一個
                this.sendTo(administrator);//傳入該管理員物件
            }
        }

    }

在sendTo中,

    private void sendTo(ApplicationUser administrator) throws MailException {        try {
            Map<String, Object> velocityParams = Maps.newHashMap();            //from為客戶端指定的郵箱地址,會校驗郵箱格式
            velocityParams.put("from", this.replyTo);
            velocityParams.put("content", this.details);
            velocityParams.put("padSize", PADSIZE);            //獲取該傳入管理員的email地址,作為收件郵箱
            Email email = new Email(administrator.getEmailAddress());
            email.setReplyTo(this.replyTo);            //將payload傳入郵件傳送佇列
            MailQueueItem item = (new EmailBuilder(email, this.getMimeType(administrator), I18nBean.getLocaleFromUser(administrator))).withSubject(this.subject).withBodyFromFile(this.getTemplateDirectory(administrator) + "contactadministrator.vm").addParameters(velocityParams).renderLater();            this.mailQueue.addItem(item);
        } catch (Exception var5) {            this.log.error("Error sending JIRA Administrator email", var5);
        }

    }

com.atlassian.jira.mail.builder.EmailBuilder物件 通過withSubject(this.subject)方法傳入使用者指定的模板(template)命令,作為Subject(主題), 圖片

這裡的payload是subjectTemplate,一個TemplateSource$Fragment物件。然後呼叫renderLater(),在atlassian-jira/WEB-INF/classes/com/atlassian/jira/mail/builder/EmailBuilder中,

    public MailQueueItem renderLater() {        return new RenderingMailQueueItem(this.createEmailRenderer());
    }    
    private EmailRenderer createEmailRenderer() {        return new EmailRenderer(this.email, this.subjectTemplate, this.bodyTemplate, this.attachments, this.templateParameters);
    }

將payload傳入,得到一個com.atlassian.jira.mail.builder.EmailRenderer物件,然後把這個EmailRender物件作為引數構造com.atlassian.jira.mail.builder.Email.RenderingMailQueueItem物件。而RenderingMailQueueItem繼承了com.atlassian.mail.queue.SingleMailQueueItem類。在其構造器中,呼叫父類構造器 圖片

SingleMailQueueItem 又繼承了AbstractMailQueueItem類。 圖片

至此,一個RenderingMailQueueItem物件已經構造完成, 得到了一個com.atlassian.mail.queue.MailQueueItem(這是一個介面)物件,然後將該物件加入到郵件傳送佇列com.atlassian.mail.queue.MailQueue中。輪到這個物件對應的郵件傳送時,才會渲染這個待發郵件的Subject(主題),所以可能會有一定的延遲。

郵件傳送執行緒

在啟動郵件佇列的執行緒中,呼叫棧如下:

renderEmailSubject:87, EmailRenderer (com.atlassian.jira.mail.builder)
render:149, EmailRenderer (com.atlassian.jira.mail.builder)
send:29, RenderingMailQueueItem (com.atlassian.jira.mail.builder)
sendBufferUnderLock:103, MailQueueImpl (com.atlassian.mail.queue)
sendBuffer:56, MailQueueImpl (com.atlassian.mail.queue)
apply:51, JiraMailQueue$1 (com.atlassian.jira.mail)
apply:48, JiraMailQueue$1 (com.atlassian.jira.mail)
runWithStaticBaseUrl:110, DefaultVelocityRequestContextFactory (com.atlassian.jira.util.velocity)
runWithStaticBaseUrl:50, DefaultBaseUrl (com.atlassian.jira.util)
sendBuffer:48, JiraMailQueue (com.atlassian.jira.mail)
run:21, MailQueueService (com.atlassian.jira.service.services.mail)
run:68, JiraServiceContainerImpl (com.atlassian.jira.service)
runService:62, ServiceRunner (com.atlassian.jira.service)
runServiceId:44, ServiceRunner (com.atlassian.jira.service)
runJob:32, ServiceRunner (com.atlassian.jira.service)
runJob:153, JobLauncher (com.atlassian.scheduler.core)
launchAndBuildResponse:118, JobLauncher (com.atlassian.scheduler.core)
launch:97, JobLauncher (com.atlassian.scheduler.core)
launchJob:443, CaesiumSchedulerService (com.atlassian.scheduler.caesium.impl)
executeLocalJob:410, CaesiumSchedulerService (com.atlassian.scheduler.caesium.impl)
executeQueuedJob:388, CaesiumSchedulerService (com.atlassian.scheduler.caesium.impl)
consume:285, CaesiumSchedulerService$1 (com.atlassian.scheduler.caesium.impl)
consume:282, CaesiumSchedulerService$1 (com.atlassian.scheduler.caesium.impl)
executeJob:65, SchedulerQueueWorker (com.atlassian.scheduler.caesium.impl)
executeNextJob:59, SchedulerQueueWorker (com.atlassian.scheduler.caesium.impl)
run:34, SchedulerQueueWorker (com.atlassian.scheduler.caesium.impl)
run:745, Thread (java.lang)

主要看前面三個,

renderEmailSubject:87, EmailRenderer (com.atlassian.jira.mail.builder)render:149, EmailRenderer (com.atlassian.jira.mail.builder)send:29, RenderingMailQueueItem (com.atlassian.jira.mail.builder)

RenderingMailQueueItem的send方法中,呼叫了EmailRender的render方法 圖片

atlassian-jira/WEB-INF/classes/com/atlassian/jira/mail/builder/EmailRenderer中:

render
=>renderEmailSubject
=>getTemplatingEngine().render(this.subjectTemplate).applying(contextParams).asPlainText();

使用預設的模板引擎對payload(Velocity模板)進行解析。

注:我們的payload物件subjectTemplate是一個com.atlassian.jira.template.TemplateSource$Fragment物件,通過之前的EmailRender的withSubject(this.subject)方法傳入。而EmailRender的subject私有成員變數是通過反射呼叫ContactAdministratorssetSubject()方法從HTTP請求中傳入(前面已分析)。

圖片

先是getTemplatingEngine()得到一個com.atlassian.jira.template.velocity.DefaultVelocityTemplatingEngine物件,然後呼叫render()方法: 呼叫applying生成一個org.apache.velocity.VelocityContext物件。 呼叫

asPlainText()
=>asPlainText(StringWriter)
=>toWriterImpl()
=>writeEncodedBodyForContent()
=>VelocityEngine.evaluate()

圖片

其中fragment.getContent()就是我們的payload,傳入進writeEncodedBodyForContent方法的第二個引數 圖片

再作為VelocityEngine#evaluate()的第四個引數,在作業系統上成功執行命令。 圖片

具體到Velocity的API呼叫棧可以在catalina日誌裡看到。 圖片

下圖顯示啟動的計算器程式為Jira的子程式。 

圖片

 

漏洞修復

下載7.13.5版本程式碼,對比atlassian-jira/WEB-INF/classes/com/atlassian/jira/web/action/user/ContactAdministrators.java檔案

圖片

雖然使用者輸入的Subject為惡意payload,作為引數傳進了 atlassian-jira/WEB-INF/classes/com/atlassian/jira/web/action/user/ContactAdministrators物件, 但是其subjectTemplate為硬編碼的模板$subject,並沒有將使用者傳入的payload直接傳入這裡的withSubject方法。 圖片

於是無法在這裡觸發。

 

Demo

無需認證觸發

實際觸發時間可能為幾秒到幾十秒(終於錄屏到一個快的作為演示...)

 圖片

後臺管理員許可權觸發

圖片

 

緩解措施

若無法及時升級Jira,可採取以下緩解措施:

  • 1、禁用”聯絡網站管理員“功能。設定=> 系統=> 編輯設定=> 聯絡管理員表單處選擇“關”,然後點選最下面的“更新”儲存設定。具體操作方式參考:https://confluence.atlassian.com/adminjiraserver/configuring-the-administrator-contact-form-974375905.html#Configuringtheadministratorcontactform-DisablingtheContactAdministratorsForm

驗證生效方法:訪問/secure/ContactAdministrators!default.jspa 出現:“您的Jira管理員尚未配置此聯絡表。”或“Your Jira administrator has not yet configured this contact form”。 圖片

  • 2、並且,禁止對/secure/admin/SendBulkMail!default.jspa的訪問。

參考

  • https://confluence.atlassian.com/jira/jira-security-advisory-2019-07-10-973486595.html

  • https://jira.atlassian.com/browse/JRASERVER-69532

 

相關文章