Skip to main content

Pure-ftpd with MySQL - Crypt() not logging me in with hashed passwords [Resolved]

I am using pure-ftpd with mysql to auth users.

Here is my mysql.conf

MYSQLServer     localhost
MYSQLPort       3306
MYSQLSocket     /var/run/mysqld/mysqld.sock
MYSQLUser       user
MYSQLPassword   pwd
MYSQLDatabase   my_db
MYSQLCrypt      crypt()
MYSQLGetPW      SELECT password FROM ftp_users WHERE login="\L"
MYSQLGetUID     SELECT u_id FROM ftp_users WHERE login="\L"
MYSQLGetGID     SELECT g_id FROM ftp_users WHERE login="\L"
MYSQLGetDir     SELECT dir FROM ftp_users WHERE login="\L"
MySQLGetQTAFS   SELECT quota_files FROM ftp_users WHERE login="\L"
MySQLGetQTASZ  SELECT quota_size FROM ftp_users WHERE login="\L"
MySQLGetRatioUL SELECT ul_ratio FROM ftp_users WHERE login="\L"
MySQLGetRatioDL SELECT dl_ratio FROM ftp_users WHERE login="\L"
MySQLGetBandwidthUL SELECT ul_bandwidth FROM ftp_users WHERE login="\L"
MySQLGetBandwidthDL SELECT dl_bandwidth FROM ftp_users WHERE login="\L"

I then have tried restarting pure-ftpd-mysql and pure-ftpd

My table has a field with the pwd as

password    varchar(255)

When I insert a user with a plaintext pwd, I can login fine with both login and password. When I insert a hash with 'lol' such as SHA512 or a BCrypt hash. It doesn't allow me to login with the pwd 'lol'.

BCrypt $2a$06$JrvxpMAvi6MnRSIvZQMMxOffIDLtEP7lrKNe0k0CTsK51v4zujfpS
SHA512 3DD28C5A23F780659D83DD99981E2DCB82BD4C4BDC8D97A7DA50AE84C7A7229A6DC0AE8AE4748640A4CC07CCC2D55DBDC023A99B3EF72BC6CE49E30B84253DAE

However, if I paste in the hash, it successfully logs in as I assume it takes it as a plaintext value.

I have tried changing the mysql.conf to

MYSQLCrypt      crypt

But this breaks it completely. There are many sites which say to use crypt, but the comments in my configuration file list crypt() as one of the options.

I have read through many posts and forums but the closest thing I found was this, which doesn't work at all.

Here is what pure-ftpd starts with

Starting ftp server: Running: /usr/sbin/pure-ftpd-mysql -l mysql:/etc/pure-ftpd/db/mysql.conf -l puredb:/etc/pure-ftpd/pureftpd.pdb -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -F /etc/pure-ftpd/fortunes.txt -j -H -J ALL:!aNULL:!SSLv3 -u 1000 -8 UTF-8 -A -O clf:/var/log/pure-ftpd/transfer.log -B

So basically it is not using crypt or I am not using it correctly. I though it could handle SHA512 natively with mysql but it doesn't. Other things I can think of is that I need code with the configuration but I can't see why it would require anything.

Asked March 20, 2017
Posted Under: Network
1 Answers

--- BEGIN tl;dr ---

Assuming your Pure-FTPd is running on a Linux host with glibc 2.7+:

  • Use MySQLCrypt cryptwithout parentheses
  • Run this Python 3 one-liner on the same system where Pure-FTPd runs:

    python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'
  • Enter the desired password.

  • Copy and paste the crypt-ed password (starting with $6$) into the database.

--- END tl;dr ---

The idea behind non-plaintext MySQLCrypt is to disable plaintext passwords altogether. The parenthesized MySQLCrypt crypt() still accepting the hash as a plaintext password suggests that Pure-FTPd treated the string crypt() as an unknown crypt method and ignored the directive altogether, using the default crypt method of plaintext instead. The un-parenthesized MySQLCrypt crypt “breaking” plaintext logins is actually a step in the right direction: It disabled plaintext login and started treating passwords in the database as crypt-ed only.

Now, the question is how to properly hash a password into an acceptable form. According to src/mysql.c of Pure-FTPd, MySQLCrypt crypt would cause it to use the crypt() function, whose acceptable forms depend on the implementation. For example, if you are running Pure-FTPd on a Linux machine with glibc 2.7 or higher, there are 4 forms accepted:

  • SHA-512 (starts with $6$)
  • SHA-256 (starts with $5$)
  • MD5 (starts with $1$)
  • Traditional DES (starts with an alphanumeric letter); should probably be avoided though

There are a few ways of invoking the system crypt(); my preferred method is Python 3's built-in crypt module. This short script would do our bidding:

import sys
from crypt import crypt, METHOD_SHA512
from getpass import getpass

print(crypt(getpass("Password: "), METHOD_SHA512))

Or if you prefer a triple-clickable, copy-and-paste-friendly one-liner:

python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'

A sample run:

root@ip-172-16-16-117:~# python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'

It is imperative that this command be run on the same host where Pure-FTPd runs, in order to use the same crypt() implementation.

The long line that starts with $6$ is the crypt-ed password. The $6$ prefix itself marks the password as hashed using SHA-512. If the system crypt() does not support SHA-512, the output will not start $6$; in this case, another algorithm should be used. For example, on macOS, a possible output is $6UCLzI8sPv16. Note that it is excessively short and also lacks the dollar sign after $6: This is because the macOS implementation of crypt() does not support SHA-512.

Answered March 20, 2017
Thank-you for explaining this step by step, I was seriously at loss with why it wasn't accepting the hashes I was giving it. I generated a pwd using your python script and it worked like a charm. As for moving forward, this information is really handy for future projects that deal with encryption, many thanks! – ServerSideSkittles 14 mins ago
 CanDoerz  2 days ago
By the way, the SHA512 3DD28C5A23F780659D83DD99981E2DCB82BD4C4BDC8D97A7DA50AE84C7A7‌​229A6DC0AE8AE4748640‌​A4CC07CCC2D55DBDC023‌​A99B3EF72BC6CE49E30B‌​84253DAE hash you posted is the unsalted hash of the password string (lol). Pure-FTPd supports only two unsalted password hashes: SHA-1 (MySQLCrypt sha1) and MD5 (MySQLCrypt md5), and even those two are deprecated. Unsalted SHA-512 is unsupported, and will likely be never implemented. – astralblue 1 hour ago
 CanDoerz  2 days ago
Your Answer