Author: p0wd3r (知道創宇404安全實驗室)

Date: 2016-10-17

0x00 漏洞概述

1.漏洞簡介

Spring Security OAuth是為Spring框架提供安全認證支持的一個模塊,在7月5日其維護者發布了這樣一個升級公告,主要說明在用戶使用Whitelabel views來處理錯誤時,攻擊者在被授權的情況下可以通過構造惡意參數來遠程執行命令。漏洞的發現者在10月13日公開了該漏洞的挖掘記錄

2.漏洞影響

授權狀態下遠程命令執行

3.影響版本

2.0.0 to 2.0.9

1.0.0 to 1.0.5

0x01 漏洞復現

1. 環境搭建

docker pull maven
FROM maven

WORKDIR /tmp/
RUN wget http://secalert.net/research/cve-2016-4977.zip
RUN unzip cve-2016-4977.zip
RUN mv spring-oauth2-sec-bug/* /usr/src/mymaven

WORKDIR /usr/src/mymaven
RUN mvn clean install

CMD ["java", "-jar", "./target/demo-0.0.1-SNAPSHOT.jar"]
docker build -t mvn-spring .
docker run --rm --name mvn-spring-app -p 8080:8080 mvn-spring

2.漏洞分析

首先我們查看src/resources/application.properties的內容來獲取clientid和用戶的密碼:

Alt text

接著我們訪問這個url:

http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=hellotom

其中client_id就是我們前面獲取到的,然后輸入用戶名user,密碼填上面的password

點擊登錄后程序會返回這樣一個頁面:

Alt text

可以看到由于hellotom對于redirect_uri來說是不合法的值,所以程序會將錯誤信息返回并且其中帶著hellotom,那么這個不合法的值可不可以是一個表達式呢?我們再訪問這個url:

http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${2334-1}

結果如下:

Alt text

可以看到表達式被執行,觸發了漏洞。

下面看代碼,由于程序使用Whitelabel作為視圖來返回錯誤頁面,所以先看/spring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/WhitelabelErrorEndpoint.java中第18-40行:

@FrameworkEndpoint
public class WhitelabelErrorEndpoint {

    private static final String ERROR = "<html><body><h1>OAuth Error</h1><p>${errorSummary}</p></body></html>";

    @RequestMapping("/oauth/error")
    public ModelAndView handleError(HttpServletRequest request) {
        Map<String, Object> model = new HashMap<String, Object>();
        Object error = request.getAttribute("error");
        // The error summary may contain malicious user input,
        // it needs to be escaped to prevent XSS
        String errorSummary;
        if (error instanceof OAuth2Exception) {
            OAuth2Exception oauthError = (OAuth2Exception) error;
            errorSummary = HtmlUtils.htmlEscape(oauthError.getSummary());
        }
        else {
            errorSummary = "Unknown error";
        }
        model.put("errorSummary", errorSummary);
        return new ModelAndView(new SpelView(ERROR), model);
    }
}

這里定義了Whitelabel對錯誤的處理方法,可以看到程序通過oauthError.getSummary()來獲取錯誤信息,我們再次訪問這個url并開啟動態調試:

http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${2334-1}

Alt text

請求中的${2334-1}已經被帶入了errorSummary中,然后errorSummary被裝入model中,再用SpelView進行渲染。

我們跟進SpelViewspring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java中第21-54行:

class SpelView implements View {

    ...

    public SpelView(String template) {
        this.template = template;
        this.context.addPropertyAccessor(new MapAccessor());
        this.helper = new PropertyPlaceholderHelper("${", "}");
        this.resolver = new PlaceholderResolver() {
            public String resolvePlaceholder(String name) {
                Expression expression = parser.parseExpression(name);
                Object value = expression.getValue(context);
                return value == null ? null : value.toString();
            }
        };
    }

    ...

    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ...
        String result = helper.replacePlaceholders(template, resolver);
        ...
    }
}

可以看到在render時通過helper${}中的值作為表達式,再用parser.parseExpression來執行,跟進一下replacePlaceholders這個函數,在/org/springframework/util/PropertyPlaceholderHelper.class第47-56行:

public String replacePlaceholders(String value, final Properties properties) {
    Assert.notNull(properties, "\'properties\' must not be null");
    return this.replacePlaceholders(value, new PropertyPlaceholderHelper.PlaceholderResolver() {
        public String resolvePlaceholder(String placeholderName) {
            return properties.getProperty(placeholderName);
        }
    });
}

這個函數是個遞歸,也就是說如果表達式的值中有${xxx}這樣形式的字符串存在,就會再取xxx作為表達式來執行。

我們看動態調試的結果:

Alt text

首先因為傳入了${errorSummary},取errorSummary作為表達式來執行,繼續執行程序:

Alt text

由于errorSummary中存在${2334-1},所以又取出了2334-1作為表達式來執行,從而觸發了漏洞。所以從這里可以看出,漏洞的關鍵點在于這個對表達式的遞歸處理使我們可控的部分也會被當作表達式執行。

3.補丁分析

Alt text

可以看到在第一次執行表達式之前程序將$替換成了由RandomValueStringGenerator().generate()生成的隨機字符串,也就是${errorSummary} -> random{errorSummary},但是這個替換不是遞歸的,所以${2334-1}并沒有變。

然后創建了一個helper使程序取random{}中的內容作為表達式,這樣就使得errorSummary被作為表達式執行了,而${2334-1}因為不符合random{}這個形式所以沒有被當作表達式,從而也就沒有辦法被執行了。

不過這個Patch有一個缺點:RandomValueStringGenerator生成的字符串雖然內容隨機,但長度固定為6,所以存在暴力破解的可能性。

0x02 修復方案

  • 使用1.0.x版本的用戶應放棄在認證通過和錯誤這兩個頁面中使用Whitelabel這個視圖。
  • 使用2.0.x版本的用戶升級到2.0.10以及更高的版本

0x03 參考

https://www.seebug.org/vuldb/ssvid-92474

http://secalert.net/#CVE-2016-4977

https://pivotal.io/de/security/cve-2016-4977

https://github.com/spring-projects/spring-security-oauth/commit/fff77d3fea477b566bcacfbfc95f85821a2bdc2d

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/70/