Background
Affected Product Summary
NetAlertX is an open-source Wi-Fi / Local Area Network (LAN) intruder detector that scans for devices connected to your network and alerts you if new and unknown devices are found.
As stated on their website, it offers “a user-friendly solution to monitor your (home or business) network, ensuring peace of mind and enhanced security”.
With more than 3k stars on the repo and 1.4 million pulls from Dockerhub, it is a popular tool for protecting SOHO network environments.
Vendor: https://netalertx.com/
Product: NetAlertX
Confirmed Vulnerable Version:
- Command Injection: v23.01.14 – v24.9.12
- Arbitrary file read: v24.7.18 -v24.9.12
Fixed Version: v24.10.12
Product Link: https://github.com/jokob-sk/NetAlertX
NetAlertX as a LAN Intrusion Detection Tool
The NetAlertX web component is developed in a combination of PHP and Python and can be deployed in a docker container. At the time of writing this blog there were 15 public instances identified online (one of which was identified belonging to a public research university). This was surprising to us, as we expected that most of these applications would only be accessible from the LAN rather than the internet. For deployments with internet facing access, compromising these would allow an attacker to pivot to the LAN. Furthermore, by compromising a server that is responsible for detecting intruders, attackers could fly under the radar while attacking the LAN.
In this blog post, I will be discussing an unauthenticated command injection vulnerability that was discovered during a security audit of the codebase.
The proof-of-concept exploit can be found in our CVE Github repository: https://github.com/RhinoSecurityLabs/CVE-Private/blob/master/CVE-2024-46506
Remediation
The initial patch commit for this CVE can be found in this commit on the NetAlertX repository. After the initial patch was released, there were 2 bypasses discovered which we communicated with the NetAlertX team. You can find the patch bypass fix here.
CVE-2024-46506: Unauthenticated command injection via settings update
NetAlertX supports plugins, which are for tasks such as scanning the network, sending an IM notification, or doing a database backup. The administrative interface provides a webpage to modify these settings:
As shown in the above image, there is a box for a command to be executed on the server (which may be susceptible to command injection) but it is grayed out. However, it was discovered that there were no actual controls on the backend to prevent someone from updating this value. More concerningly, the backend does not verify authentication on this request.
Looking at the code for this, the graphical interface for the panel, /settings.php implements authorization checks with a checkPermissions() call.
But the /settings.php file isn’t actually responsible for updating the settings, it only contains the javascript to make the request to another PHP file which updates the NetAlertX settings on the backend.
The image above shows that util.php is called from the settings.php script. util.php doesn’t perform authentication checks like settings.php did. This means checkPermissions() is never called and you can update settings by directly requesting util.php.
As shown in the image above, there are no authentication checks between the beginning of the file and where saveSettings() is called. If we send a POST request with the data “function=savesettings”, we can successfully call this function with no authentication. The saveSettings() function will update the plugin command and automatically reload the application, which involves executing each plugin that is set to execute on initialization. This results in RCE!
CVE-2024-48766: Unauthenticated Arbitrary File Read
A similar issue was also identified with the logs.php endpoint. Attackers could request a log file and read sensitive files due to no authentication being required and a path traversal vulnerability:
curl -X POST http://192.168.1.33:20211/php/components/logs.php -d 'items=[{"buttons":[{"labelStringCode":"Maint_PurgeLog","event":"logManage('app.log', 'cleanLog')"},{"labelStringCode":"Maint_RestartServer","event":"askRestartBackend()"}],"fileName":"passwd","filePath":"/app/front/../../../../../../etc/passwd","textAreaCssClass":"logs"}]'
The image above shows the content retrieval of an unsanitized filepath, which is eventually added to the HTML.
The initial patch commit bypasses
Upon the patch being committed we decided to review it for any potential bypasses/vulnerabilities. Authentication checks were implemented by performing a require_once() call against a security.php file.
We must now take a closer look at security.php. If we can get past this require_once() call, we can continue to exploit any vulnerable functionality within the application.
Bypass 1: Execute After Redirect (EAR) leads to authentication bypass
At the bottom of the security.php file, various checks will be performed to determine whether to allow a request to continue or block them. However, simply serving a “Location” header to the client is insufficient to actually block a request, as a HTTP client can simply ignore the redirect.
To exploit this bypass, we don’t need to change the original curl command for the LFI vulnerability. To fix this, the application will need to call “exit;” after sending the Location header to the client.
curl -X POST http://192.168.1.33:20211/php/components/logs.php -d 'items=[{"buttons":[{"labelStringCode":"Maint_PurgeLog","event":"logManage('app.log', 'cleanLog')"},{"labelStringCode":"Maint_RestartServer","event":"askRestartBackend()"}],"fileName":"passwd","filePath":"/app/front/../../../../../../etc/passwd","textAreaCssClass":"logs"}]'
Bypass 2: Insecure usage of strpos() leads to authentication bypass
security.php will allow the client to continue if they are properly authenticated OR if they are trying to login (as blocking unauthenticated requests for login would prevent legitimate users from logging in). security.php will check if the client is requesting the login page by using the strpos() function.
We can see in the 3rd if statement that the request will allowed to continue if 1 of 3 conditions is true:
- The session variable “login” is set to true (we have no control over this)
- isLogonPage is set to true (can we control this?)
- If we have a cookie named “NetAlertX_SaveLogin” set and the value is equal to the hash of the admin password (we would need to know the admin’s password for this)
The vulnerability arises due to the fact that we can control the value of isLogonPage by abusing the behavior of the strpos() call. Since strpos() is position independent and is called against the entire URL including parameters, we can simply add a parameter to the end of the URL and set isLogonPage to be true (despite not requesting the login page, allowing us to bypass the authentication checks).
curl -X POST http://192.168.1.33:20211/php/components/logs.php?bypass=index.php -d 'items=[{"buttons":[{"labelStringCode":"Maint_PurgeLog","event":"logManage('app.log', 'cleanLog')"}, {"labelStringCode":"Maint_RestartServer","event":"askRestartBackend()"}],"fileName":"passwd","filePath":"/app/front/../../../../../../etc/passwd","textAreaCssClass":"logs"}]'
The maintainers took our concerns into account and made another commit which fixed the aforementioned issues. The new version of NetAlertX was then released with both patches.
Conclusion
We found CVE-2024-46506 while auditing the codebase of NetAlertX due to its interesting position within a LAN. We leveraged an unauthenticated command injection vulnerability to achieve RCE on the application server and gain access to the LAN. We reviewed the initial patch and communicated additional bypasses with the NetAlertX team which were quickly fixed. We want to thank NetAlertX for working with us to remediate this vulnerability.
We have added a proof-of-concept to our CVE Github repository that demonstrates this vulnerability. As always, feel free to follow us on Twitter or LinkedIn and join our Discord server for more releases and blog posts.
Twitter: https://twitter.com/rhinosecurity
LinkedIn: https://www.linkedin.com/company/rhino-security-labs/
Discord: https://discord.gg/TUuH26G5
Researcher/Author: https://x.com/_chebuya