Java Tomcat 和 Spring中的 getRequestURI權限繞過


前言:在之前沒有學習就聽到過了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 去除雙斜杠轉化為單斜杠


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM