Peter's Blog

Redefining the Impossible

Cracking Filezilla Passwords


Needed to recover a password from my FileZilla settings. It transpires that this is not very difficult. The passwords are not strongly encoded which some regard as a security flaw but the developers seem to acknowledge that if you want security, don't trust a computer.

   1  #
   2  # Dump filezilla site manager, including account name, host, user and password.
   3  #
   4  import _winreg
   5  
   6  def DecodePassword( strPass):
   7      """Decode a filezilla password"""
   8      strKey = "FILEZILLA1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   9  
  10      nPassLen = len(strPass) / 3
  11      nOffset = nPassLen % len(strKey)
  12  
  13      strDecodedPass = ""
  14  
  15      for i in range(nPassLen):
  16          c = int(strPass[i * 3:(i * 3) + 3])
  17          c2 = ord(strKey[(i + nOffset) % len(strKey)])
  18          c3 = chr((c ^ c2))
  19  
  20          strDecodedPass += c3
  21  
  22      return strDecodedPass
  23  
  24  #
  25  # Walk through registry, decoding site details.
  26  #
  27  oReg = _winreg.ConnectRegistry( None, _winreg.HKEY_CURRENT_USER)
  28  oLicenceKey = _winreg.OpenKey( oReg, r'SOFTWARE\FileZilla\Site Manager')
  29  
  30  nIndex = 0
  31  while 1:
  32      try:
  33          strSite = _winreg.EnumKey( oLicenceKey, nIndex)
  34      except EnvironmentError:
  35          break
  36  
  37      oSiteKey = _winreg.OpenKey( oLicenceKey, strSite)
  38      strHost = _winreg.QueryValueEx( oSiteKey, u'Host')[0].encode( 'ascii')
  39      strUser = _winreg.QueryValueEx( oSiteKey, u'User')[0].encode( 'ascii')
  40      strPassword = DecodePassword( _winreg.QueryValueEx( oSiteKey, u'Pass')[0].encode( 'ascii'))
  41  
  42      print strSite, strHost, strUser, strPassword
  43  
  44      nIndex += 1
Toggle Line Numbers

Filed under: filezilla python

5 Comments

Evan Says:

over 2 years ago

Thanks for posting -- this just came in useful for me today

Heinrich Says:

about 1 year ago

Thanks! It generaly works, but not with subfolders.

Andrew Says:

about 1 year ago

Thanks for this - I was looking for a way of decrypting the passwords in FileZilla.xml so I've converted your algorithm to VBScript:

   1  Function DecodePassword (Pass)
   2    Key = "FILEZILLA1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   3  
   4    PassLen = len (Pass) / 3
   5    Offset = PassLen mod len (Key)
   6  
   7    DecodedPass = ""
   8  
   9    For Count = 1 to PassLen
  10    	c = CInt (Mid (Pass, (Count * 3) - 2, 3))
  11    	c2 = Asc (Mid (Key, (Count + Offset) mod len (Key), 1))
  12    	c3 = Chr (c Xor c2)
  13    	DecodedPass = DecodedPass & c3
  14    Next
  15  
  16    DecodePassword = DecodedPass
  17  End Function
  18  
  19  If Wscript.Arguments.Count > 0 Then
  20    Set XMLDoc = CreateObject ("Microsoft.XMLDOM")
  21    XMLDoc.async = False
  22  
  23    If XMLDoc.Load (Wscript.Arguments.Item(0)) Then
  24    	Set NodeList = XMLDoc.documentElement.selectNodes ("//FileZilla/Sites/Site")
  25  
  26    	For Each Node In NodeList
  27    		WScript.Echo "Name - " & Node.selectSingleNode ("@Name").text
  28    		WScript.Echo "Host - " & Node.selectSingleNode ("@Host").text
  29    		WScript.Echo "User - " & Node.selectSingleNode ("@User").text
  30    		WScript.Echo "Password - " & DecodePassword (Node.selectSingleNode ("@Pass").text)
  31    		WScript.Echo
  32    	Next
  33    Else
  34    	WScript.Echo "Error loading file or file not found"
  35    End If
  36  
  37    Set XMLDoc = Nothing
  38  Else
  39    WScript.Echo "Filename required"
  40  End If
Toggle Line Numbers

Quintin Says:

about 1 year ago

Thanks for that one Peter (and Andrew!)... This has just enabled me to decode the passwords from an older version of FileZilla so I can manually (grr) recreate them in the latest build (v.3.1.2).

A couple of comments:

1) Heinrich's correct in that it doesn't cope with Folders.

1a) My work around for now was to manually edit the XML before I passed it in to the VBScript and remove all the Folder nodes so that the tree is as expected (FileZilla to Sites to Site nodes).

1b) The 'proper' work around would be to re-engineer the script so that it has a dedicated function to work on a folder of sites which recursively calls itself if it finds a folder inside a folder... and, sorry, I haven't got the time in my day to 'knock that one out' either! ;)

2) Being more of an applications developer than a sys admin or script kiddie I had to get my head around how to run a VBScript... For those who need help there it's useful to know that if you simply call the VBS file from a cmd line then it will probably execute in 'WScript' mode which means that the Echo commands output to a modal dialog box (LOTS of clicking OK to everything). You can, however, run the script through the 'CScript' WSH Engine by using something like this at your cmd line:

cscript decode.vbs filezilla.xml

I hope that helps someone... and thanks again for the code!

Jay Says:

7 months ago

Heres some code ported to PHP.

   1  <?php
   2  /*
   3  Instructions: copy your FileZilla xml file to the same firectory as this script
   4  Change the value of $file to match the name of your xml file.
   5  */
   6  $file = "FileZilla.xml";
   7  
   8  //Parses the xml stuff
   9  function startElement($parser, $name, $attribs)
  10  {
  11  if($name == "SITE")
  12  {
  13      if (count($attribs)) {
  14          foreach ($attribs as $k => $v) {
  15    	if($k == "NAME" || $k == "HOST" || $k == "USER")
  16    	{
  17              echo "$k: $v<br>";
  18    	}
  19    	else if($k == "PASS")
  20    	{
  21    	echo "PASS: ". decode_filezilla($v);
  22    	}
  23          }
  24  
  25      }
  26      echo "<hr>";
  27    }
  28  }
  29  //not needed for this script
  30  function endElement($parser, $name)
  31  {
  32  //nothing is required here
  33  }
  34  
  35  //Just some XML parsing stuff
  36  function new_xml_parser($file)
  37  {
  38      global $parser_file;
  39  
  40      $xml_parser = xml_parser_create();
  41      xml_set_element_handler($xml_parser, "startElement", "endElement");
  42      if (!($fp = @fopen($file, "r"))) {
  43          return false;
  44      }
  45      if (!is_array($parser_file)) {
  46          settype($parser_file, "array");
  47      }
  48      $parser_file[$xml_parser] = $file;
  49      return array($xml_parser, $fp);
  50  }
  51  
  52  if (!(list($xml_parser, $fp) = new_xml_parser($file))) {
  53      die("could not open XML input");
  54  }
  55  
  56  while ($data = fread($fp, 4096)) {
  57      if (!xml_parse($xml_parser, $data, feof($fp))) {
  58          die(sprintf("XML error: %s at line %d\n",
  59                      xml_error_string(xml_get_error_code($xml_parser)),
  60                      xml_get_current_line_number($xml_parser)));
  61      }
  62  }
  63  xml_parser_free($xml_parser);
  64  
  65  //Given: a valid encrypted FileZilla Password - $pass
  66  //Returns: the unencrypted password
  67  function decode_filezilla($pass)
  68  {
  69  $key = "FILEZILLA1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  70  $passlen = strlen($pass) / 3;
  71  $keylen = strlen($key);
  72  $offset = $passlen % $keylen;
  73  $decodedpass="";
  74  for ($i = 1; $i <= $passlen; $i++)
  75  {
  76  $c = (int)substr($pass,($i * 3)-3,3);
  77  $c2 = ord(substr($key,(($i+$offset) % $keylen)-1,1));
  78  $c3 = $c ^ $c2;
  79  $decodedpass .= chr($c3);
  80  }
  81  return $decodedpass;
  82  }
  83  ?>
Toggle Line Numbers

Sorry but comments on this post are now closed.