We're trying to match our Filezilla Server 0.9.6 user passwords to those that the user chooses for logging into our site with. All googling points to old solutions that implement MD5, but the newer versions use SHA512 and Salt. The below code successfully updates the Server XML file, but the new passwords are not working after restarting Filezilla.
Does anyone have experience in this, and can point out where this code is incorrect?
My Code From https://forum.filezilla-project.org/viewtopic.php?t=39934
<?php
$password = "myNewPassword";
$seed = str_split("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
shuffle($seed);
$rand = '';
foreach (array_rand($seed, 64) as $k) {
$rand .= $seed[$k];
}
$salt_raw = utf8_encode($rand);
$salt_html = htmlentities($salt_raw);
$password = utf8_encode($password);
$salted_password = $password . $salt_raw;
$encoded_password = strtoupper(hash('SHA512', $salted_password));
// For Filezilla
$FzPass = $encoded_password;
$FzSalt = $salt_html;
$xmlFile = 'C:\path-to\FileZilla Server.xml';
$xml = simplexml_load_file($xmlFile);
$Pass = $xml->xpath("//User[@Name='TheUserName']//Option[@Name='Pass']");
$Salt = $xml->xpath("//User[@Name='TheUserName']//Option[@Name='Salt']");
$Pass[0][0] = $FzPass;
$Salt[0][0] = $FzSalt;
$xml->formatOutput = true;
$saved = $xml->asXML($xmlFile);
chdir( 'C:\FileZilla Server' );
exec( '"C:\FileZilla Server\FileZilla Server.exe" /reload-config' );
Before the PHP execution Filezilla Server.xml
<?xml version="1.0" ?>
<FileZillaServer>
<Users>
<User Name="TheUserName">
<Option Name="Pass">1ED89AFBA4FD8EF177EAAF21E6641C33FC870860D5F399CD9B0A79632B0F2B08B235DB7258DB2B6EB58402AF29A4C8491CC8F82ED9D2E74EABA8F7B240D0615F</Option>
<Option Name="Salt">-Z5c@tci:e?E4E@H@(cq)/W1zDMs#F>XsYd$M)0t#{/s,tc@]GJYf@{@T.&O*5sF</Option>
<Option Name="Group"></Option>
<Option Name="Bypass server userlimit">1</Option>
<Option Name="User Limit">0</Option>
<Option Name="IP Limit">0</Option>
<Option Name="Enabled">1</Option>
<Option Name="ForceSsl">0</Option>
</User>
</Users>
</FileZillaServer>
After the PHP execution
<?xml version="1.0" ?>
<FileZillaServer>
<User Name="producer">
<Option Name="Pass">57AECC6BBCCC8EE5A73F0E362E2434DBF617EC2C85404205D881B1B7A2D303405789F8B42DFE61EDD7CF197700B66307E998054615C8A9A1CA5A9988029F0EAD</Option>
<Option Name="Salt">P,]mk&amp;4N;v3`#&lt;e8S?oj[U=Ja$}Q6~:\Z1{HuB&gt;bnI/&quot;M-pDh.qFylKfd0|LgT*G</Option>
<Option Name="Group"/>
<Option Name="Bypass server userlimit">1</Option>
<Option Name="User Limit">0</Option>
<Option Name="IP Limit">0</Option>
<Option Name="Enabled">1</Option>
<Option Name="ForceSsl">0</Option>
</User>
</FileZillaServer>
SimpleXML will automatically encode* XML entities, so there's no need to do it in your PHP code with htmlentities()
. In addition, I've updated your code to include a proper random salt generator. Give it a try!
<?php
$password = "myNewPassword";
$salt = "";
while (strlen($salt) !== 64) {
$byte = ord(openssl_random_pseudo_bytes(1));
if ($byte > 31 && $byte < 127) {
$salt .= chr($byte);
}
}
$hashed_password = strtoupper(hash('SHA512', $password . $salt));
$xmlFile = 'server.xml';
$xml = simplexml_load_file($xmlFile);
$Pass = $xml->xpath("//User[@Name='TheUserName']//Option[@Name='Pass']");
$Salt = $xml->xpath("//User[@Name='TheUserName']//Option[@Name='Salt']");
$Pass[0][0] = $hashed_password;
$Salt[0][0] = $salt;
$xml->formatOutput = true;
$saved = $xml->asXML($xmlFile);
*There are special characters (&
, <
, >
, "
) that need to be encoded so as not to be confused with part of the markup (not always, but it's easier to just always encode them.) These get encoded as &
, <
, >
and "
, respectively. Since there is a special character in the encoded value, you can see double-encoding happening by looking for things like &lt;
as we see in your sample XML.