前言:在之前沒有學習就聽到過了Shiro的權限繞過,也就是通過特定字符來繞過Shiro框架自身的權限判斷,現在自己也還沒有能力去了解這個框架,但是對於權限繞過的原理,框架只是單純的封裝了,本質還是函數的邏輯漏洞,自己這里還沒有去分析框架,所以這邊就先去了解這些函數的權限繞過!
參考文章:https://www.cnblogs.com/nice0e3/p/14801884.html
參考文章:https://forum.butian.net/share/829
Tomcat的getRequestURI
../ 繞過方式
我們先把要代碼放上來,web環境結構:
DemoFilter代碼:
public class DemoFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String uri = request.getRequestURI();// /system/login
StringBuffer requestURL = request.getRequestURL(); // http://localhost:8080/system/login
System.out.println("requestURL: " + requestURL);
System.out.println("getRequestURI: " + uri);
if(uri.startsWith("/system/login")) {
//登陸接口設置⽩白名單,即登錄頁面
System.out.println("this is login page...");
resp.getWriter().write("this is login page...\n");
chain.doFilter(req, resp);
} else if(uri.endsWith(".do") || uri.endsWith(".action")) {
//檢測當前⽤戶是否登陸
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
resp.getWriter().write("unauthorized access\n"); //未授權訪問
System.out.println("unauthorized access");
return;
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
User pojo
public class User {
public User(){
}
}
AdminServlet
public class AdminServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("this is admin page...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
LoginServlet
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("login!!!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml映射關系:
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.zpchcbd.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/system/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>admin</servlet-name>
<servlet-class>com.zpchcbd.servlet.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>admin</servlet-name>
<url-pattern>/login/admin.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>demo</filter-name>
<filter-class>com.zpchcbd.filter.DemoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>demo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
web啟動環境如下:
訪問:http://localhost:8080/system/login
訪問:http://localhost:8080/login/admin.do
到了這里,就先回到主題,關於../
訪問方式繞過問題,為什么可以進行繞過?原因肯定就是相關函數在對路徑的處理時產生了邏輯問題
在Java中通常會使用request.getRequestURL()和request.getRequestURI()這兩個方法獲取請求路徑,然后對請求路徑做校驗。
我們這里主要是對getRequestURI這個函數進行研究,權限繞過也是發生在這個地方。
在代碼里面我們也有對請求的路徑進行打印,如下圖所示:
訪問:http://localhost:8080/system/login
訪問:http://localhost:8080/login/admin.do
當訪問了/login/admin.do
的時候顯示的是未授權,但是這里的話我們可以通過../
的方式來進行繞過!
我們這里訪問:http://127.0.0.1:8080/system/login/../../login/admin.do
,來看下這兩個函數是如何獲取的?
上面發現還是不行,那我們放在burp中來進行測試?
這里是有坑的,原因就是瀏覽器會自動進行解析路由的問題,它會直接幫你幫你把../
先進行解析,最后再進行訪問,所以這個點需要注意,也是自己需要注意的問題。
原因分析
這里就來調試看下它的流程是怎么走的
首先如下打上斷點
最后獲取的URI為/system/login/../../login/admin.do
最終導致了繞過
URL截斷繞過
如果還是在面對../
的情況下,但是發現./
被過濾了,主要的邏輯代碼如下:
if(uri.contains("./")){
resp.getWriter().write("error");
return;
}
那么當前的Filter實現為如下
public class DemoFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String uri = request.getRequestURI();// /system/login
StringBuffer requestURL = request.getRequestURL(); // http://localhost:8080/system/login
System.out.println("requestURL: " + requestURL);
System.out.println("getRequestURI: " + uri);
if(uri.contains("./")){
resp.getWriter().write("error");
return;
}
if(uri.startsWith("/system/login")) {
//登陸接口設置⽩白名單,即登錄頁面
System.out.println("this is login page...");
resp.getWriter().write("this is login page...\n");
chain.doFilter(req, resp);
} else if(uri.endsWith(".do") || uri.endsWith(".action")) {
//檢測當前⽤戶是否登陸
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
resp.getWriter().write("unauthorized access\n"); //未授權訪問
System.out.println("unauthorized access");
return;
}
}
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
這里我們再進行之前的訪問看看還能不能進行權限繞過,如下圖所示,發現直接回顯了一個error,所以在面對這種情況的過濾,之前的繞過已經不行了
這里繼續來進行繞過,可以進行訪問:http://127.0.0.1:8080/login/admin.do;
可以發現,成功繞過了,那么這個原因又是什么呢?
原因分析
URL中有一個保留字符分號(;),主要作為參數分隔符進行使用,有時候是請求中傳遞的參數太多了,所以使用分號(;)將參數對(key=value)連接起來作為一個請求參數進⾏傳遞。
直接在URI中引入分隔符,正常來說是不會對實際接口的訪問造成影響的。
這里就來調試看下它的流程是怎么走的,老樣子如下圖所示先打個斷點
首先第一個判斷,由於不存在./
符號,所以這里可以繞過
第二個判斷,由於結尾不是.do
和.action
來結尾,所以這里也可以進行繞過
最后成功來到了要執行的地方
再來看看 /login;Bypass/admin.do;Bypass
,發現同樣也可以,那么這個又如何理解呢?
其實一樣的,我們只要看是否存在./
和結尾是否是.do
和.action
,那么都不符合就直接繞過了
這里的話也只適合我們寫的代碼,真實環境不一定可以完美符合,就比如來驗證了路徑是否正確呢?那就不行了
URL編碼繞過
還是上面的代碼,訪問地址 http://127.0.0.1:8080/login/%61%64%6d%69%6e%2e%64%6f
當filter處理完相關的流程后,中間件會對請求的URL進行一次URL解碼操作,然后再找到對應的Servlet進行訪問。也就是說嘗試對我們訪問的URL進行一次URL編碼,中間件是可以正常解析並完成正常業務的
原因分析
tomcat中間件什么時候會做url編碼的操作呢?
通過調試會發現在進行url解碼的時候是在org.apache.catalina.connector.CoyoteAdapter#postParseRequest執行的,如下所示
在org.apache.tomcat.util.buf.UDecoder#convert方法中會對url編碼的數據進行url解碼的操作,如下圖所示會先找到百分號的索引的位置
如果百分號后面的兩個字符都是存在的,那么則會通過x2c方法進行轉換,可以看到就是將十六進制的數據轉換為十進制的數據然后返回
private static int x2c(byte b1, byte b2) {
int digit = b1 >= 65 ? (b1 & 223) - 65 + 10 : b1 - 48;
digit *= 16;
digit += b2 >= 65 ? (b2 & 223) - 65 + 10 : b2 - 48;
return digit;
}
接着下繼續講框架的權限問題,這里就先停下,到時候自己學到了會在這里繼續補上
=2021-7-18更新=
shiro框架的權限繞過問題:https://www.cnblogs.com/zpchcbd/p/15023056.html
=2023-1-21更新=
補上spring中的權限繞過的常見點
Spring
URL編碼繞過
參考文章:https://www.cnblogs.com/zpchcbd/p/17056667.html
分析原因
從這邊開始跟所有的spring請求都會從org.springframework.web.servlet.DispatcherServlet#doDispatch經過
這邊主要就是看如下三個處理函數
decodeAndCleanUriString:492, UrlPathHelper (org.springframework.web.util)
getRequestUri:381, UrlPathHelper (org.springframework.web.util)
getPathWithinApplication:297, UrlPathHelper (org.springframework.web.util)
getLookupPathForRequest:185, UrlPathHelper (org.springframework.web.util)
getHandlerInternal:124, AbstractUrlHandlerMapping (org.springframework.web.servlet.handler)
getHandler:396, AbstractHandlerMapping (org.springframework.web.servlet.handler)
getHandler:1237, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1019, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
removeSemicolonContent 清除分號和斜杠之間的數據
decodeRequestString 路徑url解碼,權限繞過就出現在這邊
getSanitizedPath 去除雙斜杠轉化為單斜杠