Java安全之Filter權(quán)限繞過(guò)的實(shí)現(xiàn)
前言
在一些需要挖掘一些無(wú)條件RCE中,大部分類似于一些系統(tǒng)大部分地方都做了權(quán)限控制的,而這時(shí)候想要利用權(quán)限繞過(guò)就顯得格外重要。在此來(lái)學(xué)習(xí)一波權(quán)限繞過(guò)的思路。
0x01 權(quán)限控制實(shí)現(xiàn)
常見(jiàn)的實(shí)現(xiàn)方式,在不調(diào)用Spring Security、Shiro等權(quán)限控制組件的情況下,會(huì)使用Filter獲取請(qǐng)求路徑,進(jìn)行校驗(yàn)。
編寫一個(gè)servlet
package com.nice0e3; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/helloServlet") public class helloServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("hello!!!"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
定義一個(gè)Filter
package com.nice0e3.filter; import com.nice0e3.User; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter("/*") public class demoFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; String uri = request.getRequestURI(); StringBuffer requestURL = request.getRequestURL(); System.out.println(requestURL); if(uri.startsWith("/system/login")) { //登陸接口設(shè)置⽩白名單,即登錄頁(yè)面 System.out.println("login_page"); resp.getWriter(). write("login_page"); chain.doFilter(request, resp); } else if(uri.endsWith(".do")||uri.endsWith(".action")) { //檢測(cè)當(dāng)前⽤戶是否登陸 User user =(User) request.getSession().getAttribute("user"); if(user == null) { resp.getWriter(). write("unauthorized access"); //未授權(quán)訪問(wèn) System.out.println("unauthorized access"); resp.getWriter(). write("go to login_page");//跳轉(zhuǎn)登錄 System.out.println("go to login_page"); } } } public void init(FilterConfig config) throws ServletException { } }
這里使用 request.getRequestURI();
獲取URI為 /system/login
開(kāi)頭 則直接放行。結(jié)尾,為.do
和.action
的請(qǐng)求去做校驗(yàn),獲取session有沒(méi)有user的值,沒(méi)有的話即返回unauthorized access
,如果不為.do
和.action
的請(qǐng)求或session中存在user即放行。
訪問(wèn)main頁(yè)面,顯示未授權(quán)訪問(wèn)并且跳轉(zhuǎn)到登錄的頁(yè)面
在Java中通常會(huì)使用request.getRequestURL()
和request.getRequestURI()
這兩個(gè)方法獲取請(qǐng)求路徑,然后對(duì)請(qǐng)求路徑做校驗(yàn)。
../
繞過(guò)方式
這里采用../
的方式繞過(guò)
這里就繞過(guò)了,權(quán)限控制,直接能訪問(wèn)到main,而不是顯示未授權(quán)訪問(wèn)。在繞過(guò)時(shí)候可以找一些白名單的路徑,然后使用../
去繞過(guò)。
payload:/system/login/../../login/main.do
繞過(guò)原理分析
上圖可以看到我們前面為system/login
開(kāi)頭
符合匹配的規(guī)則,而匹配上該規(guī)則后則是直接放行,讓系統(tǒng)認(rèn)為訪問(wèn)路徑是一個(gè)登錄的路徑,但在后面加入2個(gè)../
進(jìn)行跳轉(zhuǎn)到根目錄,并且拼接上login/main.do
,這時(shí)候?qū)嶋H訪問(wèn)到的是http://127.0.0.1/login/main.do
。
但使用
StringBuffer requestURL = request.getRequestURL(); if(requestURL.toString().startsWith("/system/login"))
request.getRequestURL();
該方法獲取URL是攜帶http://127.xxx
等信息的。其實(shí)這里比較廢話,因?yàn)轵?yàn)證首部的字符路徑的話,使用 request.getRequestURI();
來(lái)獲取請(qǐng)求路徑部分來(lái)校驗(yàn)。
URL截?cái)嗬@過(guò)
基于前面Filter代碼將../
進(jìn)行過(guò)濾
package com.nice0e3.filter; import com.nice0e3.User; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter("/*") public class demoFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; String uri = request.getRequestURI(); if(uri.contains("./")){ resp.getWriter().write("error"); return; } StringBuffer requestURL = request.getRequestURL(); System.out.println(requestURL); if(uri.startsWith("/system/login")) { //登陸接口設(shè)置⽩白名單,即登錄頁(yè)面 System.out.println("login_page"); resp.getWriter(). write("login_page"); chain.doFilter(request, resp); } else if(uri.endsWith(".do")||uri.endsWith(".action")) { //檢測(cè)當(dāng)前⽤戶是否登陸 User user =(User) request.getSession().getAttribute("user"); if(user == null) { resp.getWriter(). write("unauthorized access"); //未授權(quán)訪問(wèn) System.out.println("unauthorized access"); resp.getWriter(). write("go to login_page");//跳轉(zhuǎn)登錄 System.out.println("go to login_page"); } } chain.doFilter(request,resp); } public void init(FilterConfig config) throws ServletException { } }
添加多了一個(gè)uri.contains("./")
做過(guò)濾只要包含./
字符直接報(bào)錯(cuò)。
這時(shí)候會(huì)報(bào)錯(cuò),可見(jiàn)上圖。可;
進(jìn)行繞過(guò)
payload:/login/main.do;123
繞過(guò)分析
URL中有一個(gè)保留字符分號(hào);
,主要為參數(shù)進(jìn)行分割使用,有時(shí)候是請(qǐng)求中傳遞的參數(shù)太多了,所以使用分號(hào);
將參數(shù)對(duì)(key=value)連接起來(lái)作為一個(gè)請(qǐng)求參數(shù)進(jìn)⾏傳遞。
再來(lái)看到代碼,代碼中識(shí)別.do
和.action
的后綴的字符,而加入;
加上隨便內(nèi)容后,代碼中就識(shí)別不到了。則會(huì)走到最下面的chain.doFilter(request,resp);
,而在后面添加;
分號(hào)不會(huì)對(duì)地址的訪問(wèn)有任何影響。
多/
繞過(guò)
創(chuàng)建一個(gè)后臺(tái)接口,只允許admin用戶登錄訪問(wèn)
package com.nice0e3.Servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/system/UserInfoSearch.do") public class UserInfoServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("admin_login!!!"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
而權(quán)限控制這步肯定是在Filter里面實(shí)現(xiàn)
String uri = request.getRequestURI(); if(uri.equals("/system/UserInfoSearch.do")){ User user =(User) request.getSession().getAttribute("user"); String role = user.getRole(); if(role.equals("admin")) { //當(dāng)前⽤用戶為admin,允許訪問(wèn)該接⼝ chain.doFilter(request, resp); } else { resp.getWriter().write("Unauthorized"); return; } }
這時(shí)候去對(duì)/system/UserInfoSearch.do
做了校驗(yàn),獲取URI地址后匹配如果是這個(gè)/system/UserInfoSearch.do
,則驗(yàn)證用戶身份,加入不為admin,則顯示Unauthorized
,越權(quán)訪問(wèn)。
可直接訪問(wèn)到admin用戶才可訪問(wèn)的頁(yè)面下。
payload: //system/UserInfoSearch.do;123
繞過(guò)分析
看到代碼中只是對(duì)比了URI是否為/system/UserInfoSearch.do
,而多加一個(gè)/
并不影響正常解析,而又能讓該規(guī)則匹配不到。
URL編碼繞過(guò)
還是用上面的代碼演示,繞過(guò)手法則是換成url編碼繞過(guò)的方式。
payload:/system/%55%73%65%72%49%6e%66%6f%53%65%61%72%63%68%2e%64%6f
繞過(guò)分析
當(dāng)Filter處理完相關(guān)的流程后,中間件會(huì)對(duì)請(qǐng)求的URL進(jìn)行一次URL解碼操作,然后請(qǐng)求解碼后的Servlet,而在request.getRequestURL(
)和request.getRequestURI()
中并不會(huì)自動(dòng)進(jìn)行解碼,所以這時(shí)候直接接收過(guò)來(lái)進(jìn)行規(guī)則匹配,則識(shí)別不出來(lái)。這時(shí)候?qū)е铝死@過(guò)。
Spring MVC中追加/
繞過(guò)
在SpringMVC中假設(shè)以如下方法配置:
<servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
特定情況下Spring匹配web路徑的時(shí)候會(huì)容錯(cuò)后面的/
如,/admin/main.do/
修復(fù)
使用該代碼接受URI
String uri1 = request.getServletPath() + (request.getPathInfo() != null ? request.getPathInfo() : "");
下面來(lái)嘗試前面的幾種繞過(guò)方式。
分號(hào)階段繞過(guò) payload: /login/main.do;123
多/
繞過(guò)payload: //system/UserInfoSearch.do;123
URL編碼繞過(guò)payload:/system/%55%73%65%72%49%6e%66%6f%53%65%61%72%63%68%2e%64%6f
../
繞過(guò)payload:/system/login/../../login/main.do
均不可用,使用上面的方式接受URI后,接受過(guò)去的時(shí)候發(fā)送特殊字符一律被剔除了。打斷點(diǎn)可見(jiàn)。
關(guān)注點(diǎn)
前面提到過(guò)request.getRequestURL()
和request.getRequestURI()
,這些危險(xiǎn)字符并不會(huì)自動(dòng)剔除掉。可重點(diǎn)關(guān)注該方法。
參考
https://blog.csdn.net/qq_38154820/article/details/106799046
0x02 結(jié)尾
不只是Filter里面可以做權(quán)限繞過(guò),在使用到一些Shiro框架的時(shí)候,也會(huì)有一些權(quán)限繞過(guò)的方式。
到此這篇關(guān)于Java安全之Filter權(quán)限繞過(guò)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java Filter權(quán)限繞過(guò)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot中自動(dòng)化配置的利弊以及解決方法
這篇文章主要給大家介紹了關(guān)于Spring Boot中自動(dòng)化配置的利弊以及解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-08-08詳解如何在Spring中為@Value注解設(shè)置默認(rèn)值
在Spring開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到需要從配置文件中讀取屬性的情況,@Value注解是Spring提供的一種便捷方式,能夠讓我們輕松地將配置文件中的屬性注入到Spring Bean中,2024-10-10Spring實(shí)現(xiàn)擁有者權(quán)限驗(yàn)證的方法示例
這篇文章主要介紹了Spring實(shí)現(xiàn)擁有者權(quán)限驗(yàn)證的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Springboot基礎(chǔ)之RedisUtils工具類
本文來(lái)說(shuō)下RedisUtils工具類,主要介紹了整合Redis、MyBatis,封裝RedisUtils工具類等知識(shí),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05scala當(dāng)中的文件操作和網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)方法
這篇文章主要介紹了scala當(dāng)中的文件操作和網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06java反射的作用知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是關(guān)于java反射的作用知識(shí)點(diǎn)總結(jié),需要的朋友們可以學(xué)習(xí)下。2020-02-02Spring Boot 配置和使用多線程池的實(shí)現(xiàn)
這篇文章主要介紹了Spring Boot 配置和使用多線程池的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06